SyuanYu 11 months ago
parent
commit
0400461597

+ 1 - 1
index.html

@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="UTF-8">
-  <link rel="icon" href="/favicon.png">
+  <!-- <link rel="icon" href="/favicon.png"> -->
   <!-- Google Fonts -->
   <link rel="preconnect" href="https://fonts.googleapis.com">
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

+ 15 - 0
package-lock.json

@@ -12,6 +12,7 @@
         "animate.css": "^4.1.1",
         "axios": "^1.6.7",
         "pinia": "^2.1.7",
+        "qrcode.vue": "^3.4.1",
         "vue": "^3.3.11",
         "vue-i18n": "^9.13.1",
         "vue-picture-cropper": "^0.7.0",
@@ -1238,6 +1239,14 @@
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
       "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
     },
+    "node_modules/qrcode.vue": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.4.1.tgz",
+      "integrity": "sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==",
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -2234,6 +2243,12 @@
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
       "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
     },
+    "qrcode.vue": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.4.1.tgz",
+      "integrity": "sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==",
+      "requires": {}
+    },
     "readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
     "animate.css": "^4.1.1",
     "axios": "^1.6.7",
     "pinia": "^2.1.7",
+    "qrcode.vue": "^3.4.1",
     "vue": "^3.3.11",
     "vue-i18n": "^9.13.1",
     "vue-picture-cropper": "^0.7.0",

+ 0 - 0
src/assets/img/background_1.webp → src/assets/img/background-1.webp


+ 0 - 0
src/assets/img/button2.png → src/assets/img/button-2.png


BIN
src/assets/img/confirm-2.png


BIN
src/assets/img/confirm3.png


BIN
src/assets/img/coupon.png


+ 4 - 0
src/router/index.js

