UploadPhotos.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <script setup lang="ts">
  2. import VueQrcode from "@chenfengyuan/vue-qrcode";
  3. import draggable from "vuedraggable";
  4. import "viewerjs/dist/viewer.css";
  5. import Viewer from "viewerjs";
  6. import { watch } from "vue";
  7. import { store } from "@/store/store";
  8. import { Eye, TrashOutline } from "@vicons/ionicons5";
  9. const props = defineProps<{
  10. defaultList: string[];
  11. qrValue: string;
  12. pictureAnswer?: { order: number; fileUrl: string };
  13. uploadModalVisible: boolean;
  14. }>();
  15. const emit = defineEmits<{
  16. (e: "update:uploadModalVisible", v: boolean): void;
  17. (e: "on-photos-reseted", v: string[]): void;
  18. (e: "on-photo-removed", v: string): void;
  19. }>();
  20. let uploadList: string[] = $ref([]);
  21. watch(
  22. () => props.defaultList,
  23. () => (uploadList = [...props.defaultList])
  24. );
  25. let uploadModalVisible = $ref(false);
  26. let totalList: string[] = $ref([]);
  27. const order = store.exam.currentQuestion.order;
  28. let uploaded = $ref(false);
  29. let qrScanned = $ref(false);
  30. watch(
  31. () => props.pictureAnswer,
  32. (value) => {
  33. uploaded = true;
  34. if (value?.order === order) {
  35. totalList.push(...[...new Set(value.fileUrl.split(","))]);
  36. }
  37. }
  38. );
  39. watch(
  40. () => uploadModalVisible,
  41. () => emit("update:uploadModalVisible", uploadModalVisible)
  42. );
  43. watch(
  44. () => store.exam.questionQrCodeScanned,
  45. () => {
  46. if (store.exam.questionQrCodeScanned.order === order) {
  47. qrScanned = true;
  48. }
  49. }
  50. );
  51. let drag = $ref(false);
  52. function handleView(imagesClass: string, index: number) {
  53. const viewer = new Viewer(<HTMLElement>document.querySelector(imagesClass), {
  54. container: "#app",
  55. zIndex: 99999,
  56. title: false,
  57. toolbar: {
  58. zoomIn: 1,
  59. zoomOut: 1,
  60. oneToOne: 1,
  61. reset: 1,
  62. prev: 1,
  63. play: { show: 0, size: "large" },
  64. next: 1,
  65. rotateLeft: 1,
  66. rotateRight: 1,
  67. flipHorizontal: 1,
  68. flipVertical: 1,
  69. },
  70. ready() {
  71. viewer.view(index);
  72. },
  73. hidden() {
  74. viewer.destroy();
  75. },
  76. });
  77. viewer.show();
  78. }
  79. function handleRemove(file: string) {
  80. emit("on-photo-removed", file);
  81. }
  82. function handleRemoveTotal(file: string) {
  83. totalList.splice(totalList.indexOf(file), 1);
  84. }
  85. function modalCloseClicked() {
  86. // 在二维码被扫描,文件没得到之前,提示是否关闭。
  87. if (qrScanned && !uploaded) {
  88. $dialog.warning({
  89. title: "确认关闭?",
  90. content:
  91. "检测到二维码被扫描,但是图片未上传。关闭此窗口后,将不再接受小程序图片上传。",
  92. positiveText: "确定关闭",
  93. negativeText: "取消关闭",
  94. onPositiveClick: () => (uploadModalVisible = false),
  95. onNegativeClick: () => undefined,
  96. });
  97. return;
  98. } else if (totalList.length > 6) {
  99. // 检查是否超过6张
  100. $dialog.warning({
  101. title: "图片数量超出限制",
  102. content: "请删除多余的图片,图片总数不应该超过6张",
  103. });
  104. return;
  105. }
  106. uploadModalVisible = false;
  107. emit("on-photos-reseted", totalList);
  108. }
  109. function uploadListSort() {
  110. emit("on-photos-reseted", uploadList);
  111. }
  112. function dragMove(e: any) {
  113. // console.log(e);
  114. // 第三方组件没定义
  115. // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  116. return !e.dragged.className.includes("plus");
  117. }
  118. function prepareUpload() {
  119. uploadModalVisible = true;
  120. qrScanned = false;
  121. uploaded = false;
  122. totalList = [...uploadList];
  123. }
  124. </script>
  125. <template>
  126. <div>
  127. <draggable
  128. v-model="uploadList"
  129. :move="dragMove"
  130. class="upload-images"
  131. itemKey="index"
  132. @update="uploadListSort"
  133. >
  134. <template #item="{ element: item, index }">
  135. <div class="demo-upload-list">
  136. <img :src="item" />
  137. <div class="demo-upload-list-cover">
  138. <n-icon
  139. :component="Eye"
  140. size="30"
  141. @click="handleView('.upload-images', index)"
  142. />
  143. <n-icon
  144. :component="TrashOutline"
  145. size="20"
  146. style="position: absolute; top: 1px; right: 0px"
  147. @click="handleRemove(item)"
  148. />
  149. </div>
  150. </div>
  151. </template>
  152. <template #footer>
  153. <div
  154. v-if="uploadList.length < 6"
  155. class="demo-upload-list plus"
  156. @click="prepareUpload"
  157. >
  158. <span
  159. class="tw-inline-block"
  160. style="transform: scale(4); padding-bottom: 2px"
  161. >+</span
  162. >
  163. </div>
  164. </template>
  165. </draggable>
  166. <div>
  167. 点击
  168. <span style="color: blue; font-size: 24px">+</span>
  169. 上传图片(最多上传6张图片)
  170. </div>
  171. <n-modal
  172. :show="uploadModalVisible"
  173. title="上传图片"
  174. :maskClosable="false"
  175. preset="card"
  176. style="width: 800px"
  177. @close="modalCloseClicked"
  178. >
  179. <div>
  180. <div v-if="qrValue" style="display: flex">
  181. <VueQrcode
  182. :value="qrValue"
  183. :options="{ width: 200 }"
  184. :style="{
  185. 'margin-left': '-10px',
  186. filter: totalList.length >= 6 ? 'blur(10px)' : 'none',
  187. }"
  188. />
  189. <div style="font-size: 24px; margin-top: 10px">
  190. <div>
  191. 请使用<span style="font-weight: 900; color: #1e90ff">微信</span
  192. >扫描二维码后,在微信小程序上拍照,并上传文件。<br />
  193. 上传期间,请勿关闭二维码。
  194. </div>
  195. <div v-if="qrScanned" style="margin-top: 30px; font-size: 30px">
  196. {{ uploaded ? "已上传" : "已扫描" }}
  197. <n-icon type="md-checkmark" />
  198. </div>
  199. </div>
  200. </div>
  201. <div v-else>正在获取二维码...</div>
  202. <draggable
  203. v-model="totalList"
  204. class="total-images"
  205. itemKey="index"
  206. @start="drag = true"
  207. @end="drag = false"
  208. >
  209. <template #item="{ element: item, index }">
  210. <div class="demo-upload-list">
  211. <img :src="item" />
  212. <div class="demo-upload-list-cover">
  213. <n-icon
  214. :component="Eye"
  215. size="30"
  216. @click="handleView('.total-images', index)"
  217. />
  218. <n-icon
  219. :component="TrashOutline"
  220. size="20"
  221. style="position: absolute; top: 1px; right: 0px"
  222. @click="handleRemoveTotal(item)"
  223. />
  224. </div>
  225. </div>
  226. </template>
  227. </draggable>
  228. <div v-if="totalList.length > 6">* 图片上传最多只支持6张</div>
  229. <div style="display: flex; justify-content: center">
  230. <n-button type="success" size="large" @click="modalCloseClicked">
  231. 确认
  232. </n-button>
  233. </div>
  234. </div>
  235. </n-modal>
  236. </div>
  237. </template>
  238. <style scoped>
  239. .demo-upload-list {
  240. display: inline-block;
  241. width: 100px;
  242. height: 100px;
  243. text-align: center;
  244. line-height: 100px;
  245. border: 1px solid transparent;
  246. border-radius: 4px;
  247. overflow: hidden;
  248. background: #fff;
  249. position: relative;
  250. box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
  251. margin-right: 4px;
  252. cursor: move;
  253. }
  254. .demo-upload-list img {
  255. width: 100%;
  256. height: 100%;
  257. }
  258. .plus {
  259. font-size: 12px;
  260. box-shadow: 0 0 1px 1px var(--app-color-text-light);
  261. }
  262. .plus:hover {
  263. cursor: pointer;
  264. color: blueviolet;
  265. }
  266. .demo-upload-list-cover {
  267. display: none;
  268. position: absolute;
  269. top: 0;
  270. bottom: 0;
  271. left: 0;
  272. right: 0;
  273. background: rgba(0, 0, 0, 0.6);
  274. }
  275. .demo-upload-list:hover .demo-upload-list-cover {
  276. display: block;
  277. }
  278. .demo-upload-list-cover i {
  279. color: #fff;
  280. font-size: 20px;
  281. cursor: pointer;
  282. margin: 0 2px;
  283. }
  284. </style>