index.vue 6.3 KB

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