Image.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <script setup lang="ts">
  2. import { ref, reactive, onMounted, watch } from "vue";
  3. import { useMainStore } from "@/stores/main";
  4. import { required } from "@/utils";
  5. import { useI18n } from "vue-i18n";
  6. import type { ImageDownload } from "@/interfaces";
  7. import Dialog from "@/components/Dialog.vue";
  8. import { wsUrl } from "@/env";
  9. const WS = new WebSocket(`${wsUrl}/api/v1/images/sr`);
  10. const mainStore = useMainStore();
  11. const { t } = useI18n();
  12. const valid = ref(true);
  13. const Form = ref();
  14. let imgFiles = ref();
  15. let imgList: any[] = reactive([]);
  16. let loading = ref(false);
  17. let imgInput = ref(true);
  18. watch(imgFiles, (newVal, oldVal) => {
  19. if (newVal) {
  20. if (!newVal.length) {
  21. imgInput.value = true;
  22. } else {
  23. imgInput.value = false;
  24. }
  25. }
  26. });
  27. // props
  28. let dialog = reactive({
  29. msg: "圖片處理需要幾秒鐘的時間,敬請耐心等候",
  30. state: "info",
  31. show: false,
  32. });
  33. async function Submit() {
  34. WS.send("subscribe");
  35. loading.value = true;
  36. setTimeout(() => {
  37. dialog.show = true;
  38. }, 500);
  39. await mainStore.uploadImage(imgFiles.value);
  40. (Form as any).value.reset();
  41. loading.value = false;
  42. for (let i = 0; i < mainStore.images.length; i++) {
  43. const element = mainStore.images[i];
  44. imgList = imgList.filter(
  45. (e) => e.stored_file_name === element.stored_file_name
  46. );
  47. imgList.push(element);
  48. }
  49. }
  50. onMounted(() => {
  51. // 存入 localStorage
  52. // if (imgList.length === 0) {
  53. // let images: any | null = localStorage.getItem("imagesList");
  54. // if (images) {
  55. // images = JSON.parse(images);
  56. // for (let i = 0; i < images.length; i++) {
  57. // const item = images[i];
  58. // imgList.push(item);
  59. // }
  60. // }
  61. // }
  62. // webSocket
  63. WS.onmessage = function (e) {
  64. setTimeout(() => {
  65. let image: ImageDownload = {
  66. file_name: "",
  67. stored_file_name: "",
  68. };
  69. mainStore.images.map((item) => {
  70. if (item.stored_file_name === e.data) {
  71. image.file_name = item.file_name;
  72. image.stored_file_name = item.stored_file_name;
  73. mainStore.finishImage(image);
  74. }
  75. });
  76. }, 1000);
  77. };
  78. });
  79. async function downloadImg(file_name: string, stored_file_name: string) {
  80. mainStore.images.map((item) => {
  81. if (item.stored_file_name === stored_file_name) {
  82. // 生成下載連結
  83. const href = URL.createObjectURL(item.link);
  84. const link = document.createElement("a");
  85. link.href = href;
  86. link.setAttribute("download", `${file_name}_hr.png`);
  87. document.body.appendChild(link);
  88. link.click();
  89. document.body.removeChild(link);
  90. URL.revokeObjectURL(href);
  91. }
  92. });
  93. // const data: ImageDownload = {
  94. // file_name: file_name.split(".")[0],
  95. // stored_file_name: stored_file_name,
  96. // };
  97. // await mainStore.getImage(data);
  98. }
  99. const headers = [
  100. {
  101. title: "檔名",
  102. sortable: true,
  103. key: "file_name",
  104. align: "left",
  105. },
  106. {
  107. title: t("state"),
  108. sortable: true,
  109. key: "state",
  110. align: "left",
  111. },
  112. {
  113. title: t("download"),
  114. key: "stored_file_name",
  115. },
  116. ];
  117. </script>
  118. <template>
  119. <v-container fluid>
  120. <v-card class="ma-3 pa-3">
  121. <v-card-title primary-title>
  122. <h3 class="card-title mb-3">圖片優化</h3>
  123. </v-card-title>
  124. <v-card-text>
  125. <!-- <section class="d-flex flex-column form-title">
  126. <img src="@/assets/img/icon/add-image.png" alt="" class="mb-4" />
  127. <p>請點擊加入圖片並開始優化</p>
  128. </section> -->
  129. <v-form v-model="valid" ref="Form">
  130. <!-- <img src="@/assets/img/icon/add-image.png" alt="" class="mb-4" /> -->
  131. <!-- <input
  132. @change="upload()"
  133. ref="imgFiles"
  134. type="file"
  135. multiple
  136. class="file-input"
  137. /> -->
  138. <v-file-input
  139. v-model="imgFiles"
  140. multiple
  141. label="請選擇圖片"
  142. prepend-icon="add_photo_alternate"
  143. accept=".jpg, .png"
  144. :rules="[(imgFiles) => imgFiles.length || '']"
  145. ></v-file-input>
  146. <small class="d-block" style="margin: -15px 0 0 40px"
  147. >支援格式:JPEG、PNG</small
  148. >
  149. <!-- <p>請點擊加入圖片並開始優化</p> -->
  150. </v-form>
  151. </v-card-text>
  152. <v-card-actions class="justify-center">
  153. <v-btn
  154. variant="flat"
  155. size="large"
  156. color="primary"
  157. class="px-5"
  158. prepend-icon="file_upload"
  159. :loading="loading"
  160. :disabled="imgInput"
  161. @click="Submit()"
  162. >上傳</v-btn
  163. >
  164. </v-card-actions>
  165. </v-card>
  166. <v-card class="ma-3 pa-3 mt-6 img-progress">
  167. <v-card-title primary-title>
  168. <h3 class="card-title mb-3">上傳清單</h3>
  169. </v-card-title>
  170. <v-data-table :headers="headers" :items="mainStore.images">
  171. <template v-slot:item.state="{ item }">
  172. <span v-if="item.raw.state === 'completed'">
  173. <v-icon icon="check_circle" color="success" />
  174. 完成
  175. </span>
  176. <span v-else>
  177. <v-progress-circular
  178. indeterminate
  179. color="info"
  180. style="width: 20px"
  181. ></v-progress-circular>
  182. 處理中
  183. </span>
  184. </template>
  185. <template v-slot:item.stored_file_name="{ item }">
  186. <v-btn
  187. flat
  188. :disabled="item.raw.state !== 'completed'"
  189. @click="downloadImg(item.raw.file_name, item.raw.stored_file_name)"
  190. >
  191. <v-icon icon="crop_original" />
  192. </v-btn>
  193. </template>
  194. </v-data-table>
  195. </v-card>
  196. <Dialog
  197. :msg="dialog.msg"
  198. :state="dialog.state"
  199. :dialog="dialog.show"
  200. @close="dialog.show = false"
  201. ></Dialog>
  202. </v-container>
  203. </template>
  204. <style lang="scss">
  205. .img-form {
  206. position: relative;
  207. top: 50%;
  208. left: 50%;
  209. margin-left: -250px;
  210. width: 500px;
  211. height: 200px;
  212. border: 2px dashed #b5b5b5;
  213. .file-input {
  214. position: absolute;
  215. z-index: 100;
  216. margin: 0;
  217. padding: 0;
  218. width: 100%;
  219. height: 100%;
  220. outline: none;
  221. opacity: 0;
  222. cursor: pointer;
  223. }
  224. img {
  225. position: absolute;
  226. width: 50px;
  227. left: 50%;
  228. top: 50%;
  229. transform: translate(-50%, -80%);
  230. }
  231. p {
  232. width: 100%;
  233. text-align: center;
  234. position: absolute;
  235. bottom: 50px;
  236. font-size: 18px;
  237. font-weight: 600;
  238. letter-spacing: 1px;
  239. }
  240. }
  241. .img-progress {
  242. .v-data-table-footer {
  243. margin-top: 20px;
  244. }
  245. }
  246. </style>