Ver código fonte

fix conflict

tomoya 2 anos atrás
pai
commit
b41782effb

+ 3 - 1
frontend/.env

@@ -1,4 +1,6 @@
-VITE_APP_DOMAIN_DEV=cloud.choozmo.com
+# VITE_APP_DOMAIN_DEV=cloud.choozmo.com # 正式
+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

+ 35 - 1
frontend/src/api.ts

@@ -63,7 +63,41 @@ export const api = {
     formData.append("anchor_id", video_data.anchor_id.toString())
     formData.append("lang_id", video_data.lang_id.toString())
     formData.append("upload_file", file)
-    return axios.post<{msg:string}>(`${apiUrl}/api/v1/videos/`, formData, authHeaders(token));
+    return axios.post<{ msg: string }>(`${apiUrl}/api/v1/videos/`, formData, authHeaders(token));
+  },
+  async uploadImage(token: string, file: File[]) {
+    const formData = new FormData();
+    for (let i = 0; i < file.length; i++) {
+      const element = file[i];
+      formData.append("upload_files", element);
+    }
+    return axios.post<{ filenames: string[] }>(`${apiUrl}/api/v1/images/sr`, formData, authHeaders(token));
+  },
+  async getImage(token: string, data: ImageDownload) {
+    axios({
+      url: `${apiUrl}/api/v1/images/sr?stored_file_name=${data.stored_file_name}&file_name=${data.file_name}`,
+      method: 'GET',
+      responseType: 'blob',
+      headers: {
+        'Authorization': `Bearer ${token}`
+      },
+    }).then((response) => {
+      const href = URL.createObjectURL(response.data);
+      const link = document.createElement('a');
+      link.href = href;
+      link.setAttribute('download', `${data.file_name}_hr.png`); //or any other extension
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+      URL.revokeObjectURL(href);
+    });
+  },
+  async uploadArticle(token: string, article_data: ArticleCreate) {
+    const formData = new FormData();
+    formData.append("title", article_data.title)
+    formData.append("link", article_data.link)
+    formData.append("content", article_data.content)
+    return axios.post<{ msg: string }>(`${apiUrl}/api/v1/reputations/`, formData, authHeaders(token));
   },
   async getVideos(token: string) {
     return axios.get<Video[]>(`${apiUrl}/api/v1/videos/`, authHeaders(token));

+ 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;
 

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

@@ -52,4 +52,22 @@ export interface VideoCreate{
   title: string;
   anchor_id: number;
   lang_id: number;
-}
+}
+
+export interface ArticleCreate {
+  title: string;
+  link: string;
+  content: string;
+}
+
+export interface Image {
+  file_name: string;
+  stored_file_name: string;
+  content: string;
+  state: string;
+}
+
+export interface ImageDownload {
+  file_name: string;
+  stored_file_name: string;
+}

+ 3 - 1
frontend/src/main.ts

@@ -14,6 +14,8 @@ app.use(vuetify);
 app.use(i18n);
 app.use(vue3GoogleLogin, {
     clientId: '136107811725-n71808u8t465f1afhpe2e5j7mn606nd8.apps.googleusercontent.com'
-})
+});
 
 app.mount("#app");
+
+export default app;

+ 92 - 1
frontend/src/stores/main.ts

@@ -6,7 +6,7 @@ import { getLocalToken, removeLocalToken, saveLocalToken } from "@/utils";
 import type { AppNotification } from '@/interfaces';
 import type { IUserProfile, IUserProfileCreate, IUserProfileUpdate, MainState, Video, VideoCreate } from '@/interfaces';
 import i18n from '@/plugins/i18n'
