index.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <MainPanel>
  3. <template v-slot:header>
  4. <Header
  5. :count="selects.length"
  6. :title="`全部照片(${photos.length})`"
  7. type="return"
  8. :on-back="() => router.back()"
  9. >
  10. <ui-button
  11. type="primary"
  12. @click="selectMode = !selectMode"
  13. width="96px"
  14. v-if="sortPhotos.length"
  15. >
  16. {{ selectMode ? "取消" : "选择" }}
  17. </ui-button>
  18. </Header>
  19. </template>
  20. <Photos
  21. undata-msg="无照片,请打开场景拍照获取。"
  22. :getURL="(data) => data.urlRaw || data.url"
  23. v-model:active="active"
  24. v-model:selects="selects"
  25. :select-mode="selectMode"
  26. :data="sortPhotos"
  27. />
  28. <!-- @click="router.push(writeRouteName.scene)"-->
  29. <ButtonPane class="back fun-ctrl" v-if="!selectMode">
  30. <ui-icon type="photo" class="icon" />
  31. <ui-input
  32. type="select"
  33. :options="photoOptions"
  34. dire="top"
  35. class="select"
  36. v-model="photoType"
  37. />
  38. </ButtonPane>
  39. <ActionMenus
  40. class="select-menus"
  41. :menus="selectMenus"
  42. dire="row"
  43. v-if="selects.length"
  44. />
  45. </MainPanel>
  46. <FillSlide
  47. :data="sortPhotos"
  48. v-model:active="active"
  49. @quit="active = null"
  50. v-if="active"
  51. :getURL="(data) => data.urlRaw || data.url"
  52. >
  53. <template v-slot:foot>
  54. <ActionMenus class="menus" :menus="menus" dire="row" />
  55. </template>
  56. </FillSlide>
  57. </template>
  58. <script setup lang="ts">
  59. import MainPanel from "@/components/main-panel/index.vue";
  60. import FillSlide from "@/components/fill-slide/index.vue";
  61. import Header from "@/components/photos/header.vue";
  62. import { PhotoRaw, photos } from "@/store/photos";
  63. import UiIcon from "@/components/base/components/icon/index.vue";
  64. import { router, writeRouteName } from "@/router";
  65. import ButtonPane from "@/components/button-pane/index.vue";
  66. import { computed, onActivated, ref, watchEffect } from "vue";
  67. import { Mode } from "@/views/graphic/menus";
  68. import UiButton from "@/components/base/components/button/index.vue";
  69. import Photos from "@/components/photos/index.vue";
  70. import ActionMenus from "@/components/group-button/index.vue";
  71. import { useConfirm } from "@/hook";
  72. import UiInput from "@/components/base/components/input/index.vue";
  73. import { api, downloadImage, uploadImage } from "@/store/sync";
  74. import { formatDate, getId, imageToBlob } from "@/utils";
  75. import { genUseLoading } from "@/hook";
  76. const sortPhotos = computed(() => [...photos.value].reverse());
  77. const active = ref<PhotoRaw>();
  78. const selectMode = ref(false);
  79. const selects = ref<PhotoRaw[]>([]);
  80. const menus = [
  81. {
  82. key: "road",
  83. text: "现场绘图",
  84. icon: "draw_s",
  85. onClick: () => gotoDraw(Mode.Road),
  86. },
  87. {
  88. key: "accident",
  89. icon: "label",
  90. text: "照片标注",
  91. onClick: () => gotoDraw(Mode.Photo),
  92. },
  93. {
  94. key: "del",
  95. icon: "del",
  96. text: "删除",
  97. onClick: () => delPhoto(),
  98. },
  99. {
  100. key: "share",
  101. icon: "share",
  102. text: "分享",
  103. onClick: genUseLoading(async () => {
  104. // 如果没下载过相册则下载,通过文件名判断
  105. if (!active.value.url.includes("img_")) {
  106. const filename = `img_${formatDate(new Date(), "yyyyMMddhhmmss")}_${
  107. active.value.meterPerPixel || 1
  108. }_${new Date().getTime().toString().substring(8)}.jpg`;
  109. const img = await api.getFile(active.value.url);
  110. const blob = await imageToBlob(img);
  111. const url = await uploadImage(blob, filename);
  112. await downloadImage(blob, filename);
  113. active.value.url = url;
  114. }
  115. api.shareImage(active.value.url);
  116. }),
  117. },
  118. ];
  119. const selectMenus = [
  120. {
  121. key: "del",
  122. icon: "del",
  123. text: "删除",
  124. onClick: () => delSelects(),
  125. },
  126. ];
  127. const photoType = ref<string>();
  128. const photoOptions = [
  129. { value: "photograph", label: "相机拍照" },
  130. { value: "selectPhotoAlbum", label: "相册选择" },
  131. { value: "scene", label: "场景截图" },
  132. ];
  133. watchEffect(() => {
  134. if (photoType.value) {
  135. if (photoType.value === "scene") {
  136. router.push(writeRouteName.scene);
  137. } else {
  138. api[photoType.value]().then((url) => {
  139. if (url) {
  140. photos.value.push({
  141. id: getId(),
  142. url,
  143. urlRaw: url,
  144. time: new Date().getTime(),
  145. meterPerPixel: null,
  146. measures: [],
  147. baseLines: [],
  148. fixPoints: [],
  149. basePoints: [],
  150. });
  151. }
  152. });
  153. }
  154. photoType.value = null;
  155. }
  156. });
  157. watchEffect(() => {
  158. if (!selectMode.value) {
  159. selects.value = [];
  160. }
  161. });
  162. const delPhotoRaw = (photo = active.value) => {
  163. const index = photos.value.indexOf(photo);
  164. const reset = active.value ? photos.value.indexOf(active.value) : -1;
  165. if (~index) {
  166. photos.value.splice(index, 1);
  167. }
  168. if (~reset) {
  169. if (reset >= photos.value.length) {
  170. if (photos.value.length) {
  171. active.value = photos.value[photos.value.length - 1];
  172. } else {
  173. active.value = null;
  174. }
  175. } else {
  176. active.value = photos.value[reset];
  177. }
  178. }
  179. };
  180. const delPhoto = async (photo = active.value) => {
  181. if (await useConfirm(`确定要删除此数据?`)) {
  182. delPhotoRaw(photo);
  183. }
  184. };
  185. const delSelects = async () => {
  186. if (await useConfirm(`确定要删除这${selects.value.length}项数据?`)) {
  187. while (selects.value.length) {
  188. delPhotoRaw(selects.value[0]);
  189. selects.value.shift();
  190. }
  191. if (!sortPhotos.value.length) {
  192. selectMode.value = false;
  193. }
  194. }
  195. };
  196. const gotoDraw = (mode: Mode) => {
  197. router.push({
  198. name: writeRouteName.graphic,
  199. params: { mode, id: active.value.id, action: "add" },
  200. });
  201. };
  202. onActivated(() => {
  203. active.value = null;
  204. selectMode.value = false;
  205. });
  206. </script>
  207. <style scoped lang="scss">
  208. .fun-ctrl {
  209. color: #fff;
  210. font-size: 20px;
  211. transition: color 0.3s ease;
  212. .icon {
  213. position: absolute;
  214. transform: translateX(-50%);
  215. }
  216. }
  217. .select-menus {
  218. left: 50%;
  219. transform: translateX(-50%);
  220. bottom: var(--boundMargin);
  221. }
  222. .back {
  223. right: var(--boundMargin);
  224. bottom: var(--boundMargin);
  225. overflow: hidden;
  226. background-color: #fff;
  227. i {
  228. color: var(--editor-menu-back);
  229. }
  230. .select {
  231. position: absolute;
  232. width: 100px;
  233. right: 0;
  234. opacity: 0;
  235. height: 100%;
  236. top: -5px;
  237. }
  238. }
  239. .menus {
  240. left: 50%;
  241. transform: translateX(-50%);
  242. bottom: var(--boundMargin);
  243. }
  244. </style>