SyuanYu 9 月之前
父節點
當前提交
271b3ee752
共有 10 個文件被更改,包括 788 次插入382 次删除
  1. 1 4
      src/router/index.js
  2. 4 2
      src/stores/store.js
  3. 2 2
      src/views/Step_2.vue
  4. 0 0
      src/views/Step_2_backup.vue
  5. 76 180
      src/views/Step_3.vue
  6. 221 150
      src/views/Step_4.vue
  7. 394 0
      src/views/Step_4_backup.vue
  8. 22 4
      src/views/Step_5.vue
  9. 65 37
      src/views/Step_6.vue
  10. 3 3
      src/views/Step_7.vue

+ 1 - 4
src/router/index.js

@@ -43,9 +43,6 @@ const router = createRouter({
         {
           path: 'step4',
           component: Step_4,
-          meta: {
-            requiresBgImg: true
-          }
         },
         {
           path: 'step5',
@@ -103,7 +100,7 @@ router.beforeEach((to, from, next) => {
   const store = useMainStore();
   // 檢查是否已選擇背景圖,否則不能進入結果頁(step6)
   if (to.meta.requiresBgImg && !store.assignBgImg) {
-    next('/step3'); // 返回選擇背景頁面
+    next('/step2'); // 返回選擇背景頁面
   } else {
     next();
   }

+ 4 - 2
src/stores/store.js

@@ -2,10 +2,12 @@ import { defineStore } from 'pinia';
 
 export const useMainStore = defineStore('mainStore', {
     state: () => ({
-        assignBgImg: "",
         styleNum: null,
         imgPath: "",
-        assignGender: ""
+        assignGender: "", // 性別
+        assignRace: "", // 種族
+        assignBgImg: "", // 背景
+        assignBgImgUrl: "", // 背景
     }),
     getters: {
     },

+ 2 - 2
src/views/Step_2.vue

@@ -13,12 +13,12 @@ const store = useMainStore();
 let genderList = reactive([
   {
     text: "男",
-    value: "male",
+    value: "man",
     icon: "mdi-gender-male",
   },
   {
     text: "女",
-    value: "female",
+    value: "woman",
     icon: "mdi-gender-female",
   },
 ]);

+ 0 - 0
src/views/Step_2 backup.vue → src/views/Step_2_backup.vue


+ 76 - 180
src/views/Step_3.vue

@@ -15,29 +15,59 @@ 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) => {
-    if (e.bg_img === item.bg_img) {
-      store.styleNum = index;
-    }
-  });
-
-  console.log("store.assignBgImg", store.assignBgImg);
-  console.log("store.styleNum", store.styleNum);
-}
+let parameter = ref([]);
+let imgLoading = ref(false);
 
 onMounted(() => {
-  getParameters();
+  console.log("store.assignGender", store.assignGender);
+  getIconImageList(store.assignGender);
 });
 
+// 取得種族清單
+async function getIconImageList(gender) {
+  imgLoading.value = true;
+  let url = `http://172.104.93.163:3219/fs/icon-image-list/${gender}`;
+
+  try {
+    let response = await axios.get(url);
+    console.log("response", response);
+
+    let imagePromises = response.data.map((item, index) => {
+      let imageUrl = `http://172.104.93.163:3219/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);
+}
+
+// 取得種族圖片
+async function getIconImage(url, index) {
+  try {
+    // 設定 responseType 為 arraybuffer 以取得二進位數據
+    let response = await axios.get(url, { responseType: "arraybuffer" });
+
+    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);
+  }
+}
+
 const currentPhotos = computed(() => {
   const start = currentIndex.value;
   const end = start + perPage.value;
+  console.log("store 性別", store.assignGender);
   return parameter.value.slice(start, end);
 });
 
@@ -67,155 +97,11 @@ const currentPage = computed(
   () => Math.floor(currentIndex.value / perPage.value) + 1
 );
 
-let parameterList = ref([]);
-
-// 背景清單
-let parameter = ref([
-  {
-    bg_img: "臺北陽明山-母親節.png",
-    title: "taipei_yangmingshan",
-    description: "taipei_yangmingshan_description",
-  },
-  {
-    bg_img: "台南孔廟.png",
-    title: "tainan_confucius_temple",
-    description: "tainan_confucius_temple_description",
-  },
-  {
-    bg_img: "臺北中正紀念堂-2.png",
-    title: "taipei_chiang_kai_shek_memorial_hall_2",
-    description: "taipei_chiang_kai_shek_memorial_hall_2_description",
-  },
-  {
-    bg_img: "台東嘉明湖.png",
-    title: "taitung_jiaming_lake",
-    description: "taitung_jiaming_lake_description",
-  },
-  {
-    bg_img: "中秋節.png",
-    title: "mid_autumn_festival",
-    description: "mid_autumn_festival_description",
-  },
-  {
-    bg_img: "新北野柳女王頭.png",
-    title: "new_taipei_yehliu_queen_head",
-    description: "new_taipei_yehliu_queen_head_description",
-  },
-  {
-    bg_img: "基隆和平島公園.png",
-    title: "keelung_heping_island_park",
-    description: "keelung_heping_island_park_description",
-  },
-  {
-    bg_img: "台中日月潭.png",
-    title: "taichung_sun_moon_lake",
-    description: "taichung_sun_moon_lake_description",
-  },
-  {
-    bg_img: "臺北中正紀念堂.png",
-    title: "taipei_chiang_kai_shek_memorial_hall",
-    description: "taipei_chiang_kai_shek_memorial_hall_description",
-  },
-  {
-    bg_img: "台南鹽田.png",
-    title: "tainan_salt_field",
-    description: "tainan_salt_field_description",
-  },
-  {
-    bg_img: "高雄美麗島.png",
-    title: "kaohsiung_formosa_boulevard_station",
-    description: "kaohsiung_formosa_boulevard_station_description",
-  },
-  {
-    bg_img: "新北十分瀑布.png",
-    title: "new_taipei_shifen_waterfall",
-    description: "new_taipei_shifen_waterfall_description",
-  },
-  {
-    bg_img: "臺北故宮.png",
-    title: "taipei_national_palace_museum",
-    description: "taipei_national_palace_museum_description",
-  },
-  {
-    bg_img: "臺北故宮-2.png",
-    title: "taipei_national_palace_museum_2",
-    description: "taipei_national_palace_museum_2_description",
-  },
-  {
-    bg_img: "台中歌劇院.png",
-    title: "national_taichung_theater",
-    description: "national_taichung_theater_description",
-  },
-  {
-    bg_img: "嘉義森林之歌.png",
-    title: "chiayi_song_of_forest",
-    description: "chiayi_song_of_forest_description",
-  },
-  {
-    bg_img: "基隆八斗子鐵路.png",
-    title: "keelung_baduzi_railway",
-    description: "keelung_baduzi_railway_description",
-  },
-  {
-    bg_img: "花蓮清水斷崖.png",
-    title: "hualien_qingshui_cliff",
-    description: "hualien_qingshui_cliff_description",
-  },
-  {
-    bg_img: "澎湖.png",
-    title: "penghu",
-    description: "penghu_description",
-  },
-  {
-    bg_img: "南投清境農場.png",
-    title: "nantou_qingjing_farm",
-    description: "nantou_qingjing_farm_description",
-  },
-  {
-    bg_img: "高雄流行音樂中心.png",
-    title: "kaohsiung_music_center",
-    description: "kaohsiung_music_center_description",
-  },
-  {
-    bg_img: "花蓮金針花山.png",
-    title: "hualien_daylily_mountain",
-    description: "hualien_daylily_mountain_description",
-  },
-  {
-    bg_img: "新北九份老街.png",
-    title: "new_taipei_jiufen_old_street",
-    description: "new_taipei_jiufen_old_street_description",
-  },
-  {
-    bg_img: "嘉義阿里山小火車.png",
-    title: "chiayi_alishan_forest_railways",
-    description: "chiayi_alishan_forest_railways_description",
-  },
-  {
-    bg_img: "台中高美濕地.png",
-    title: "taichung_gaomei_wetland",
-    description: "taichung_gaomei_wetland_description",
-  },
-]);
-
-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);
-  }
-}
-
 let alertShow = ref(false);
 
 function checkImg() {
-  if (store.assignBgImg && store.assignBgImg !== "") {
+  console.log("checkImg");
+  if (store.assignRace && store.assignRace !== "") {
     alertShow.value = false;
     router.push("/step4");
   } else {
@@ -225,12 +111,30 @@ function checkImg() {
     }, 2000);
   }
 }
+
+function handleBgImg(race) {
+  store.assignRace = race;
+  console.log("store.assignRace", store.assignRace);
+}
 </script>
 
 <template>
   <div class="content">
-    <p class="title">{{ t("postcard.step2.text_2") }}</p>
-    <div class="img-content">
+    <p class="title">請選擇人種</p>
+
+    <div
+      v-if="imgLoading"
+      class="d-flex flex-column align-center justify-center pt-15"
+    >
+      <v-progress-circular
+        :size="70"
+        :width="7"
+        color="white"
+        indeterminate
+      ></v-progress-circular>
+    </div>
+
+    <div v-else class="img-content">
       <div class="slider-btn">
         <button class="prev" @click="prev">
           <img class="arrow" src="../assets/img/arrow_l.png" alt="" />
@@ -241,16 +145,11 @@ function checkImg() {
       </div>
 
       <div
-        @click="handleBgImg(item)"
+        @click="handleBgImg(item.race)"
         v-for="item in currentPhotos"
         class="bg-img"
       >
-        <v-img
-          cover
-          class="cover"
-          :lazy-src="`http://172.104.93.163:3219/static/assets/img/bg/${item.bg_img}`"
-          :src="`http://172.104.93.163:3219/static/assets/img/bg/${item.bg_img}`"
-        >
+        <v-img cover class="cover" :lazy-src="item.imgUrl" :src="item.imgUrl">
           <template v-slot:placeholder>
             <div class="d-flex align-center justify-center fill-height">
               <v-progress-circular
@@ -261,10 +160,8 @@ function checkImg() {
           </template>
         </v-img>
 
-        <p>{{ t(item.title) }}</p>
-
         <img
-          v-if="item === assignBgImg"
+          v-if="item.race === store.assignRace && store.assignRace !== ''"
           class="icon active"
           src="../assets/img/confirm.png"
           alt=""
@@ -280,7 +177,7 @@ function checkImg() {
 
       <div v-if="alertShow" class="alert-item">
         <v-alert border="top" type="warning" variant="outlined" class="mt-5">
-          尚未選擇背景
+          尚未選擇
         </v-alert>
       </div>
     </div>
@@ -309,8 +206,7 @@ function checkImg() {
 
     .cover {
       max-width: 100%;
-      width: 17rem;
-      height: 20vh;
+      width: 14rem;
       object-fit: cover;
     }
   }
@@ -320,10 +216,10 @@ function checkImg() {
   }
 
   .icon {
-    width: 5rem;
+    width: 4rem;
     position: absolute;
-    top: 0.5rem;
-    right: 0.5rem;
+    top: -1rem;
+    right: -2.5rem;
   }
 }
 

+ 221 - 150
src/views/Step_4.vue

@@ -15,23 +15,70 @@ 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("");
+let race = store.assignRace; // 種族
+let gender = store.assignGender; // 性別
+let imgLoading = ref(false);
+let parameter = ref([]);
 
-function handleBgImg(item) {
-  console.log("name", item);
-  assignBgImg.value = item;
-  store.assignBgImg = item;
-  parameterList.value.filter((e, index) => {
-    if (e.bg_img === item.bg_img) {
-      store.styleNum = index;
-    }
-  });
+// 取得背景清單
+async function getTargetImageList() {
+  imgLoading.value = true;
+  let url = `http://172.104.93.163:3219/fs/target-image-list/${gender}/${race}`;
+  console.log("url", url);
+
+  try {
+    let response = await axios.get(url);
+    console.log("response", response);
 
+    let imagePromises = response.data.map((item, index) => {
+      let imageUrl = `http://172.104.93.163:3219/fs/target-image/${gender}/${race}/${item}`;
+      return getTargetImage(imageUrl, index); // 取得種族圖片
+    });
+
+    await Promise.all(imagePromises);
+    console.log("全部完成");
+
+    imgLoading.value = false;
+  } catch (error) {
+    console.log("error", error);
+  }
+}
+
+// 取得背景圖片
+async function getTargetImage(url, index) {
+  try {
+    // 設定 responseType 為 arraybuffer 以取得二進位數據
+    let response = await axios.get(url, { responseType: "arraybuffer" });
+
+    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;
   console.log("store.assignBgImg", store.assignBgImg);
-  console.log("store.styleNum", store.styleNum);
+  console.log("store.assignBgImgUrl", store.assignBgImgUrl);
+
+  // assignBgImg.value = item;
+  // store.assignBgImg = item;
+  // parameterList.value.filter((e, index) => {
+  //   if (e.bg_img === item.bg_img) {
+  //     store.styleNum = index;
+  //   }
+  // });
+  // console.log("store.styleNum", store.styleNum);
 }
 
 onMounted(() => {
+  getTargetImageList();
   getParameters();
 });
 
@@ -70,135 +117,135 @@ const currentPage = computed(
 let parameterList = ref([]);
 
 // 背景清單
-let parameter = ref([
-  {
-    bg_img: "臺北陽明山-母親節.png",
-    title: "taipei_yangmingshan",
-    description: "taipei_yangmingshan_description",
-  },
-  {
-    bg_img: "台南孔廟.png",
-    title: "tainan_confucius_temple",
-    description: "tainan_confucius_temple_description",
-  },
-  {
-    bg_img: "臺北中正紀念堂-2.png",
-    title: "taipei_chiang_kai_shek_memorial_hall_2",
-    description: "taipei_chiang_kai_shek_memorial_hall_2_description",
-  },
-  {
-    bg_img: "台東嘉明湖.png",
-    title: "taitung_jiaming_lake",
-    description: "taitung_jiaming_lake_description",
-  },
-  {
-    bg_img: "中秋節.png",
-    title: "mid_autumn_festival",
-    description: "mid_autumn_festival_description",
-  },
-  {
-    bg_img: "新北野柳女王頭.png",
-    title: "new_taipei_yehliu_queen_head",
-    description: "new_taipei_yehliu_queen_head_description",
-  },
-  {
-    bg_img: "基隆和平島公園.png",
-    title: "keelung_heping_island_park",
-    description: "keelung_heping_island_park_description",
-  },
-  {
-    bg_img: "台中日月潭.png",
-    title: "taichung_sun_moon_lake",
-    description: "taichung_sun_moon_lake_description",
-  },
-  {
-    bg_img: "臺北中正紀念堂.png",
-    title: "taipei_chiang_kai_shek_memorial_hall",
-    description: "taipei_chiang_kai_shek_memorial_hall_description",
-  },
-  {
-    bg_img: "台南鹽田.png",
-    title: "tainan_salt_field",
-    description: "tainan_salt_field_description",
-  },
-  {
-    bg_img: "高雄美麗島.png",
-    title: "kaohsiung_formosa_boulevard_station",
-    description: "kaohsiung_formosa_boulevard_station_description",
-  },
-  {
-    bg_img: "新北十分瀑布.png",
-    title: "new_taipei_shifen_waterfall",
-    description: "new_taipei_shifen_waterfall_description",
-  },
-  {
-    bg_img: "臺北故宮.png",
-    title: "taipei_national_palace_museum",
-    description: "taipei_national_palace_museum_description",
-  },
-  {
-    bg_img: "臺北故宮-2.png",
-    title: "taipei_national_palace_museum_2",
-    description: "taipei_national_palace_museum_2_description",
-  },
-  {
-    bg_img: "台中歌劇院.png",
-    title: "national_taichung_theater",
-    description: "national_taichung_theater_description",
-  },
-  {
-    bg_img: "嘉義森林之歌.png",
-    title: "chiayi_song_of_forest",
-    description: "chiayi_song_of_forest_description",
-  },
-  {
-    bg_img: "基隆八斗子鐵路.png",
-    title: "keelung_baduzi_railway",
-    description: "keelung_baduzi_railway_description",
-  },
-  {
-    bg_img: "花蓮清水斷崖.png",
-    title: "hualien_qingshui_cliff",
-    description: "hualien_qingshui_cliff_description",
-  },
-  {
-    bg_img: "澎湖.png",
-    title: "penghu",
-    description: "penghu_description",
-  },
-  {
-    bg_img: "南投清境農場.png",
-    title: "nantou_qingjing_farm",
-    description: "nantou_qingjing_farm_description",
-  },
-  {
-    bg_img: "高雄流行音樂中心.png",
-    title: "kaohsiung_music_center",
-    description: "kaohsiung_music_center_description",
-  },
-  {
-    bg_img: "花蓮金針花山.png",
-    title: "hualien_daylily_mountain",
-    description: "hualien_daylily_mountain_description",
-  },
-  {
-    bg_img: "新北九份老街.png",
-    title: "new_taipei_jiufen_old_street",
-    description: "new_taipei_jiufen_old_street_description",
-  },
-  {
-    bg_img: "嘉義阿里山小火車.png",
-    title: "chiayi_alishan_forest_railways",
-    description: "chiayi_alishan_forest_railways_description",
-  },
-  {
-    bg_img: "台中高美濕地.png",
-    title: "taichung_gaomei_wetland",
-    description: "taichung_gaomei_wetland_description",
-  },
-]);
-
-console.log("parameter", parameter.value);
+// let parameter = ref([
+//   {
+//     bg_img: "臺北陽明山-母親節.png",
+//     title: "taipei_yangmingshan",
+//     description: "taipei_yangmingshan_description",
+//   },
+//   {
+//     bg_img: "台南孔廟.png",
+//     title: "tainan_confucius_temple",
+//     description: "tainan_confucius_temple_description",
+//   },
+//   {
+//     bg_img: "臺北中正紀念堂-2.png",
+//     title: "taipei_chiang_kai_shek_memorial_hall_2",
+//     description: "taipei_chiang_kai_shek_memorial_hall_2_description",
+//   },
+//   {
+//     bg_img: "台東嘉明湖.png",
+//     title: "taitung_jiaming_lake",
+//     description: "taitung_jiaming_lake_description",
+//   },
+//   {
+//     bg_img: "中秋節.png",
+//     title: "mid_autumn_festival",
+//     description: "mid_autumn_festival_description",
+//   },
+//   {
+//     bg_img: "新北野柳女王頭.png",
+//     title: "new_taipei_yehliu_queen_head",
+//     description: "new_taipei_yehliu_queen_head_description",
+//   },
+//   {
+//     bg_img: "基隆和平島公園.png",
+//     title: "keelung_heping_island_park",
+//     description: "keelung_heping_island_park_description",
+//   },
+//   {
+//     bg_img: "台中日月潭.png",
+//     title: "taichung_sun_moon_lake",
+//     description: "taichung_sun_moon_lake_description",
+//   },
+//   {
+//     bg_img: "臺北中正紀念堂.png",
+//     title: "taipei_chiang_kai_shek_memorial_hall",
+//     description: "taipei_chiang_kai_shek_memorial_hall_description",
+//   },
+//   {
+//     bg_img: "台南鹽田.png",
+//     title: "tainan_salt_field",
+//     description: "tainan_salt_field_description",
+//   },
+//   {
+//     bg_img: "高雄美麗島.png",
+//     title: "kaohsiung_formosa_boulevard_station",
+//     description: "kaohsiung_formosa_boulevard_station_description",
+//   },
+//   {
+//     bg_img: "新北十分瀑布.png",
+//     title: "new_taipei_shifen_waterfall",
+//     description: "new_taipei_shifen_waterfall_description",
+//   },
+//   {
+//     bg_img: "臺北故宮.png",
+//     title: "taipei_national_palace_museum",
+//     description: "taipei_national_palace_museum_description",
+//   },
+//   {
+//     bg_img: "臺北故宮-2.png",
+//     title: "taipei_national_palace_museum_2",
+//     description: "taipei_national_palace_museum_2_description",
+//   },
+//   {
+//     bg_img: "台中歌劇院.png",
+//     title: "national_taichung_theater",
+//     description: "national_taichung_theater_description",
+//   },
+//   {
+//     bg_img: "嘉義森林之歌.png",
+//     title: "chiayi_song_of_forest",
+//     description: "chiayi_song_of_forest_description",
+//   },
+//   {
+//     bg_img: "基隆八斗子鐵路.png",
+//     title: "keelung_baduzi_railway",
+//     description: "keelung_baduzi_railway_description",
+//   },
+//   {
+//     bg_img: "花蓮清水斷崖.png",
+//     title: "hualien_qingshui_cliff",
+//     description: "hualien_qingshui_cliff_description",
+//   },
+//   {
+//     bg_img: "澎湖.png",
+//     title: "penghu",
+//     description: "penghu_description",
+//   },
+//   {
+//     bg_img: "南投清境農場.png",
+//     title: "nantou_qingjing_farm",
+//     description: "nantou_qingjing_farm_description",
+//   },
+//   {
+//     bg_img: "高雄流行音樂中心.png",
+//     title: "kaohsiung_music_center",
+//     description: "kaohsiung_music_center_description",
+//   },
+//   {
+//     bg_img: "花蓮金針花山.png",
+//     title: "hualien_daylily_mountain",
+//     description: "hualien_daylily_mountain_description",
+//   },
+//   {
+//     bg_img: "新北九份老街.png",
+//     title: "new_taipei_jiufen_old_street",
+//     description: "new_taipei_jiufen_old_street_description",
+//   },
+//   {
+//     bg_img: "嘉義阿里山小火車.png",
+//     title: "chiayi_alishan_forest_railways",
+//     description: "chiayi_alishan_forest_railways_description",
+//   },
+//   {
+//     bg_img: "台中高美濕地.png",
+//     title: "taichung_gaomei_wetland",
+//     description: "taichung_gaomei_wetland_description",
+//   },
+// ]);
+
+// console.log("parameter", parameter.value);
 
 async function getParameters() {
   let url = `${apiUrl}/sd/parameters`;
@@ -230,7 +277,20 @@ function checkImg() {
 <template>
   <div class="content">
     <p class="title">{{ t("postcard.step2.text_2") }}</p>
-    <div class="img-content">
+
+    <div
+      v-if="imgLoading"
+      class="d-flex flex-column align-center justify-center pt-15"
+    >
+      <v-progress-circular
+        :size="70"
+        :width="7"
+        color="white"
+        indeterminate
+      ></v-progress-circular>
+    </div>
+
+    <div v-else class="img-content">
       <div class="slider-btn">
         <button class="prev" @click="prev">
           <img class="arrow" src="../assets/img/arrow_l.png" alt="" />
@@ -245,7 +305,7 @@ function checkImg() {
         v-for="item in currentPhotos"
         class="bg-img"
       >
-        <v-img
+        <!-- <v-img
           cover
           class="cover"
           :lazy-src="`http://172.104.93.163:3219/static/assets/img/bg/${item.bg_img}`"
@@ -259,12 +319,23 @@ function checkImg() {
               ></v-progress-circular>
             </div>
           </template>
-        </v-img>
+        </v-img> -->
 
-        <p>{{ t(item.title) }}</p>
+        <!-- <p>{{ t(item.title) }}</p> -->
+
+        <v-img cover class="cover" :lazy-src="item.imgUrl" :src="item.imgUrl">
+          <template v-slot:placeholder>
+            <div class="d-flex align-center justify-center fill-height">
+              <v-progress-circular
+                color="grey-lighten-4"
+                indeterminate
+              ></v-progress-circular>
+            </div>
+          </template>
+        </v-img>
 
         <img
-          v-if="item === assignBgImg"
+          v-if="item.name === store.assignBgImg"
           class="icon active"
           src="../assets/img/confirm.png"
           alt=""
@@ -309,8 +380,8 @@ function checkImg() {
 
     .cover {
       max-width: 100%;
-      width: 17rem;
-      height: 20vh;
+      width: 75vw;
+      height: 50vw;
       object-fit: cover;
     }
   }
@@ -320,10 +391,10 @@ function checkImg() {
   }
 
   .icon {
-    width: 5rem;
+    width: 3.5rem;
     position: absolute;
-    top: 0.5rem;
-    right: 0.5rem;
+    top: 0.1rem;
+    right: 0.1rem;
   }
 }
 

+ 394 - 0
src/views/Step_4_backup.vue

@@ -0,0 +1,394 @@
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import { useMainStore } from "@/stores/store";
+import { useRouter } from "vue-router";
+import { useI18n } from "vue-i18n";
+import "animate.css";
+import axios from "axios";
+import Footer from "../components/Footer.vue";
+
+const { t } = useI18n();
+const router = useRouter();
+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) => {
+    if (e.bg_img === item.bg_img) {
+      store.styleNum = index;
+    }
+  });
+
+  console.log("store.assignBgImg", store.assignBgImg);
+  console.log("store.styleNum", store.styleNum);
+}
+
+onMounted(() => {
+  getParameters();
+});
+
+const currentPhotos = computed(() => {
+  const start = currentIndex.value;
+  const end = start + perPage.value;
+  return parameter.value.slice(start, end);
+});
+
+console.log("currentPhotos", currentPhotos);
+
+let currentIndex = ref(0);
+let perPage = ref(2);
+
+function prev() {
+  if (currentIndex.value > 0) {
+    currentIndex.value -= perPage.value;
+  }
+}
+
+function next() {
+  if (currentIndex.value + perPage.value < parameter.value.length) {
+    currentIndex.value += perPage.value;
+  }
+}
+
+// 計算頁數
+const totalPages = computed(() =>
+  Math.ceil(parameter.value.length / perPage.value)
+);
+
+const currentPage = computed(
+  () => Math.floor(currentIndex.value / perPage.value) + 1
+);
+
+let parameterList = ref([]);
+
+// 背景清單
+let parameter = ref([
+  {
+    bg_img: "臺北陽明山-母親節.png",
+    title: "taipei_yangmingshan",
+    description: "taipei_yangmingshan_description",
+  },
+  {
+    bg_img: "台南孔廟.png",
+    title: "tainan_confucius_temple",
+    description: "tainan_confucius_temple_description",
+  },
+  {
+    bg_img: "臺北中正紀念堂-2.png",
+    title: "taipei_chiang_kai_shek_memorial_hall_2",
+    description: "taipei_chiang_kai_shek_memorial_hall_2_description",
+  },
+  {
+    bg_img: "台東嘉明湖.png",
+    title: "taitung_jiaming_lake",
+    description: "taitung_jiaming_lake_description",
+  },
+  {
+    bg_img: "中秋節.png",
+    title: "mid_autumn_festival",
+    description: "mid_autumn_festival_description",
+  },
+  {
+    bg_img: "新北野柳女王頭.png",
+    title: "new_taipei_yehliu_queen_head",
+    description: "new_taipei_yehliu_queen_head_description",
+  },
+  {
+    bg_img: "基隆和平島公園.png",
+    title: "keelung_heping_island_park",
+    description: "keelung_heping_island_park_description",
+  },
+  {
+    bg_img: "台中日月潭.png",
+    title: "taichung_sun_moon_lake",
+    description: "taichung_sun_moon_lake_description",
+  },
+  {
+    bg_img: "臺北中正紀念堂.png",
+    title: "taipei_chiang_kai_shek_memorial_hall",
+    description: "taipei_chiang_kai_shek_memorial_hall_description",
+  },
+  {
+    bg_img: "台南鹽田.png",
+    title: "tainan_salt_field",
+    description: "tainan_salt_field_description",
+  },
+  {
+    bg_img: "高雄美麗島.png",
+    title: "kaohsiung_formosa_boulevard_station",
+    description: "kaohsiung_formosa_boulevard_station_description",
+  },
+  {
+    bg_img: "新北十分瀑布.png",
+    title: "new_taipei_shifen_waterfall",
+    description: "new_taipei_shifen_waterfall_description",
+  },
+  {
+    bg_img: "臺北故宮.png",
+    title: "taipei_national_palace_museum",
+    description: "taipei_national_palace_museum_description",
+  },
+  {
+    bg_img: "臺北故宮-2.png",
+    title: "taipei_national_palace_museum_2",
+    description: "taipei_national_palace_museum_2_description",
+  },
+  {
+    bg_img: "台中歌劇院.png",
+    title: "national_taichung_theater",
+    description: "national_taichung_theater_description",
+  },
+  {
+    bg_img: "嘉義森林之歌.png",
+    title: "chiayi_song_of_forest",
+    description: "chiayi_song_of_forest_description",
+  },
+  {
+    bg_img: "基隆八斗子鐵路.png",
+    title: "keelung_baduzi_railway",
+    description: "keelung_baduzi_railway_description",
+  },
+  {
+    bg_img: "花蓮清水斷崖.png",
+    title: "hualien_qingshui_cliff",
+    description: "hualien_qingshui_cliff_description",
+  },
+  {
+    bg_img: "澎湖.png",
+    title: "penghu",
+    description: "penghu_description",
+  },
+  {
+    bg_img: "南投清境農場.png",
+    title: "nantou_qingjing_farm",
+    description: "nantou_qingjing_farm_description",
+  },
+  {
+    bg_img: "高雄流行音樂中心.png",
+    title: "kaohsiung_music_center",
+    description: "kaohsiung_music_center_description",
+  },
+  {
+    bg_img: "花蓮金針花山.png",
+    title: "hualien_daylily_mountain",
+    description: "hualien_daylily_mountain_description",
+  },
+  {
+    bg_img: "新北九份老街.png",
+    title: "new_taipei_jiufen_old_street",
+    description: "new_taipei_jiufen_old_street_description",
+  },
+  {
+    bg_img: "嘉義阿里山小火車.png",
+    title: "chiayi_alishan_forest_railways",
+    description: "chiayi_alishan_forest_railways_description",
+  },
+  {
+    bg_img: "台中高美濕地.png",
+    title: "taichung_gaomei_wetland",
+    description: "taichung_gaomei_wetland_description",
+  },
+]);
+
+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);
+  }
+}
+
+let alertShow = ref(false);
+
+function checkImg() {
+  if (store.assignBgImg && store.assignBgImg !== "") {
+    alertShow.value = false;
+    router.push("/step5");
+  } else {
+    alertShow.value = true;
+    setTimeout(() => {
+      alertShow.value = false;
+    }, 2000);
+  }
+}
+</script>
+
+<template>
+  <div class="content">
+    <p class="title">{{ t("postcard.step2.text_2") }}</p>
+    <div class="img-content">
+      <div class="slider-btn">
+        <button class="prev" @click="prev">
+          <img class="arrow" src="../assets/img/arrow_l.png" alt="" />
+        </button>
+        <button class="next" @click="next">
+          <img class="arrow" src="../assets/img/arrow_r.png" alt="" />
+        </button>
+      </div>
+
+      <div
+        @click="handleBgImg(item)"
+        v-for="item in currentPhotos"
+        class="bg-img"
+      >
+        <v-img
+          cover
+          class="cover"
+          :lazy-src="`http://172.104.93.163:3219/static/assets/img/bg/${item.bg_img}`"
+          :src="`http://172.104.93.163:3219/static/assets/img/bg/${item.bg_img}`"
+        >
+          <template v-slot:placeholder>
+            <div class="d-flex align-center justify-center fill-height">
+              <v-progress-circular
+                color="grey-lighten-4"
+                indeterminate
+              ></v-progress-circular>
+            </div>
+          </template>
+        </v-img>
+
+        <p>{{ t(item.title) }}</p>
+
+        <img
+          v-if="item === assignBgImg"
+          class="icon active"
+          src="../assets/img/confirm.png"
+          alt=""
+        />
+        <img v-else class="icon" src="../assets/img/confirm-solid.png" alt="" />
+      </div>
+
+      <span class="page-num">{{ currentPage }} / {{ totalPages }}</span>
+
+      <a @click="checkImg()" href="javascript:;" class="main-btn">
+        {{ t("next_step") }}
+      </a>
+
+      <div v-if="alertShow" class="alert-item">
+        <v-alert border="top" type="warning" variant="outlined" class="mt-5">
+          尚未選擇背景
+        </v-alert>
+      </div>
+    </div>
+
+    <Footer url="/step3" />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.img-content {
+  // height: 80vh;
+  padding: 0 2rem;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  @media (max-width: 600px) {
+    height: 100%;
+    padding: 0 4rem;
+  }
+
+  .bg-img {
+    margin-bottom: 2rem;
+    cursor: pointer;
+    position: relative;
+
+    .cover {
+      max-width: 100%;
+      width: 17rem;
+      height: 20vh;
+      object-fit: cover;
+    }
+  }
+
+  p {
+    margin-top: 0.5rem;
+  }
+
+  .icon {
+    width: 5rem;
+    position: absolute;
+    top: 0.5rem;
+    right: 0.5rem;
+  }
+}
+
+.slider-btn {
+  width: 100%;
+  position: absolute;
+  z-index: 100;
+  top: 50vh;
+
+  img {
+    width: 100px;
+    transition: all 0.2s;
+
+    @media (max-width: 600px) {
+      width: 50px;
+    }
+  }
+
+  .prev,
+  .next {
+    position: absolute;
+    cursor: pointer;
+    border: none;
+    background-color: transparent;
+
+    &:hover {
+      img {
+        opacity: 0.7;
+      }
+    }
+  }
+
+  .prev {
+    left: 0;
+  }
+
+  .next {
+    right: 0;
+  }
+}
+
+.page-num {
+  margin: auto auto 2.2rem;
+  color: white;
+  letter-spacing: 0.2rem;
+}
+
+.content {
+  @media (max-width: 600px) {
+    min-height: 100vh;
+  }
+}
+
+.alert-item {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+
+  .v-alert {
+    background-color: var(--sub-color);
+  }
+
+  .text-warning {
+    color: var(--main-color) !important;
+  }
+}
+</style>

+ 22 - 4
src/views/Step_5.vue

@@ -6,6 +6,7 @@ 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>
@@ -15,6 +16,23 @@ console.log("step5 store.assignBgImg", store.assignBgImg);
     >
       <div>
         <v-img
+          width="85vw"
+          cover
+          class="my-5 mx-auto"
+          :lazy-src="store.assignBgImgUrl"
+          :src="store.assignBgImgUrl"
+        >
+          <template v-slot:placeholder>
+            <div class="d-flex align-center justify-center fill-height">
+              <v-progress-circular
+                color="grey-lighten-4"
+                indeterminate
+              ></v-progress-circular>
+            </div>
+          </template>
+        </v-img>
+
+        <!-- <v-img
           max-width="500"
           cover
           class="my-5 mx-auto"
@@ -29,14 +47,14 @@ console.log("step5 store.assignBgImg", store.assignBgImg);
               ></v-progress-circular>
             </div>
           </template>
-        </v-img>
+        </v-img> -->
 
-        <p>{{ t(store.assignBgImg.title) }}</p>
+        <!-- <p>{{ t(store.assignBgImg.title) }}</p> -->
 
-        <p
+        <!-- <p
           class="text-start px-5 my-10 description"
           v-html="t(store.assignBgImg.description)"
-        ></p>
+        ></p> -->
       </div>
 
       <router-link to="/step6" class="main-btn mt-auto">

+ 65 - 37
src/views/Step_6.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;
 
-// 測試開始
 const isShowModal = ref(false);
 const uploadInput = ref(null);
 const pic = ref("");
@@ -90,26 +89,7 @@ function remove() {
 console.log("step5 store.assignBgImg", store.assignBgImg);
 
 let file = ref(null);
-// let fileInput = ref(null);
 let imgFile = ref(null);
-// let imageUrl = ref(null);
-
-// 選擇檔案
-// function onFileChange() {
-//   console.log("fileInput", fileInput.value);
-//   if (fileInput.value.files.length) {
-//     imgFile.value = fileInput.value.files[0];
-
-//     // 預覽相片
-//     const reader = new FileReader();
-//     reader.onload = () => {
-//       imageUrl.value = reader.result;
-//     };
-//     reader.readAsDataURL(imgFile.value);
-//   }
-//   console.log("imgFile.value", imgFile.value);
-// }
-
 let imgLoading = ref(false);
 
 // 算圖欄位
@@ -121,6 +101,43 @@ let runParameters = reactive({
 });
 
 // 算圖
+// 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) {
@@ -129,20 +146,30 @@ async function upload() {
 
   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}`;
+  let url = `http://172.104.93.163:3219/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);
 
   try {
-    let response = await axios.post(url, formData);
+    let response = await axios.post(url, formData, {
+      responseType: "arraybuffer",
+    });
     console.log("runImg", response);
 
     if (response.status === 200) {
-      store.imgPath = response.data[0].path;
+      // store.imgPath = response.data[0].path;
+
+      // 圖檔轉網址
+      let blob = new Blob([response.data], { type: "image/png" }); // 創建 blob
+      let imageUrl = URL.createObjectURL(blob); // 創建圖片 URL
+      store.imgPath = imageUrl;
+      console.log("imageUrl", imageUrl);
+
       imgLoading.value = false;
       console.log("store.imgPath", store.imgPath);
 
@@ -247,16 +274,16 @@ const openUploadInput = () => {
             class="preview-img mt-5"
             :style="`background-image: url('${result.dataURL}')`"
           >
-            <div
+            <!-- 圖片遮罩 -->
+            <!-- <div
               class="mask"
               :style="`background-image: url('${result.dataURL}')`"
-            ></div>
-            <!-- <img :src="result.dataURL" /> -->
+            ></div> -->
           </div>
 
-          <p class="text-white mt-5">
+          <!-- <p class="text-white mt-5">
             將人像放置於畫面右下角<br />會得到最好看的畫面唷!
-          </p>
+          </p> -->
         </section>
 
         <!-- <v-file-input
@@ -310,14 +337,15 @@ const openUploadInput = () => {
   background-position: center center;
   background-size: cover;
 
-  &::after {
-    position: absolute;
-    content: "";
-    width: 100%;
-    height: 100%;
-    background-color: rgba(0, 0, 0, 0.7);
-    z-index: 0;
-  }
+  // 隱藏遮罩
+  // &::after {
+  //   position: absolute;
+  //   content: "";
+  //   width: 100%;
+  //   height: 100%;
+  //   background-color: rgba(0, 0, 0, 0.7);
+  //   z-index: 0;
+  // }
 
   .mask {
     position: absolute;

+ 3 - 3
src/views/Step_7.vue

@@ -88,13 +88,13 @@ async function share() {
           :src="store.imgPath"
           alt=""
         />
-        <p>{{ t(store.assignBgImg.title) }}</p>
+        <!-- <p>{{ t(store.assignBgImg.title) }}</p> -->
       </div>
 
-      <p
+      <!-- <p
         class="text-start px-5 description"
         v-html="t(store.assignBgImg.description)"
-      ></p>
+      ></p> -->
       <!-- <button @click="share()" class="main-btn mt-15">分享相片</button> -->
       <button @click="downloadImage(store.imgPath)" class="main-btn mt-15">
         儲存相片