|
@@ -1,68 +1,494 @@
|
|
|
<script setup>
|
|
|
-import { ref, reactive } from "vue";
|
|
|
+import { ref, reactive, computed, onMounted } from "vue";
|
|
|
+import { useMainStore } from "@/stores/store";
|
|
|
+import { useRouter } from "vue-router";
|
|
|
import "animate.css";
|
|
|
import axios from "axios";
|
|
|
+import Footer from "../components/Footer.vue";
|
|
|
+
|
|
|
+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 bgImg = reactive({
|
|
|
+ list: [],
|
|
|
+});
|
|
|
+
|
|
|
+let assignBgImg = ref("");
|
|
|
+
|
|
|
+function handleBgImg(item) {
|
|
|
+ console.log("name", item);
|
|
|
+ assignBgImg.value = item;
|
|
|
+ store.assignBgImg = item;
|
|
|
+
|
|
|
+ parameter.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);
|
|
|
+}
|
|
|
+
|
|
|
+// async function getBgImgNames() {
|
|
|
+// let url = `${apiUrl}/sd/bg_img_names`;
|
|
|
+
|
|
|
+// try {
|
|
|
+// let response = await axios.get(url);
|
|
|
+// console.log("getBgImgNames", response.data);
|
|
|
+// response.data.map((item) => bgImg.list.push(item));
|
|
|
+// console.log("bgImg.list", bgImg.list);
|
|
|
+// } catch (error) {
|
|
|
+// console.log("error", error);
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // getBgImgNames();
|
|
|
+ getParameters();
|
|
|
+});
|
|
|
+
|
|
|
+const currentPhotos = computed(() => {
|
|
|
+ const start = currentIndex.value;
|
|
|
+ const end = start + perPage.value;
|
|
|
+ return parameter.value.slice(start, end);
|
|
|
+ // return bgImg.list.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 parameters = reactive({
|
|
|
+ styel_name: "",
|
|
|
+ prompt: "",
|
|
|
+ negative_prompt: "",
|
|
|
+ bg_img: "",
|
|
|
+ styles: ["real"],
|
|
|
+});
|
|
|
+
|
|
|
+// 算圖測試欄位
|
|
|
+let runParameters = reactive({
|
|
|
+ seed: "54987890",
|
|
|
+ denoising_strength: "0.35",
|
|
|
+ batch_size: "1",
|
|
|
+ n_iter: "30",
|
|
|
+});
|
|
|
+
|
|
|
+let fileInput = ref(null);
|
|
|
+let imgFile = ref(null);
|
|
|
+
|
|
|
+function onFileChange() {
|
|
|
+ console.log("fileInput", fileInput.value);
|
|
|
+ if (fileInput.value.files.length) {
|
|
|
+ imgFile.value = fileInput.value.files[0];
|
|
|
+ }
|
|
|
+ console.log("imgFile.value", imgFile.value);
|
|
|
+}
|
|
|
+
|
|
|
+let parameter = ref([]);
|
|
|
+
|
|
|
+async function getParameters() {
|
|
|
+ let url = `${apiUrl}/sd/parameters`;
|
|
|
+
|
|
|
+ try {
|
|
|
+ let response = await axios.get(url);
|
|
|
+ console.log("getParameters", response);
|
|
|
+ console.log("response", response);
|
|
|
+ parameter.value = response.data;
|
|
|
+ console.log("parameter.list", parameter.value);
|
|
|
+ } catch (error) {
|
|
|
+ console.log("error", error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function setParameters() {
|
|
|
+ let url = `${apiUrl}/sd/paprameter`;
|
|
|
+ let getUrl = `${apiUrl}/sd/parameters`;
|
|
|
+
|
|
|
+ if (assignBgImg.value === "") {
|
|
|
+ alert("尚未選取背景圖");
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ parameters.bg_img = assignBgImg.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!imgFile.value) {
|
|
|
+ alert("尚未上傳人物圖");
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log("parameters", parameters);
|
|
|
+
|
|
|
+ try {
|
|
|
+ let response = await axios.post(url, parameters);
|
|
|
+ console.log("setParameters", response);
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ let getResponse = await axios.get(getUrl);
|
|
|
+ console.log("getResponse", getResponse);
|
|
|
+
|
|
|
+ // 算圖
|
|
|
+ runImg(getResponse.data.length);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log("error", error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let imgLoading = ref(false);
|
|
|
+let imgPath = ref("");
|
|
|
+
|
|
|
+async function runImg(styleNum) {
|
|
|
+ imgPath.value = "";
|
|
|
+ imgLoading.value = true;
|
|
|
+ console.log("styleNum", 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=${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) {
|
|
|
+ imgPath.value = response.data[0].path;
|
|
|
+ imgLoading.value = false;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log("error", error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let alertShow = ref(false);
|
|
|
+
|
|
|
+function checkImg() {
|
|
|
+ if (store.assignBgImg && store.assignBgImg !== "") {
|
|
|
+ alertShow.value = false;
|
|
|
+ router.push("/step4");
|
|
|
+ } else {
|
|
|
+ alertShow.value = true;
|
|
|
+ setTimeout(() => {
|
|
|
+ alertShow.value = false;
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<div class="content">
|
|
|
- <p>
|
|
|
- AI 明信片可以分辨來自不同國籍的旅客 <br />
|
|
|
- 只需要使用手機自拍,加上 AI 技術 <br />
|
|
|
- 將您完美合成至台灣各景點
|
|
|
- </p>
|
|
|
- <p>
|
|
|
- 如果您此趟旅程沒辦法每個地方都去過 <br />
|
|
|
- 那這是我們送給您的禮物
|
|
|
- </p>
|
|
|
- <router-link to="/step2" class="main-btn">下一步</router-link>
|
|
|
- <div class="hashtag">
|
|
|
- <span># 認識台灣</span>
|
|
|
- <span># 馬上取得照片</span>
|
|
|
+ <p class="title">選擇背景</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"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ class="cover"
|
|
|
+ :src="`http://192.168.192.38:8000/static/assets/img/bg/${item.bg_img}`"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <!-- {{ bg_img }} -->
|
|
|
+ <p>{{ item.bg_img.replace(".png", "") }}</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>
|
|
|
+
|
|
|
+ <!-- <div class="upload-input">
|
|
|
+ <v-file-input
|
|
|
+ label="File input"
|
|
|
+ prepend-icon="mdi-camera"
|
|
|
+ variant="filled"
|
|
|
+ ></v-file-input>
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+ <!-- 測試 -->
|
|
|
+ <!-- <div class="test-box">
|
|
|
+ <form>
|
|
|
+ <label for="styel_name">styel_name</label>
|
|
|
+ <input v-model="parameters.styel_name" id="styel_name" type="text" />
|
|
|
+
|
|
|
+ <label for="prompt">prompt</label>
|
|
|
+ <input v-model="parameters.prompt" id="prompt" type="text" />
|
|
|
+
|
|
|
+ <label for="negative_prompt">negative_prompt</label>
|
|
|
+ <input
|
|
|
+ v-model="parameters.negative_prompt"
|
|
|
+ id="negative_prompt"
|
|
|
+ type="text"
|
|
|
+ />
|
|
|
+
|
|
|
+ <label for="seed">seed</label>
|
|
|
+ <input
|
|
|
+ v-model="runParameters.seed"
|
|
|
+ id="seed"
|
|
|
+ type="text"
|
|
|
+ />
|
|
|
+
|
|
|
+ <label for="denoising_strength">denoising_strength</label>
|
|
|
+ <input
|
|
|
+ v-model="runParameters.denoising_strength"
|
|
|
+ id="denoising_strength"
|
|
|
+ type="text"
|
|
|
+ />
|
|
|
+
|
|
|
+ <label for="batch_size">batch_size</label>
|
|
|
+ <input
|
|
|
+ v-model="runParameters.batch_size"
|
|
|
+ id="batch_size"
|
|
|
+ type="text"
|
|
|
+ />
|
|
|
+
|
|
|
+ <label for="n_iter">n_iter</label>
|
|
|
+ <input
|
|
|
+ v-model="runParameters.n_iter"
|
|
|
+ id="n_iter"
|
|
|
+ type="text"
|
|
|
+ />
|
|
|
+
|
|
|
+ <label for="file">上傳人物照片</label>
|
|
|
+ <input
|
|
|
+ ref="fileInput"
|
|
|
+ v-on:change="onFileChange()"
|
|
|
+ type="file"
|
|
|
+ id="file"
|
|
|
+ />
|
|
|
+ </form>
|
|
|
+
|
|
|
+ <button @click="setParameters()" class="main-btn">
|
|
|
+ <img
|
|
|
+ v-if="imgLoading"
|
|
|
+ class="spinner"
|
|
|
+ src="../assets/img/Spinner-1s-200px.svg"
|
|
|
+ alt=""
|
|
|
+ />
|
|
|
+ <span v-else>測試算圖</span>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <div v-if="imgPath !== ''">
|
|
|
+ <a :href="imgPath" target="_blank">查看算圖結果</a>
|
|
|
+ </div>
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+ <a @click="checkImg()" href="javascript:;" class="main-btn">下一步</a>
|
|
|
+
|
|
|
+ <!-- <router-link to="/step4" class="main-btn">下一步</router-link> -->
|
|
|
+
|
|
|
+ <div v-if="alertShow" class="alert-item">
|
|
|
+ <v-alert border="top" type="warning" variant="outlined" class="mt-5">
|
|
|
+ 尚未選擇背景
|
|
|
+ </v-alert>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <Footer url="/step2" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-p {
|
|
|
- font-size: 1.5rem;
|
|
|
+// .test-box {
|
|
|
+// form {
|
|
|
+// display: flex;
|
|
|
+// flex-direction: column;
|
|
|
+// margin-bottom: 40px;
|
|
|
+// }
|
|
|
+// label {
|
|
|
+// color: white;
|
|
|
+// margin-bottom: 5px;
|
|
|
+// letter-spacing: 1px;
|
|
|
+// }
|
|
|
|
|
|
- @media (max-width: 600px) {
|
|
|
- font-size: 1rem;
|
|
|
- }
|
|
|
+// input {
|
|
|
+// padding: 10px;
|
|
|
+// margin-bottom: 20px;
|
|
|
+// }
|
|
|
+
|
|
|
+// .spinner {
|
|
|
+// width: 70px;
|
|
|
+// margin-bottom: -10px;
|
|
|
+// }
|
|
|
+
|
|
|
+// a,
|
|
|
+// #file {
|
|
|
+// color: white;
|
|
|
+// }
|
|
|
+
|
|
|
+// a {
|
|
|
+// text-align: center;
|
|
|
+// display: block;
|
|
|
+// margin-top: 10px;
|
|
|
+// }
|
|
|
+
|
|
|
+// .main-btn {
|
|
|
+// margin: auto;
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// .upload-input {
|
|
|
+// width: 30rem;
|
|
|
+// padding: 2rem;
|
|
|
+// background: #fff;
|
|
|
+// border-radius: 5px;
|
|
|
+// }
|
|
|
+
|
|
|
+img {
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
-span {
|
|
|
- font-size: 1.25rem;
|
|
|
+// .title {
|
|
|
+// padding-top: 4rem;
|
|
|
+// margin-bottom: 2rem;
|
|
|
+// font-size: 1.625rem;
|
|
|
+
|
|
|
+// @media (max-width: 600px) {
|
|
|
+// padding-top: 2rem;
|
|
|
+// font-size: 1.25rem;
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+.img-content {
|
|
|
+ // height: 80vh;
|
|
|
+ padding: 0 2rem;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
|
|
|
@media (max-width: 600px) {
|
|
|
- font-size: 1rem;
|
|
|
+ height: 100%;
|
|
|
+ padding: 0 4rem;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-.content {
|
|
|
- height: 100vh;
|
|
|
+ .bg-img {
|
|
|
+ margin-bottom: 2rem;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
|
|
|
- p {
|
|
|
- &:first-child {
|
|
|
- margin-bottom: 1.625rem;
|
|
|
+ .cover {
|
|
|
+ max-width: 100%;
|
|
|
+ width: 30rem;
|
|
|
+ height: 25vh;
|
|
|
+ object-fit: cover;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .main-btn {
|
|
|
- margin: 3rem auto;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .hashtag {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
+ .prev,
|
|
|
+ .next {
|
|
|
+ position: absolute;
|
|
|
+ cursor: pointer;
|
|
|
+ border: none;
|
|
|
+ background-color: transparent;
|
|
|
|
|
|
- span {
|
|
|
- color: white;
|
|
|
- &:last-child {
|
|
|
- margin-left: 20px;
|
|
|
+ &: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>
|