-import { GoogleLogin } from "vue3-google-login";
+import WS from "@/stores/ws";
 
 const defaultState: MainState = {
   isLoggedIn: null,
@@ -256,6 +256,97 @@ export const useMainStore = defineStore("MainStoreId", {
         await mainStore.checkApiError(error);
       }
     },
+    async uploadImage(file: File[]) {
+      const mainStore = useMainStore();
+      try {
+        const loadingNotification = { content: i18n.global.t("sending"), showProgress: true };
+        mainStore.addNotification(loadingNotification);
+        const response = (
+          await Promise.all([
+            api.uploadImage(mainStore.token, file),
+            await new Promise<void>((resolve, _) => setTimeout(() => resolve(), 0)),
+          ]).then(data => {
+            for (let i = 0; i < data[0].data.filenames.length; i++) {
+              const element = data[0].data.filenames[i];
+              const tmpImage: Image = {
+                file_name: file[i].name,
+                stored_file_name: element,
+                content: "sr",
+                state: "subscribe"
+              };
+              this.addImage(tmpImage);
+            }
+            // localStorage.setItem('imagesList', JSON.stringify(this.images));
+          })
+        );
+        mainStore.removeNotification(loadingNotification);
+        mainStore.addNotification({
+          content: i18n.global.t("fileReceived"),
+          color: "success",
+        })
+        // this.actionGetVideos();
+      } catch (error) {
+        await mainStore.checkApiError(error);
+      }
+    },
+    async getImage(data: ImageDownload) {
+      const mainStore = useMainStore();
+      try {
+        const response = (
+          await Promise.all([
+            api.getImage(mainStore.token, data),
+            await new Promise<void>((resolve, _) => setTimeout(() => resolve(), 0)),
+          ])
+        );
+      } catch (error) {
+        await mainStore.checkApiError(error);
+      }
+    },
+    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 {
+        const loadingNotification = { content: i18n.global.t("sending"), showProgress: true };
+        mainStore.addNotification(loadingNotification);
+        const response = (
+          await Promise.all([
+            api.uploadArticle(mainStore.token, article_data),
+            await new Promise<void>((resolve, _) => setTimeout(() => resolve(), 0)),
+          ])
+        );
+        mainStore.removeNotification(loadingNotification);
+        mainStore.addNotification({
+          content: i18n.global.t("fileReceived"),
+          color: "success",
+        })
+        // this.actionGetVideos();
+      } catch (error) {
+        await mainStore.checkApiError(error);
+      }
+    },
     async actionGetVideos() {
       const mainStore = useMainStore();
       try {

+ 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;

+ 1 - 1
frontend/src/views/Login.vue

@@ -15,7 +15,7 @@ const callback: CallbackTypes.CredentialCallback = (response: any) => {
   console.log("Handle the response", response);
   const userData: any = decodeCredential(response.credential);
   console.log("Handle the userData", userData);
-  mainStore.googleLogin(userData.email, "google");
+  mainStore.googleLogin(userData.email);
 };
 
 // const data = ref();

+ 59 - 13
frontend/src/views/main/Image.vue

@@ -3,6 +3,9 @@ import { ref, reactive } from "vue";
 import { useMainStore } from "@/stores/main";
 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();
@@ -30,6 +33,57 @@ async function upload() {
 //     valid.value = false;
 //   }
 // }
+// props
+let dialog = reactive({
+  msg: "圖片處理需要幾秒鐘的時間,敬請耐心等候",
+  state: "info",
+  show: false,
+});
+
+async function Submit() {
+  WS.send("subscribe");
+
+  setTimeout(() => {
+    dialog.show = true;
+  }, 2000);
+  await mainStore.uploadImage(imgFiles.value);
+  (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(() => {
+  // 存入 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: name.split(".")[0],
+    stored_file_name: id,
+  };
+
+  await mainStore.getImage(data);
+}
 
 const headers = [
   {
@@ -41,7 +95,7 @@ const headers = [
   {
     title: t("state"),
     sortable: true,
-    key: "progress_state",
+    key: "state",
     align: "left",
   },
   {
@@ -87,17 +141,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"
@@ -105,10 +155,6 @@ const headers = [
             ></v-progress-circular>
             處理中
           </span>
-          <span v-else>
-            <v-icon icon="check_circle" color="success" />
-            完成
-          </span>
         </template>
         <template v-slot:item.id="{ item }">
           <v-icon icon="crop_original" />

+ 6 - 0
frontend/src/views/main/Upload.vue

@@ -274,6 +274,7 @@ async function Submit() {
                     v-for="n in templateList"
                     :key="n.template_id"
                     v-slot="{ isSelected, toggle, selectedClass }"
+                    :disabled="n.template_id !== 0"
                   >
                     <v-card
                       color="grey-lighten-1"
@@ -287,6 +288,11 @@ async function Submit() {
                         <v-icon icon="done" color="white" />
                       </span>
                       <img :src="getImageUrl('template', n.img)" alt="" />
+                      <div :class="{ 'img-disabled': n.template_id !== 0 }">
+                        <img :src="getImageUrl('template', n.img)" alt="" />
+                        <p v-if="n.template_id !== 0">Coming Soon</p>
+                      </div>
+                      <!-- <img :src="getImageUrl('template', n.img)" alt="" /> -->
                     </v-card>
                   </v-slide-group-item>
                 </v-slide-group>