@@ -23,6 +23,10 @@ const router = createRouter({
       name: 'home',
       component: HomeView,
       children: [
+        {
+          path: '/',
+          component: Step_1,
+        },
         {
           path: 'step1',
           component: Step_1,

+ 1 - 0
src/stores/store.js

@@ -4,6 +4,7 @@ export const useMainStore = defineStore('mainStore', {
     state: () => ({
         styleNum: null,
         imgPath: "",
+        imgFile: "",
         assignGender: "", // 性別
         assignRace: "", // 種族
         assignBgImg: "", // 背景圖片檔名

+ 2 - 2
src/views/HomeView.vue

@@ -50,12 +50,12 @@ p {
 
 .main-btn {
   width: 15rem;
-  padding: 1.2rem;
+  padding: 1rem;
   display: block;
   font-size: 1.25rem;
   font-weight: 600;
   color: #fff;
-  background-image: url("../assets/img/button2.png");
+  background-image: url("../assets/img/button-2.png");
   background-position: center;
   background-size: cover;
   // border: 2px solid var(--main-color);

+ 0 - 3
src/views/SkylanternHome.vue

@@ -6,9 +6,7 @@ const route = useRoute();
 const router = useRouter();
 
 onMounted(() => {
-  console.log('onMounted');
   let lang = localStorage.getItem("lang");
-  console.log("lang >>", lang);
 
   if (lang && lang !== "") {
     chooseLang(lang);
@@ -16,7 +14,6 @@ onMounted(() => {
 });
 
 function chooseLang(lang) {
-  console.log("選擇語言:", lang);
   localStorage.setItem("lang", lang);
   router.push("/skylantern");
 }

+ 14 - 5
src/views/Step_1.vue

@@ -119,6 +119,9 @@ span {
   justify-content: center;
 
   p {
+    position: relative;
+    z-index: 100;
+
     &:first-child {
       margin-bottom: 1.625rem;
     }
@@ -126,6 +129,8 @@ span {
 
   .main-btn {
     margin: 3rem auto;
+    position: relative;
+    z-index: 100;
   }
 
   .hashtag {
@@ -140,12 +145,10 @@ span {
       }
     }
   }
-}
 
-#marquee {
-  position: absolute;
-  top: 50%;
-  transform: translate(0, -50%);
+  #marquee {
+    position: relative;
+  }
 }
 
 .lang-content {
@@ -194,5 +197,11 @@ span {
     position: relative;
     z-index: 100;
   }
+
+  #marquee {
+    position: absolute;
+    top: 50%;
+    transform: translate(0, -50%);
+  }
 }
 </style>

+ 3 - 5
src/views/Step_2.vue

@@ -25,13 +25,11 @@ let genderList = reactive([
 
 function setGender(value) {
   store.assignGender = value;
-  console.log("setGender", store.assignGender);
 }
 
 let alertShow = ref(false);
 
 function check() {
-  console.log("store.assignGender", store.assignGender);
   if (store.assignGender === "") {
     // alert("尚未選擇性別");
     alertShow.value = true;
@@ -103,12 +101,12 @@ span {
 
   button {
     display: flex;
-    width: 14rem;
+    width: 13rem;
     padding: 0.8rem;
     margin-bottom: 2rem;
     border-radius: 100px;
     border: 3px solid transparent;
-    background-image: url("../assets/img/button2.png");
+    background-image: url("../assets/img/button-2.png");
     background-position: center;
     background-size: cover;
     // background-color: var(--main-color);
@@ -123,7 +121,7 @@ span {
 
     &.assign {
       border: 3px solid white;
-      background-color: #ae774f;
+      // background-color: #ae774f;
     }
   }
 }

+ 1 - 15
src/views/Step_3.vue

@@ -13,13 +13,11 @@ const store = useMainStore();
 
 const apiUrl = import.meta.env.VITE_API_URL;
 const imgUrl = import.meta.env.VITE_API_IMG_URL;
-console.log("VITE_API_URL", apiUrl);
 
 let parameter = ref([]);
 let imgLoading = ref(false);
 
 onMounted(() => {
-  console.log("store.assignGender", store.assignGender);
   getIconImageList(store.assignGender);
 });
 
@@ -30,22 +28,17 @@ async function getIconImageList(gender) {
 
   try {
     let response = await axios.get(url);
-    console.log("response", response);
-
     let imagePromises = response.data.map((item, index) => {
       let imageUrl = `https://cmm.ai/postcard/fs/icon-image/${gender}/${item}`;
       return getIconImage(imageUrl, index); // 取得種族圖片
     });
 
     await Promise.all(imagePromises);
-    console.log("全部完成");
 
     imgLoading.value = false;
   } catch (error) {
     console.log("error", error);
   }
-
-  console.log("getIconImageList url", url);
 }
 
 // 取得種族圖片
@@ -56,9 +49,7 @@ async function getIconImage(url, index) {
 
     let blob = new Blob([response.data], { type: "image/png" }); // 創建 blob
     let imageUrl = URL.createObjectURL(blob); // 創建圖片 URL
-    console.log("imageUrl", imageUrl);
     parameter.value.push({ imgUrl: imageUrl, race: `${index}` });
-    console.log("parameter.value", parameter.value);
   } catch (error) {
     console.log("error", error);
   }
@@ -67,12 +58,9 @@ async function getIconImage(url, index) {
 const currentPhotos = computed(() => {
   const start = currentIndex.value;
   const end = start + perPage.value;
-  console.log("store 性別", store.assignGender);
   return parameter.value.slice(start, end);
 });
 
-console.log("currentPhotos", currentPhotos);
-
 let currentIndex = ref(0);
 let perPage = ref(2);
 
@@ -100,7 +88,6 @@ const currentPage = computed(
 let alertShow = ref(false);
 
 function checkImg() {
-  console.log("checkImg");
   if (store.assignRace && store.assignRace !== "") {
     alertShow.value = false;
     router.push("/step4");
@@ -114,7 +101,6 @@ function checkImg() {
 
 function handleBgImg(race) {
   store.assignRace = race;
-  console.log("store.assignRace", store.assignRace);
 }
 </script>
 
@@ -163,7 +149,7 @@ function handleBgImg(race) {
         <img
           v-if="item.race === store.assignRace && store.assignRace !== ''"
           class="icon active"
-          src="../assets/img/confirm3.png"
+          src="../assets/img/confirm-2.png"
           alt=""
         />
         <img v-else class="icon" src="../assets/img/confirm-solid.png" alt="" />

+ 1 - 15
src/views/Step_4.vue

@@ -13,7 +13,6 @@ const store = useMainStore();
 
 const apiUrl = import.meta.env.VITE_API_URL;
 const imgUrl = import.meta.env.VITE_API_IMG_URL;
-console.log("VITE_API_URL", apiUrl);
 
 let race = store.assignRace; // 種族
 let gender = store.assignGender; // 性別
@@ -30,7 +29,6 @@ onMounted(() => {
 async function getTargetImageList() {
   imgLoading.value = true;
   let url = `https://cmm.ai/postcard/fs/target-image-list/${gender}/${race}`;
-  console.log("url", url);
 
   try {
     let response = await axios.get(url);
@@ -42,7 +40,6 @@ async function getTargetImageList() {
     });
 
     await Promise.all(imagePromises);
-    console.log("全部完成");
 
     // 依照檔名進行排序 (0-9)
     parameter.value.sort((a, b) => {
@@ -63,7 +60,6 @@ async function getTargetImageList() {
       name: `${index}.jpg`,
       title: landmarkNames.value[index],
     }));
-    console.log("items >>>", parameter.value);
 
     imgLoading.value = false;
   } catch (error) {
@@ -80,8 +76,6 @@ async function getLandmark() {
   try {
     let response = await axios.get(url);
     landmarkNames.value = response.data;
-
-    console.log("getLandmark", landmarkNames.value);
   } catch (error) {
     console.log("error", error);
   }
@@ -95,21 +89,16 @@ async function getTargetImage(url, index) {
 
     let blob = new Blob([response.data], { type: "image/png" }); // 創建 blob
     let imageUrl = URL.createObjectURL(blob); // 創建圖片 URL
-    console.log("imageUrl", imageUrl);
     parameter.value.push({ imgUrl: imageUrl, name: `${index}.jpg` });
-    console.log("parameter.value", parameter.value);
   } catch (error) {
     console.log("error", error);
   }
 }
 
 function handleBgImg(item) {
-  console.log("item", item);
   store.assignBgImg = item.name;
   store.assignBgImgUrl = item.imgUrl;
   store.assignBgImgTitle = item.title;
-  console.log("store.assignBgImg", store.assignBgImg);
-  console.log("store.assignBgImgUrl", store.assignBgImgUrl);
 }
 
 const currentPhotos = computed(() => {
@@ -118,8 +107,6 @@ const currentPhotos = computed(() => {
   return parameter.value.slice(start, end);
 });
 
-console.log("currentPhotos", currentPhotos);
-
 let currentIndex = ref(0);
 let perPage = ref(2);
 
@@ -283,7 +270,6 @@ async function getParameters() {
   try {
     let response = await axios.get(url);
     parameterList.value = response.data;
-    console.log("parameterList", parameterList.value);
   } catch (error) {
     console.log("error", error);
   }
@@ -367,7 +353,7 @@ function checkImg() {
         <img
           v-if="item.name === store.assignBgImg"
           class="icon active"
-          src="../assets/img/confirm3.png"
+          src="../assets/img/confirm-2.png"
           alt=""
         />
         <img v-else class="icon" src="../assets/img/confirm-solid.png" alt="" />

+ 0 - 10
src/views/Step_4_backup.vue

@@ -13,12 +13,10 @@ const store = useMainStore();
 
 const apiUrl = import.meta.env.VITE_API_URL;
 const imgUrl = import.meta.env.VITE_API_IMG_URL;
-console.log("VITE_API_URL", apiUrl);
 
 let assignBgImg = ref("");
 
 function handleBgImg(item) {
-  console.log("name", item);
   assignBgImg.value = item;
   store.assignBgImg = item;
   parameterList.value.filter((e, index) => {
@@ -26,9 +24,6 @@ function handleBgImg(item) {
       store.styleNum = index;
     }
   });
-
-  console.log("store.assignBgImg", store.assignBgImg);
-  console.log("store.styleNum", store.styleNum);
 }
 
 onMounted(() => {
@@ -41,8 +36,6 @@ const currentPhotos = computed(() => {
   return parameter.value.slice(start, end);
 });
 
-console.log("currentPhotos", currentPhotos);
-
 let currentIndex = ref(0);
 let perPage = ref(2);
 
@@ -198,15 +191,12 @@ let parameter = ref([
   },
 ]);
 
-console.log("parameter", parameter.value);
-
 async function getParameters() {
   let url = `${apiUrl}/sd/parameters`;
 
   try {
     let response = await axios.get(url);
     parameterList.value = response.data;
-    console.log("parameterList", parameterList.value);
   } catch (error) {
     console.log("error", error);
   }

+ 5 - 7
src/views/Step_5.vue

@@ -5,14 +5,12 @@ import Footer from "../components/Footer.vue";
 
 const { t } = useI18n();
 const store = useMainStore();
-console.log("step5 store.assignBgImg", store.assignBgImg);
-console.log("step5 store.assignBgImgUrl", store.assignBgImgUrl);
 </script>
 
 <template>
   <div class="content main-bg">
     <v-container
-      class="px-5 px-sm-15 mt-15 d-flex flex-column align-center justify-center"
+      class="px-5 px-sm-15 d-flex flex-column align-center justify-center"
     >
       <div>
         <v-img
@@ -59,7 +57,7 @@ console.log("step5 store.assignBgImgUrl", store.assignBgImgUrl);
         ></p> -->
       </div>
 
-      <router-link to="/step6" class="main-btn mt-auto">
+      <router-link to="/step6" class="main-btn mt-10">
         {{ t("confirm") }}
       </router-link>
     </v-container>
@@ -69,9 +67,9 @@ console.log("step5 store.assignBgImgUrl", store.assignBgImgUrl);
 </template>
 
 <style lang="scss" scoped>
-.v-container {
-  min-height: 75vh;
-}
+// .v-container {
+//   min-height: 75vh;
+// }
 
 .content {
   min-height: 100vh;

+ 2 - 55
src/views/Step_6.vue

@@ -57,12 +57,8 @@ async function getResult() {
     fileName: "fileName",
   });
 
-  console.log("imgFile.value >>", imgFile.value);
-
   result.dataURL = base64;
   isShowModal.value = false;
-  // result.blobURL = URL.createObjectURL(blob);
-  // console.log({ base64, blob, file });
 }
 
 // 清除
@@ -86,60 +82,16 @@ function remove() {
   result.dataURL = "";
 }
 
-console.log("step5 store.assignBgImg", store.assignBgImg);
-
 let file = ref(null);
 let imgFile = ref(null);
 let imgLoading = ref(false);
 
-// 算圖欄位
-let runParameters = reactive({
-  seed: "54987890",
-  denoising_strength: "0.35",
-  batch_size: "1",
-  n_iter: "30",
-});
-
-// 算圖
-// async function upload() {
-//   console.log("upload");
-//   if (!imgFile.value) {
-//     return;
-//   }
-
-//   store.imgPath = "";
-//   imgLoading.value = true;
-//   console.log("store styleNum", store.styleNum);
-
-//   let url = `${apiUrl}/sd/run?seed=${runParameters.seed}&denoising_strength=${runParameters.denoising_strength}&batch_size=${runParameters.batch_size}&n_iter=${runParameters.n_iter}&style_num=${store.styleNum}`;
-
-//   // 人物圖
-//   const formData = new FormData();
-//   formData.append("file", imgFile.value);
-
-//   try {
-//     let response = await axios.post(url, formData);
-//     console.log("runImg", response);
-
-//     if (response.status === 200) {
-//       store.imgPath = response.data[0].path;
-//       imgLoading.value = false;
-//       console.log("store.imgPath", store.imgPath);
-
-//       router.push("/step7");
-//     }
-//   } catch (error) {
-//     console.log("error", error);
-//   }
-// }
-
 let race = store.assignRace; // 種族
 let gender = store.assignGender; // 性別
 let bgImg = store.assignBgImg; // 背景
 
 // 換臉
 async function upload() {
-  console.log("upload");
   if (!imgFile.value) {
     return;
   }
@@ -149,8 +101,6 @@ async function upload() {
 
   let url = `https://cmm.ai/postcard/fs/swap-face/${gender}/${race}/${bgImg}`;
 
-  // let url = `${apiUrl}/sd/run?seed=${runParameters.seed}&denoising_strength=${runParameters.denoising_strength}&batch_size=${runParameters.batch_size}&n_iter=${runParameters.n_iter}&style_num=${store.styleNum}`;
-
   // 人物圖
   const formData = new FormData();
   formData.append("file", imgFile.value);
@@ -167,12 +117,9 @@ async function upload() {
       // 圖檔轉網址
       let blob = new Blob([response.data], { type: "image/png" }); // 創建 blob
       let imageUrl = URL.createObjectURL(blob); // 創建圖片 URL
-      store.imgPath = imageUrl;
-      console.log("imageUrl", imageUrl);
-
+      store.imgFile = blob; // 圖片檔案
+      store.imgPath = imageUrl; // 圖片網址
       imgLoading.value = false;
-      console.log("store.imgPath", store.imgPath);
-
       router.push("/step7");
     }
   } catch (error) {

+ 115 - 26
src/views/Step_7.vue

@@ -3,6 +3,7 @@ import { ref } from "vue";
 import { useI18n } from "vue-i18n";
 import { useMainStore } from "@/stores/store";
 import axios from "axios";
+import QrcodeVue from "qrcode.vue";
 import Footer from "../components/Footer.vue";
 
 const { t } = useI18n();
@@ -15,9 +16,6 @@ const shareData = {
   text: "AI明信片", // 文字內容
 };
 
-console.log("shareData", shareData);
-console.log("store.assignBgImgTitle", store.assignBgImgTitle);
-
 const imageUrl = ref("");
 
 // 儲存圖片
@@ -59,22 +57,50 @@ async function share() {
       alert(err);
     }
   }
+}
+
+let dialog = ref(false);
+let randomUrl = ref(""); // 館外店家連結
+let randomStoreName = ref(""); // 館外店家名稱
+
+// 取得隨機館外店家
+async function getBrand() {
+  let url =
+    "https://cmm.ai:9101/find_brand?keyword=%E9%A4%A8%E5%A4%96&language=ch";
+
+  try {
+    let response = await axios.get(url);
+    console.log("getBrand", response);
 
-  // console.log("share");
-  // if (navigator.share) {
-  //   try {
-  //     await navigator.share({
-  //       title: "101",
-  //       text: "AI明信片",
-  //       url: store.imgPath,
-  //     });
-  //     console.log("分享成功");
-  //   } catch (error) {
-  //     console.error("分享失敗", error);
-  //   }
-  // } else {
-  //   alert("Web Share API 不支援在此設備上運行");
-  // }
+    const randomItem =
+      response.data.data[Math.floor(Math.random() * response.data.data.length)];
+
+    randomUrl.value = randomItem.info.url;
+    randomStoreName.value = randomItem.info.name;
+
+    setPostcardLog();
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+getBrand();
+
+// 儲存 Log
+async function setPostcardLog() {
+  let url = `https://cmm.ai:9101/ai-postcard-log/`;
+
+  const formData = new FormData();
+  formData.append("url", randomUrl.value);
+  formData.append("file", store.imgFile);
+  formData.append("store_name", randomStoreName.value);
+
+  try {
+    let response = await axios.post(url, formData);
+    console.log("setPostcardLog", response);
+  } catch (error) {
+    console.log("error", error);
+  }
 }
 </script>
 
@@ -100,12 +126,46 @@ async function share() {
 
       <!-- <button @click="share()" class="main-btn mt-15">分享相片</button> -->
 
-      <div class="d-flex flex-column mt-15">
+      <div class="d-flex flex-column mt-10">
         <button @click="downloadImage(store.imgPath)" class="main-btn mb-7">
           {{ t("save_photo") }}
         </button>
 
-        <button class="main-btn">{{ t("get_coupon") }}</button>
+        <!-- <a :href="randomUrl" target="_blank" class="main-btn">
+          {{ t("get_coupon") }}
+        </a> -->
+
+        <button @click="dialog = true" class="main-btn">
+          {{ t("get_coupon") }}
+        </button>
+
+        <v-dialog v-model="dialog" width="auto">
+          <v-card max-width="400">
+            <v-card-title class="pa-6">
+              {{ randomStoreName }} <br />
+              5G活動優惠
+            </v-card-title>
+
+            <v-card-text class="pt-0">
+              <div class="coupon">
+                <img class="bg-img" src="../assets/img/coupon.png" alt="" />
+
+                <img class="result-img" :src="store.imgPath" alt="" />
+
+                <span class="qrcode">
+                  <qrcode-vue :value="randomUrl" size="100" level="H" />
+                </span>
+              </div>
+            </v-card-text>
+            <template v-slot:actions>
+              <v-btn
+                class="ms-auto"
+                text="關閉"
+                @click="dialog = false"
+              ></v-btn>
+            </template>
+          </v-card>
+        </v-dialog>
       </div>
 
       <v-dialog v-if="store.assignBgImgTitle === '南投日月潭'" max-width="300">
@@ -161,7 +221,14 @@ async function share() {
 
   .img-item {
     img {
-      border: 8px solid white;
+      border: 8px solid transparent;
+      border-image: linear-gradient(
+          to right,
+          rgba(138, 160, 172, 0.75),
+          rgba(165, 142, 116, 0.75),
+          rgba(128, 108, 87, 0.75)
+        )
+        1;
     }
 
     p {
@@ -174,12 +241,34 @@ async function share() {
   }
 }
 
-.v-card-title {
-  letter-spacing: 1px;
-}
-
 a {
-  color: var(--main-color);
+  color: white;
   text-decoration: none;
 }
+
+.coupon {
+  position: relative;
+  .bg-img {
+    width: 100%;
+    position: relative;
+    z-index: 100;
+  }
+
+  .result-img {
+    width: 100%;
+    position: absolute;
+    left: 0;
+  }
+
+  .qrcode {
+    position: absolute;
+    right: 5vw;
+    bottom: 5vw;
+    z-index: 100;
+  }
+}
+
+.v-card-title {
+  letter-spacing: 1px;
+}
 </style>