SyuanYu пре 2 година
родитељ
комит
9079d0e204

+ 2 - 1
frontend/.env

@@ -1,5 +1,6 @@
 # VITE_APP_DOMAIN_DEV=cloud.choozmo.com # 正式
-VITE_APP_DOMAIN_DEV=192.168.192.252:8080 # 測試
+VITE_APP_DOMAIN_DEV=dev.ai-anchor.com # 測試
+# VITE_APP_DOMAIN_DEV=192.168.192.252 # 測試
 # VUE_APP_DOMAIN_DEV=local.dockertoolbox.tiangolo.com
 # VUE_APP_DOMAIN_DEV=localhost.tiangolo.com
 # VUE_APP_DOMAIN_DEV=dev.ai-anchor.com

+ 21 - 30
frontend/package-lock.json

@@ -13,7 +13,6 @@
         "sass": "^1.57.1",
         "vue": "^3.2.45",
         "vue-i18n": "^9.2.2",
-        "vue-native-websocket-vue3": "^3.1.7",
         "vue-router": "^4.1.6",
         "vue3-google-login": "^2.0.14",
         "vuetify": "^3.1.1"
@@ -1870,7 +1869,8 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
     },
     "node_modules/binary-extensions": {
       "version": "2.2.0",
@@ -1890,6 +1890,7 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -2106,7 +2107,8 @@
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
     },
     "node_modules/convert-source-map": {
       "version": "1.9.0",
@@ -2114,17 +2116,6 @@
       "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
       "dev": true
     },
-    "node_modules/core-js": {
-      "version": "3.29.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz",
-      "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==",
-      "hasInstallScript": true,
-      "peer": true,
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/core-js"
-      }
-    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3013,6 +3004,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
       "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
       "dependencies": {
         "flat-cache": "^3.0.4"
       },
@@ -3051,6 +3043,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
       "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
       "dependencies": {
         "flatted": "^3.1.0",
         "rimraf": "^3.0.2"
@@ -3062,7 +3055,8 @@
     "node_modules/flatted": {
       "version": "3.2.7",
       "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
-      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
+      "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+      "dev": true
     },
     "node_modules/follow-redirects": {
       "version": "1.15.2",
@@ -3108,7 +3102,8 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
     },
     "node_modules/fsevents": {
       "version": "2.3.2",
@@ -3208,6 +3203,7 @@
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
       "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -3505,6 +3501,7 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -3513,7 +3510,8 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
     },
     "node_modules/internal-slot": {
       "version": "1.0.4",
@@ -4065,6 +4063,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -4316,6 +4315,7 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
       "dependencies": {
         "wrappy": "1"
       }
@@ -4417,6 +4417,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4794,6 +4795,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
       "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
       "dependencies": {
         "glob": "^7.1.3"
       },
@@ -5645,18 +5647,6 @@
         "vue": "^3.0.0"
       }
     },
-    "node_modules/vue-native-websocket-vue3": {
-      "version": "3.1.7",
-      "resolved": "https://registry.npmjs.org/vue-native-websocket-vue3/-/vue-native-websocket-vue3-3.1.7.tgz",
-      "integrity": "sha512-f7cmJE+okUrGGu1kR5ZY5ZGzNDCJW0ktUso3QytnrtdM4i8cyiJDHjUl8hPBiwTqVuuwadc3lRsPGt0BrcrReQ==",
-      "dependencies": {
-        "file-entry-cache": "^6.0.1"
-      },
-      "peerDependencies": {
-        "core-js": "^3.6.5",
-        "vue": "^3.0.0"
-      }
-    },
     "node_modules/vue-router": {
       "version": "4.1.6",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
@@ -5868,7 +5858,8 @@
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
     },
     "node_modules/ws": {
       "version": "8.12.0",

+ 0 - 1
frontend/package.json

@@ -17,7 +17,6 @@
     "sass": "^1.57.1",
     "vue": "^3.2.45",
     "vue-i18n": "^9.2.2",
-    "vue-native-websocket-vue3": "^3.1.7",
     "vue-router": "^4.1.6",
     "vue3-google-login": "^2.0.14",
     "vuetify": "^3.1.1"

+ 1 - 2
frontend/src/api.ts

@@ -82,11 +82,10 @@ export const api = {
         'Authorization': `Bearer ${token}`
       },
     }).then((response) => {
-      console.log('getImage response', response);
       const href = URL.createObjectURL(response.data);
       const link = document.createElement('a');
       link.href = href;
-      link.setAttribute('download', `${data.stored_file_name}_hr.png`); //or any other extension
+      link.setAttribute('download', `${data.file_name}_hr.png`); //or any other extension
       document.body.appendChild(link);
       link.click();
       document.body.removeChild(link);

+ 4 - 0
frontend/src/env.ts

@@ -1,16 +1,20 @@
 const env = import.meta.env.VITE_APP_ENV;
 
 let envApiUrl = "";
+let envWsUrl = "";
 
 if (env === "production") {
   envApiUrl = `https://${import.meta.env.VITE_APP_DOMAIN_PROD}`;
+  envWsUrl = `wss://${import.meta.env.VITE_APP_DOMAIN_DEV}`;
 } else if (env === "staging") {
   envApiUrl = `https://${import.meta.env.VITE_APP_DOMAIN_STAG}`;
 } else {
   envApiUrl = `http://${import.meta.env.VITE_APP_DOMAIN_DEV}`;
+  envWsUrl = `ws://${import.meta.env.VITE_APP_DOMAIN_DEV}`;
 }
 
 export const apiUrl = envApiUrl;
+export const wsUrl = envWsUrl;
 export const appName = import.meta.env.VITE_APP_NAME;
 export const openRegisration = import.meta.env.VITE_APP_OPEN_REGISRATION;
 

+ 1 - 0
frontend/src/interfaces/index.ts

@@ -63,6 +63,7 @@ export interface Image {
   file_name: string;
   stored_file_name: string;
   content: string;
+  state: string;
 }
 
 export interface ImageDownload {

+ 0 - 6
frontend/src/main.ts

@@ -5,11 +5,8 @@ import { vuetify } from "./plugins/vuetify";
 import { pinia } from "./plugins/pinia";
 import i18n from './plugins/i18n'
 import vue3GoogleLogin from 'vue3-google-login'
-import VueNativeSock from "vue-native-websocket-vue3";
-import { useSocketStoreWithOut } from './stores/useSocketStore';
 
 const app = createApp(App);
-const store = useSocketStoreWithOut();
 
 app.use(pinia);
 app.use(router);
@@ -18,9 +15,6 @@ app.use(i18n);
 app.use(vue3GoogleLogin, {
     clientId: '136107811725-n71808u8t465f1afhpe2e5j7mn606nd8.apps.googleusercontent.com'
 });
-app.use(VueNativeSock, "ws://172.104.93.163:1853/ws", {
-    store: store
-});
 
 app.mount("#app");
 

+ 25 - 4
frontend/src/stores/main.ts

@@ -6,6 +6,7 @@ import { getLocalToken, removeLocalToken, saveLocalToken } from "@/utils";
 import type { AppNotification } from '@/interfaces';
 import type { IUserProfile, IUserProfileCreate, IUserProfileUpdate, MainState, Video, VideoCreate, ArticleCreate, Image, ImageDownload } from '@/interfaces';
 import i18n from '@/plugins/i18n'
+import WS from "@/stores/ws";
 
 const defaultState: MainState = {
   isLoggedIn: null,
@@ -270,12 +271,12 @@ export const useMainStore = defineStore("MainStoreId", {
               const tmpImage: Image = {
                 file_name: file[i].name,
                 stored_file_name: element,
-                content: "sr"
+                content: "sr",
+                state: "subscribe"
               };
               this.addImage(tmpImage);
             }
-            localStorage.setItem('imagesList',JSON.stringify(this.images));
-            console.log('this.images',this.images);
+            // localStorage.setItem('imagesList', JSON.stringify(this.images));
           })
         );
         mainStore.removeNotification(loadingNotification);
@@ -289,7 +290,6 @@ export const useMainStore = defineStore("MainStoreId", {
       }
     },
     async getImage(data: ImageDownload) {
-      console.log('getImage data', data);
       const mainStore = useMainStore();
       try {
         const response = (
@@ -305,6 +305,27 @@ export const useMainStore = defineStore("MainStoreId", {
     addImage(payload: Image) {
       this.images.push(payload);
     },
+    finishImage(payload: string) {
+      let image = this.images.filter(e => {
+        return e.stored_file_name === payload
+      });
+
+      if (image) {
+        image.map(e => {
+          e.state = "completed";
+        })
+      }
+
+      // 全部完成後關閉 WebSocket
+      // let processing = this.images.find(e => e.state !== "completed")
+      // if (!processing) {
+      //   setTimeout(() => {
+      //     WS.close();
+      //   }, 1000)
+      // }
+
+      return !this.images.some(e => e.state === "completed")
+    },
     async uploadArticle(article_data: ArticleCreate) {
       const mainStore = useMainStore();
       try {

+ 0 - 10
frontend/src/stores/store.ts

@@ -1,10 +0,0 @@
-import { createPinia } from "pinia";
-import type { App } from "vue";
-
-const store = createPinia();
-
-export function setupStore(app: App<Element>) {
-  app.use(store);
-}
-
-export { store };

+ 0 - 16
frontend/src/stores/type.ts

@@ -1,16 +0,0 @@
-export type SocketStore = {
-    // 連接狀態
-    isConnected: boolean;
-    // 內容
-    message: string;
-    // 重新連接錯誤
-    reconnectError: boolean;
-    // 心跳消息發送時間
-    heartBeatInterval: number;
-    // 心跳定時器
-    heartBeatTimer: number;
-  };
-  
-  export type socketType = {
-    $connect: () => void;
-  };

+ 0 - 69
frontend/src/stores/useSocketStore.ts

@@ -1,69 +0,0 @@
-import { defineStore } from "pinia";
-import { store } from "@/stores/store";
-import main from "../main";
-import type { SocketStore } from "./type";
-
-export const useSocketStore = defineStore({
-  id: "socket",
-  state: (): SocketStore => ({
-    // 連接狀態
-    isConnected: false,
-    // 訊息內容
-    message: "",
-    // 重新連接錯誤
-    reconnectError: false,
-    // 心跳消息發送時間
-    heartBeatInterval: 50000,
-    // 心跳定時器
-    heartBeatTimer: 0
-  }),
-  actions: {
-    // 開啟連接
-    SOCKET_ONOPEN(event: any) {
-      console.log("successful websocket connection");
-      console.log('event',event);
-      main.config.globalProperties.$socket = event.currentTarget;
-      this.isConnected = true;
-      // 連接成功時啟動定時發送心跳訊息,避免被服務器斷開連接
-      this.heartBeatTimer = window.setInterval(() => {
-        const message = "心跳消息";
-        this.isConnected &&
-          main.config.globalProperties.$socket.sendObj({
-            code: 200,
-            msg: message
-          });
-      }, this.heartBeatInterval);
-    },
-    // 關閉連接
-    SOCKET_ONCLOSE(event: any) {
-      this.isConnected = false;
-      // 連接關閉時停掉心跳訊息
-      window.clearInterval(this.heartBeatTimer);
-      this.heartBeatTimer = 0;
-      console.log("連接已斷線:" + new Date());
-      console.log(event);
-    },
-    // 發生錯誤
-    SOCKET_ONERROR(event: any) {
-      console.error(event);
-    },
-    // 收到服務端發送的訊息
-    SOCKET_ONMESSAGE(message: any) {
-      this.message = message;
-      console.log('SOCKET_ONMESSAGE',message);
-    },
-    // 自動重連
-    SOCKET_RECONNECT(count: any) {
-      console.info("重新連接中...", count);
-    },
-    // 重連錯誤
-    SOCKET_RECONNECT_ERROR() {
-      this.reconnectError = true;
-    }
-  }
-});
-
-// Need to be used outside the setup
-export function useSocketStoreWithOut() {
-  return useSocketStore(store);
-}

+ 5 - 0
frontend/src/stores/ws.ts

@@ -0,0 +1,5 @@
+import { wsUrl } from "@/env";
+
+const WS = new WebSocket(`${wsUrl}/api/v1/images/sr`);
+
+export default WS;

+ 29 - 25
frontend/src/views/main/Image.vue

@@ -5,6 +5,7 @@ import { required } from "@/utils";
 import { useI18n } from "vue-i18n";
 import type { ImageDownload } from "@/interfaces";
 import Dialog from "@/components/Dialog.vue";
+import WS from "@/stores/ws";
 
 const mainStore = useMainStore();
 const { t } = useI18n();
@@ -21,6 +22,8 @@ let dialog = reactive({
 });
 
 async function Submit() {
+  WS.send("subscribe");
+
   setTimeout(() => {
     dialog.show = true;
   }, 2000);
@@ -28,27 +31,36 @@ async function Submit() {
   (Form as any).value.reset();
   for (let i = 0; i < mainStore.images.length; i++) {
     const element = mainStore.images[i];
+    imgList = imgList.filter(
+      (e) => e.stored_file_name === element.stored_file_name
+    );
     imgList.push(element);
   }
 }
 
 onMounted(() => {
-  if (imgList.length === 0) {
-    let images: any | null = localStorage.getItem("imagesList");
-    if (images) {
-      images = JSON.parse(images);
-      for (let i = 0; i < images.length; i++) {
-        const item = images[i];
-        imgList.push(item);
-      }
-    }
-  }
+  // 存入 localStorage
+  // if (imgList.length === 0) {
+  //   let images: any | null = localStorage.getItem("imagesList");
+  //   if (images) {
+  //     images = JSON.parse(images);
+  //     for (let i = 0; i < images.length; i++) {
+  //       const item = images[i];
+  //       imgList.push(item);
+  //     }
+  //   }
+  // }
+
+  // webSocket
+  WS.onmessage = function (e) {
+    mainStore.finishImage(e.data);
+  };
 });
 
 async function downloadImg(name: string, id: string) {
   const data: ImageDownload = {
-    file_name: id,
-    stored_file_name: name.split(".")[0],
+    file_name: name.split(".")[0],
+    stored_file_name: id,
   };
 
   await mainStore.getImage(data);
@@ -64,7 +76,7 @@ const headers = [
   {
     title: t("state"),
     sortable: true,
-    key: "progress_state",
+    key: "state",
     align: "left",
   },
   {
@@ -124,17 +136,13 @@ const headers = [
         <h3 class="card-title mb-3">上傳清單</h3>
       </v-card-title>
 
-      <v-data-table :headers="headers" :items="imgList">
-        <template v-slot:item.progress_state="{ item }">
-          <span v-if="item.raw.progress_state === 'completed'">
+      <v-data-table :headers="headers" :items="mainStore.images">
+        <template v-slot:item.state="{ item }">
+          <span v-if="item.raw.state === 'completed'">
             <v-icon icon="check_circle" color="success" />
             完成
           </span>
-          <span v-else-if="item.raw.progress_state === 'waiting'">
-            <v-icon icon="pending" color="warning" />
-            等待中
-          </span>
-          <span v-else-if="item.raw.progress_state === 'processing'">
+          <span v-else>
             <v-progress-circular
               indeterminate
               color="info"
@@ -142,10 +150,6 @@ const headers = [
             ></v-progress-circular>
             處理中
           </span>
-          <span v-else>
-            <v-icon icon="check_circle" color="success" />
-            完成
-          </span>
         </template>
         <template v-slot:item.stored_file_name="{ item }">
           <v-btn