Compare commits
82 Commits
321c0bdb92
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| df5cfc5eba | |||
| 8100459c4e | |||
| 10ee247b8d | |||
| 7f34ce80b9 | |||
| 57359d1067 | |||
| 9101116430 | |||
|
|
7a87c6cd11 | ||
|
|
c8dc6cf8d1 | ||
| f092a76162 | |||
| 48a32a60a1 | |||
|
|
f157c6ead3 | ||
|
|
592792d071 | ||
|
|
aace73d5c4 | ||
|
|
02dcfba4ba | ||
|
|
465fa5e6ae | ||
|
|
0ec9e4dc46 | ||
|
|
e230b4c83f | ||
|
|
cf15e371ab | ||
|
|
ef314931eb | ||
|
|
71a3946aae | ||
|
|
38f027d3ad | ||
|
|
d497471e09 | ||
| 92c571b5f9 | |||
| 6156c88429 | |||
|
|
319130dc6a | ||
|
|
6a2b456bf3 | ||
|
|
135adb3907 | ||
| 089a266c35 | |||
| a9732a659e | |||
|
|
24e221902e | ||
|
|
8b92d360c9 | ||
|
|
f10039e9dc | ||
| 9d0b29ab95 | |||
| 433dec935a | |||
| dfdfce2076 | |||
|
|
eebe7b5b40 | ||
|
|
fc1c212eca | ||
|
|
9f338a772d | ||
| 8e50f3d8a5 | |||
| f5bfad7973 | |||
| 27bb0394f8 | |||
|
|
73ae6744ab | ||
|
|
2b42ef6ab7 | ||
|
|
0794749f9a | ||
|
|
5f84eb496c | ||
| 10fc7f3e30 | |||
| 6b19672f1c | |||
| 32245b8931 | |||
| 25852e7c5a | |||
|
|
5d89d06531 | ||
|
|
279776c4f7 | ||
|
|
94cb354483 | ||
|
|
88abe91538 | ||
|
|
55ed8474be | ||
|
|
fab733c7c7 | ||
|
|
3826e553b1 | ||
|
|
8cb77593f8 | ||
|
|
891fb3f4b0 | ||
|
|
c2b800eb35 | ||
|
|
15a4220fbc | ||
| 9da2cd9dc2 | |||
| 8af7092f56 | |||
| f0ff9ebcbe | |||
| 9c41bf4711 | |||
|
|
59c864fef3 | ||
|
|
28f67cf228 | ||
|
|
1be879fbe4 | ||
| 8c2f4ca6e6 | |||
| 53b9a83b80 | |||
|
|
9dd3bed850 | ||
| a5cf919ded | |||
| 42a7194156 | |||
|
|
b206cae025 | ||
|
|
1a52eaa3c3 | ||
|
|
70cf624d88 | ||
| a629a08e66 | |||
| 73e8662895 | |||
|
|
9a51259b11 | ||
|
|
e6d542fb78 | ||
| 33659049ce | |||
| 9310761a5d | |||
|
|
6b255728af |
@@ -59,3 +59,19 @@ export const googleAuth = (data: GoogleAuthParamsType): Promise<LoginResponse> =
|
|||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 更改用户信息
|
||||||
|
* @param data 包含用户信息的对象
|
||||||
|
* @param data.username 用户名
|
||||||
|
* @param data.email 邮箱
|
||||||
|
* @param data.password 密码
|
||||||
|
* @returns 包含更新后的用户信息的对象
|
||||||
|
*/
|
||||||
|
export const updateUserInfo = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/api/auth/updateUserInfo',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
button.sandblasted-blurred {
|
button.sandblasted-blurred {
|
||||||
box-sizing: content-box;
|
width: 35rem;
|
||||||
border: 0.4rem solid #fff;
|
height: 8.3rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0.25rem solid #fff;
|
||||||
font-family: satoshiMedium;
|
font-family: satoshiMedium;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
button.sandblasted-blurred {
|
button.sandblasted-blurred {
|
||||||
box-sizing: content-box;
|
width: 35rem;
|
||||||
border: 0.4rem solid #fff;
|
height: 8.3rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0.25rem solid #fff;
|
||||||
font-family: satoshiMedium;
|
font-family: satoshiMedium;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
@@ -41,7 +43,9 @@ button.sandblasted-blurred {
|
|||||||
|
|
||||||
//只使用浅色模式
|
//只使用浅色模式
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root, body {
|
|
||||||
|
:root,
|
||||||
|
body {
|
||||||
background: white !important;
|
background: white !important;
|
||||||
color: black !important;
|
color: black !important;
|
||||||
}
|
}
|
||||||
@@ -71,10 +75,11 @@ html:root {
|
|||||||
--van-toast-default-width: 88rem;
|
--van-toast-default-width: 88rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.van-toast{
|
.van-toast {
|
||||||
min-height: fit-content;
|
min-height: fit-content;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.van-toast__text {
|
.van-toast__text {
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
height: 5rem;
|
height: 5rem;
|
||||||
@@ -84,19 +89,21 @@ html:root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.general,
|
button.general,
|
||||||
.general_button{
|
.general_button {
|
||||||
border-radius: .7rem;
|
border-radius: .7rem;
|
||||||
border: 3px solid #000;
|
border: 3px solid #000;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: satoshiMedium;
|
font-family: satoshiMedium;
|
||||||
&.smail{
|
|
||||||
|
&.smail {
|
||||||
font-size: 3.6rem;
|
font-size: 3.6rem;
|
||||||
width: 24.6rem;
|
width: 24.6rem;
|
||||||
line-height: 6.7rem;
|
line-height: 6.7rem;
|
||||||
}
|
}
|
||||||
&.big{
|
|
||||||
|
&.big {
|
||||||
font-size: 3.8rem;
|
font-size: 3.8rem;
|
||||||
line-height: 7.4rem;
|
line-height: 7.4rem;
|
||||||
width: 3.4rem;
|
width: 3.4rem;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<svg width="34" height="33" viewBox="0 0 34 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M1.96387 16.8115C1.96387 19.7782 2.8436 22.6783 4.49182 25.1451C6.14004 27.6118 8.48272 29.5344 11.2236 30.6697C13.9645 31.805 16.9805 32.1021 19.8902 31.5233C22.7999 30.9445 25.4727 29.5159 27.5705 27.4181C29.6682 25.3203 31.0969 22.6476 31.6756 19.7379C32.2544 16.8282 31.9574 13.8122 30.8221 11.0713C29.6867 8.33038 27.7641 5.9877 25.2974 4.33948C22.8307 2.69126 19.9306 1.81152 16.9639 1.81152C12.7705 1.8273 8.74548 3.46356 5.73053 6.37819L1.96387 10.1449" stroke="black" stroke-width="2.28788" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M1 21C1 24.9556 2.17298 28.8224 4.37061 32.1114C6.56823 35.4004 9.69181 37.9638 13.3463 39.4776C17.0008 40.9913 21.0222 41.3874 24.9018 40.6157C28.7814 39.844 32.3451 37.9392 35.1421 35.1421C37.9392 32.3451 39.844 28.7814 40.6157 24.9018C41.3874 21.0222 40.9913 17.0008 39.4776 13.3463C37.9638 9.69181 35.4004 6.56823 32.1114 4.37061C28.8224 2.17298 24.9556 1 21 1C15.4088 1.02103 10.0422 3.20272 6.02222 7.08889L1 12.1111" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M1.96387 1.81152V10.1449H10.2972M16.9639 8.47819V16.8115L23.6305 20.1448" stroke="black" stroke-width="2.28788" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M1 1V12.1111H12.1111M21 9.88889V21L29.8889 25.4444" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 759 B |
11
src/assets/icons/profileFilledBlack.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
16
src/assets/icons/profileFilledWhite.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -1,9 +1,7 @@
|
|||||||
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<rect width="44" height="44" fill="url(#pattern0_27_31213)"/>
|
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 860.38">
|
||||||
<defs>
|
<path d="M0,778.02v-31c5.85-.83,5.72-6.77,7.59-9.91,8.69-14.56,35.82-36.92,48.91-50.09,132.41-133.33,265.22-266.72,399-399,24.48-24.21,50.22-64.36,90.41-43.4,11.6,6.05,52.53,47.35,62.07,58.93,36.58,44.38-10.81,75.6-38.98,103.98-134.47,135.49-269.91,271.36-406,406-16.26,16.09-36.44,43.61-60.47,46.53-29.22,3.55-48.2-22.96-66.04-41.03-9.24-9.36-21.12-18.28-28.34-29.66-2.17-3.42-2.85-10.44-8.15-11.35ZM411.35,365.37c-2.17,2.21-15.35,15.62-15.83,16.67-.89,1.93-.9,4.04,0,5.97,13.11,12.89,26.45,25.54,39.5,38.49,9.43,9.36,18.54,18.95,28.02,27.98,3.81,3.63,7.06,10.05,13.42,10.57l111.05-110.01c8.1-9.65,12.08-18.21,5.33-30.36-3.97-7.14-45.89-48.98-53.82-55.18-15.22-11.91-27.19-3.82-38.92,7.13-29.56,27.59-60.23,59.7-88.75,88.75ZM45.35,731.37c-11.89,12.23-27.69,25.33-17.21,44.01,4.11,7.33,43.34,46.29,51.36,52.64,12.24,9.71,22.88,9.89,35.03.02l338.5-338.5c2.02-1.93,3.87-4.47,2.24-7.24l-76.71-77.33c-6.81-3.73-7.97,2-11.53,5.57-106.81,107.19-216.25,212.35-321.68,320.82Z"/>
|
||||||
<pattern id="pattern0_27_31213" patternContentUnits="objectBoundingBox" width="1" height="1">
|
<path d="M880,334.02v17c-5.37-.47-7.34,5.21-11.28,7.22-8.66,4.42-23.83,6.25-34.09,10.91-26.75,12.14-29.6,29.14-37.9,54.1-3.19,9.58-7.95,22.23-20.19,21.83-17.04-.55-18.67-24.85-23.04-37.04-6.38-17.79-17.31-31.49-34.79-39.21-12.9-5.7-50.73-9.57-44.24-30.86,4.1-13.47,23.9-15.02,35.08-18.92,23.2-8.09,37.12-22.61,45.17-45.83,3.07-8.85,4.36-25.17,12.91-31.09,13.68-9.47,24.05,3.61,28.11,15.65,5.67,16.79,7.2,30.82,20.37,44.63,16.19,16.99,35.71,15.07,53.62,24.38,3.37,1.75,6.17,6.34,10.28,7.22ZM776.74,289.16c-4.56.64-5.93,9.79-7.87,13.23-4.92,8.72-16.76,21.22-24.93,27.07-3.39,2.43-27.4,12.25-17.91,17.04,1.57.79,3.84.48,5.69,1.3,14.82,6.58,27.91,19.11,36.1,32.9,2.41,4.06,4.1,13.24,9.67,13.25,5.06.01,6.38-9.04,8.68-13.25,7.33-13.41,22.11-26.66,36.1-32.9,2.84-1.26,8.25-.22,7.76-5.23-.43-4.4-10.52-7.04-13.94-9.15-10.29-6.37-25.31-20.31-30.97-31.03-1.68-3.19-3.2-13.95-8.4-13.22Z"/>
|
||||||
<use xlink:href="#image0_27_31213" transform="scale(0.0078125)"/>
|
<path d="M372.75.26c17.02-2.7,21.7,16.54,25.74,28.78.66,2,.35,4.18.97,6.03,13.37,39.65,24.62,56.65,65.57,70.43,12.7,4.27,34.46,6.35,40.83,19.17,3.04,6.11.84,15.15-3.82,19.9-7.28,7.4-39.68,13.7-51.41,18.59-18.8,7.84-36.26,25.05-44.04,43.96-4.84,11.74-11.29,45.73-19.62,52.38-9.65,7.7-21.8,4.05-27.82-6.11-7.31-12.34-9.82-36.44-16.95-51.05-15.65-32.09-39.88-37.6-70.19-47.81-6.54-2.2-20.02-5.97-23.84-11.16-7.42-10.08-4.71-18.67,3.87-26.8,29-11.22,57.77-13.95,78.99-39.01,16.65-19.67,17.52-40.92,26.48-63.52,2.43-6.14,8.52-12.7,15.25-13.77ZM373.63,45.09c-3.49,2.24-2.14,6.63-3.08,9.98-8.8,31.51-36.03,59.55-66.76,70.24-3.39,1.18-14.11,1.7-14.73,5.33-.78,4.51,19.66,9.4,23.63,11.19,23.74,10.71,47.33,35.36,55.8,60.2,1.49,4.36,1.94,11.23,3.41,14.59.55,1.24.82,2.54,2.58,2.47,1.5-.08,2.17-1.03,2.94-2.13,3.14-4.52,5.53-17.08,8.16-22.84,9.96-21.83,30.88-42.46,52.72-52.28,5.99-2.69,14.22-4.36,20.1-6.9,13.11-5.67-3.32-7.16-6.66-8.15-28.02-8.37-54.24-31.06-66.23-57.77-1.97-4.39-6.53-21.22-8.95-23.05-.95-.72-1.67-1.23-2.94-.86Z"/>
|
||||||
</pattern>
|
<path d="M636.71,457.23c20.94-4.69,20.95,15.78,25.59,28.99,5.11,14.55,14.97,26.74,29.53,32.47,9.09,3.58,25.95,5.73,31.6,13.4,9.34,12.66-1.53,22.37-13.25,26.61-18.27,6.61-33.98,7.98-43.9,28.1-6.28,12.74-8.15,46.13-30.23,38.68-10.99-3.71-14.09-28.07-18.87-38.13-5.97-12.57-17.69-20.98-30.64-25.36-3.51-1.19-7.43-1.16-10.72-2.28-6.67-2.26-17.88-7.24-18.84-15.15-2.53-20.97,22.31-20.38,35.96-26.09,8.39-3.51,21.6-16.31,25.26-24.74,4.28-9.87,6.48-33.79,18.51-36.49ZM676.76,545.79c5.98-5.71-8.51-11.82-11.7-14.33-6.14-4.82-12.27-10.92-16.47-17.53-1.39-2.19-3.02-9.96-6.08-9.97-2.5,0-11.59,15.35-14.51,18.56-5.69,6.22-13.07,9.73-19.02,14.98-8.94,7.88,1.05,8.3,5.39,10.67,4.81,2.63,14.75,12.4,18.08,16.92,2.63,3.58,6.01,13.02,10.06,13.03s3.7-4.8,5.7-8.4c2.93-5.27,16.66-19.78,21.87-22.13,1.99-.9,5.35-.52,6.69-1.81Z"/>
|
||||||
<image id="image0_27_31213" width="128" height="128" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.8 KiB |
3
src/assets/icons/send_bold.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M30.5 16C30.5 16.5682 30.1791 17.0877 29.6709 17.3418L3.6709 30.3418C3.12081 30.6168 2.45842 30.5281 2 30.1182C1.54166 29.7082 1.37965 29.0596 1.5918 28.4824L6.18457 16L1.5918 3.51758C1.37965 2.94039 1.54166 2.29185 2 1.88183C2.45842 1.47186 3.12081 1.38316 3.6709 1.6582L29.6709 14.6582L29.8525 14.7656C30.2544 15.043 30.5 15.5029 30.5 16Z" stroke="#6D6868" stroke-width="3" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 514 B |
9
src/assets/icons/share.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<rect width="25.338" height="25.338" fill="url(#pattern0_27_28523)"/>
|
||||||
|
<defs>
|
||||||
|
<pattern id="pattern0_27_28523" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||||
|
<use xlink:href="#image0_27_28523" transform="scale(0.0078125)"/>
|
||||||
|
</pattern>
|
||||||
|
<image id="image0_27_28523" width="128" height="128" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/assets/images/Crystal.jpg
Normal file
|
After Width: | Height: | Size: 667 KiB |
BIN
src/assets/images/Crystal_thumb.jpg
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
src/assets/images/dressfor.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
src/assets/images/female_thumb.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
src/assets/images/male_thumb.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
src/assets/images/mini.jpg
Normal file
|
After Width: | Height: | Size: 655 KiB |
BIN
src/assets/images/mini_thumb.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
@@ -1,13 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
title: { type: String, default: 'AI STYLING ASSISTANT' }
|
title: { type: String, default: 'STYLING ASSISTANT' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['clickReturn', 'clickProfile'])
|
const emit = defineEmits(['clickReturn', 'clickProfile'])
|
||||||
|
|
||||||
|
const profileVisible = ref(false)
|
||||||
|
const handleProfileVisibleChange = (visible) => {
|
||||||
|
profileVisible.value = visible
|
||||||
|
}
|
||||||
|
MyEvent.add('change-profile-visible', handleProfileVisibleChange)
|
||||||
|
|
||||||
const handleClickReturn = () => {
|
const handleClickReturn = () => {
|
||||||
router.back()
|
router.back()
|
||||||
// emit('clickReturn')
|
// emit('clickReturn')
|
||||||
@@ -22,7 +31,9 @@
|
|||||||
<div class="header-title">
|
<div class="header-title">
|
||||||
<div class="return" @click="handleClickReturn"><SvgIcon name="return" size="34" /></div>
|
<div class="return" @click="handleClickReturn"><SvgIcon name="return" size="34" /></div>
|
||||||
<span class="title">{{ title }}</span>
|
<span class="title">{{ title }}</span>
|
||||||
<div class="profile" @click="handleClickProfile"><SvgIcon name="profile" size="45" /></div>
|
<div class="profile" @click="handleClickProfile">
|
||||||
|
<SvgIcon :name="profileVisible ? 'profileFilledBlack' : 'profile_white'" size="45" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -49,6 +60,7 @@
|
|||||||
color: #2c2c2c;
|
color: #2c2c2c;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
|
letter-spacing: 0.3rem;
|
||||||
}
|
}
|
||||||
> .profile {
|
> .profile {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
loading: { default: false, type: Boolean },
|
loading: { default: false, type: Boolean },
|
||||||
finish: { default: false, type: Boolean },
|
finish: { default: false, type: Boolean },
|
||||||
|
empty: { default: false, type: Boolean },
|
||||||
pel: { default: () => {}, type: Function }
|
pel: { default: () => {}, type: Function }
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['load'])
|
const emit = defineEmits(['load'])
|
||||||
@@ -23,12 +24,13 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="my-list" ref="el">
|
<div class="my-list" ref="el" :class="{ empty: !loading && empty }">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p v-show="!loading" class="placeholder" ref="placeholder"></p>
|
<p v-show="!loading" class="placeholder" ref="placeholder"></p>
|
||||||
<span class="loading" v-show="loading">Loading...</span>
|
<span class="loading" v-if="loading">Loading...</span>
|
||||||
<span class="nomore" v-show="finish">No more</span>
|
<span class="empty" v-else-if="empty">Nothing Here</span>
|
||||||
|
<span class="nomore" v-else-if="finish">No more</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,12 +42,28 @@
|
|||||||
> .footer {
|
> .footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
color: #000;
|
color: #a1a1a1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: var(--my-list-footer-margin, 0);
|
margin: var(--my-list-footer-margin, 0);
|
||||||
> .placeholder {
|
> .placeholder {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
> .empty {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.empty {
|
||||||
|
> .footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
> .empty {
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="routeCache" :view-type="viewType">
|
<div class="routeCache" :view-type="viewType">
|
||||||
<router-view v-slot="{ Component, route }" @view-type="changeViewType">
|
<router-view v-slot="{ Component, route }">
|
||||||
<keep-alive :include="cachedViews">
|
<keep-alive :include="cachedViews">
|
||||||
<component :is="Component" :key="route.name" />
|
<component :is="Component" :key="route.name" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
@@ -14,6 +14,13 @@ import { useRoute } from 'vue-router'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
viewType: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
// 缓存的组件名称列表
|
// 缓存的组件名称列表
|
||||||
@@ -59,17 +66,6 @@ onMounted(() => {
|
|||||||
MyEvent.add('clearAllCache', clearCache)
|
MyEvent.add('clearAllCache', clearCache)
|
||||||
})
|
})
|
||||||
|
|
||||||
//根据viewType设置布局风格样式
|
|
||||||
const viewType = ref(0)
|
|
||||||
const changeViewType = (v: number) => {
|
|
||||||
viewType.value = v
|
|
||||||
}
|
|
||||||
const router = useRouter()
|
|
||||||
watch(
|
|
||||||
() => router.currentRoute.value,
|
|
||||||
() => (viewType.value = 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
// 暴露方法供外部使用
|
// 暴露方法供外部使用
|
||||||
defineExpose({
|
defineExpose({
|
||||||
clearCache,
|
clearCache,
|
||||||
|
|||||||
78
src/components/WaveLoading.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div class="loading-container" :style="containerStyle">
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { defineProps } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#000' // 默认颜色
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 10 // 默认圆点大小(px)
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
type: Number,
|
||||||
|
default: 3 // 默认间距(px),调整为合适小尺寸
|
||||||
|
},
|
||||||
|
amplitude: {
|
||||||
|
type: Number,
|
||||||
|
default: 8 // 默认浮动幅度(px),调整为合适比例
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
default: 1.2 // 默认动画持续时间(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const containerStyle = computed(() => ({
|
||||||
|
'--color': props.color,
|
||||||
|
'--size': `${props.size}px`,
|
||||||
|
'--spacing': `${props.spacing}px`,
|
||||||
|
'--amplitude': `${props.amplitude}px`,
|
||||||
|
'--duration': `${props.duration}s`
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
/* 可以根据需要调整容器高度或移除 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
background-color: var(--color);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 var(--spacing);
|
||||||
|
animation: wave var(--duration) infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot:nth-child(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wave {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(calc(-1 * var(--amplitude)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -48,11 +48,11 @@ const {} = toRefs(data);
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
border-radius: var(--gradientButtonBorderRadius);
|
border-radius: var(--gradientButtonBorderRadius);
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
background: linear-gradient(125deg,
|
background: linear-gradient(156deg,
|
||||||
#f1f1f1 0%,
|
#d3d3d3 0%,
|
||||||
#000 40%,
|
#8a8682 40%,
|
||||||
#000 65%,
|
#8a8682 65%,
|
||||||
#fff 100%);
|
#ebebeb 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -82,22 +82,11 @@ const {} = toRefs(data);
|
|||||||
</div>
|
</div>
|
||||||
<div class="mask" v-if="item.id == select?.oldId"></div>
|
<div class="mask" v-if="item.id == select?.oldId"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn">
|
<!-- <div class="btn">
|
||||||
<!-- <div>
|
|
||||||
<SvgIcon v-if="!item.isLike" @click.stop="setLike(item,'like')" name="noLike" size="30" />
|
|
||||||
<SvgIcon v-else name="like" @click.stop="setLike(item,'noLike')" color="#FF4949" size="30" />
|
|
||||||
</div> -->
|
|
||||||
<div>
|
<div>
|
||||||
<SvgIcon @click.stop="updateStyle(item,index)" name="update" size="30" />
|
<SvgIcon @click.stop="updateStyle(item,index)" name="update" size="30" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div>
|
</div> -->
|
||||||
<SvgIcon v-if="!item.isAdd" @click.stop="addLibrary(item,'add')" name="add" size="30" />
|
|
||||||
<SvgIcon v-else @click.stop="addLibrary(item,'delete')" name="confirmation" size="30" />
|
|
||||||
</div> -->
|
|
||||||
<!-- <div>
|
|
||||||
<SvgIcon @click.stop="deleteStyle(index)" name="delete" size="30" />
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -109,7 +98,7 @@ const {} = toRefs(data);
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
> .item{
|
> .item{
|
||||||
width: calc(50% - 3.1rem / 2);
|
width: calc(50% - 3.5rem / 2);
|
||||||
position: relative;
|
position: relative;
|
||||||
// margin-bottom: 3.3rem;
|
// margin-bottom: 3.3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -126,7 +115,7 @@ const {} = toRefs(data);
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
height: 45rem;
|
height: 45rem;
|
||||||
margin: 2.4rem 0;
|
margin: 2.3rem 0;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: .6px solid #acacac;
|
border: .6px solid #acacac;
|
||||||
@@ -170,26 +159,26 @@ const {} = toRefs(data);
|
|||||||
// max-height: 50%;
|
// max-height: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .btn{
|
// > .btn{
|
||||||
display: flex;
|
// display: flex;
|
||||||
align-items: center;
|
// align-items: center;
|
||||||
justify-content: flex-end;
|
// justify-content: flex-end;
|
||||||
> div{
|
// > div{
|
||||||
color: #000;
|
// color: #000;
|
||||||
margin-right: 1.2rem;
|
// margin-right: 1.2rem;
|
||||||
border-radius: 50%;
|
// border-radius: 50%;
|
||||||
width: 5.2rem;
|
// width: 5.2rem;
|
||||||
height: 5.2rem;
|
// height: 5.2rem;
|
||||||
padding: 1rem;
|
// padding: 1rem;
|
||||||
background-color: #fff;
|
// background-color: #fff;
|
||||||
&:last-child{
|
// &:last-child{
|
||||||
margin-right: 0rem;
|
// margin-right: 0rem;
|
||||||
}
|
// }
|
||||||
&:hover{
|
// &:hover{
|
||||||
color: #000;
|
// color: #000;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
112
src/hooks/useStreamChat.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { showToast } from 'vant'
|
||||||
|
import { streamChatAddress } from '@/api/workshop'
|
||||||
|
import { useUserInfoStore } from '@/stores'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流式对话 Hook
|
||||||
|
* @param onSuccess - 成功时的回调(流式响应时调用)
|
||||||
|
* @returns { fetchMessage, isGenerating }
|
||||||
|
*/
|
||||||
|
export function useStreamChat(onSuccess?: () => void) {
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const isGenerating = ref(false)
|
||||||
|
|
||||||
|
const fetchMessage = (message: string, sessionId: string): Promise<void> => {
|
||||||
|
isGenerating.value = true
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
message,
|
||||||
|
sessionId,
|
||||||
|
gender: userInfoStore.state.generateParams.sex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接使用 fetch 进行流式请求
|
||||||
|
const token = userInfoStore.state.token
|
||||||
|
const baseURL = import.meta.env.MODE === 'development' ? '' : import.meta.env.VITE_APP_URL
|
||||||
|
|
||||||
|
// 构建查询参数
|
||||||
|
const queryParams = new URLSearchParams()
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
queryParams.append(key, String(value))
|
||||||
|
})
|
||||||
|
|
||||||
|
const url = `${baseURL}${streamChatAddress}?${queryParams.toString()}`
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: token,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
// 检查响应内容类型,判断是否为流式响应
|
||||||
|
const contentType = response.headers.get('content-type') || ''
|
||||||
|
const isStreamResponse =
|
||||||
|
contentType.includes('text/event-stream') || contentType.includes('stream')
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
// 非流式错误响应,使用 text() 读取错误信息
|
||||||
|
const errorText = await response.text()
|
||||||
|
console.error('请求错误:', errorText)
|
||||||
|
showToast({
|
||||||
|
message: `failed to fetch: ${response.status}`,
|
||||||
|
position: 'top',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
throw new Error(`发起对话错误--- ${response.status}: ${errorText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不是流式响应,使用 text()读取错误信息
|
||||||
|
if (!isStreamResponse) {
|
||||||
|
const text = await response.text()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const errorData = JSON.parse(text)
|
||||||
|
if (errorData.message || errorData.error) {
|
||||||
|
showToast({
|
||||||
|
message: errorData.message || errorData.error || 'network error',
|
||||||
|
position: 'top',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 如果不是 JSON,直接显示文本内容
|
||||||
|
showToast({
|
||||||
|
message: text || 'network error',
|
||||||
|
position: 'top',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
throw new Error(text || 'network error')
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流式响应处理
|
||||||
|
const reader = response.body?.getReader()
|
||||||
|
if (!reader) throw new Error('无法获取流读取器')
|
||||||
|
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
// 流式响应时调用成功回调
|
||||||
|
onSuccess?.()
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('fetch请求失败:', error)
|
||||||
|
showToast({
|
||||||
|
message: error.message || 'network error'
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
isGenerating.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetchMessage,
|
||||||
|
isGenerating
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { useGenerateStore } from '@/stores/modules/generate'
|
import { useGenerateStore } from '@/stores/modules/generate'
|
||||||
const VerifyIDs = (num: number) => {
|
const VerifyIDs = (num: number) => {
|
||||||
return true
|
return true
|
||||||
const ids = [
|
const ids = [
|
||||||
!!useGenerateStore().customerId,
|
!!useGenerateStore().customerId,
|
||||||
!!useGenerateStore().visitRecordId,
|
!!useGenerateStore().visitRecordId,
|
||||||
!!useGenerateStore().styleId,
|
!!useGenerateStore().styleId,
|
||||||
// !!useGenerateStore().modelPhotoId,
|
// !!useGenerateStore().modelPhotoId,
|
||||||
true,
|
true,
|
||||||
!!useGenerateStore().originalTryOnId,
|
!!useGenerateStore().originalTryOnId
|
||||||
];
|
]
|
||||||
return ids.splice(0, num).every(id => id) ? true : "/stylist/customer";
|
return ids.splice(0, num).every((id) => id) ? true : '/stylist/customer'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,8 +48,8 @@ const router = createRouter({
|
|||||||
component: () => import('@/views/login/LoginPage.vue')
|
component: () => import('@/views/login/LoginPage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path:'/reset',
|
path: '/reset',
|
||||||
name:'ResetPage',
|
name: 'ResetPage',
|
||||||
component: () => import('@/views/login/ResetPage.vue')
|
component: () => import('@/views/login/ResetPage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62,16 +62,16 @@ const router = createRouter({
|
|||||||
name: 'WelcomePage',
|
name: 'WelcomePage',
|
||||||
component: () => import('@/views/login/WelcomePage.vue')
|
component: () => import('@/views/login/WelcomePage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customer',
|
path: '/customer',
|
||||||
name: 'customer',
|
name: 'customer',
|
||||||
component: () => import('@/views/login/customer.vue'),
|
component: () => import('@/views/login/customer.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/asistant',
|
path: '/asistant',
|
||||||
name: 'asistant',
|
name: 'asistant',
|
||||||
component: () => import('../views/asistant/index.vue'),
|
component: () => import('../views/asistant/index.vue'),
|
||||||
meta: { cache: true, verify: ()=> VerifyIDs(2) }
|
meta: { cache: true, verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop',
|
path: '/workshop',
|
||||||
@@ -82,115 +82,114 @@ const router = createRouter({
|
|||||||
// path: '/workshop',
|
// path: '/workshop',
|
||||||
// redirect: '/workshop/selectStyle'
|
// redirect: '/workshop/selectStyle'
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
path: '/workshop/home',
|
path: '/workshop/home',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
component: () => import('@/views/Workshop/home.vue')
|
component: () => import('@/views/Workshop/home.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/homeNav',
|
path: '/workshop/homeNav',
|
||||||
name: 'HomeNav',
|
name: 'HomeNav',
|
||||||
component: () => import('@/views/Workshop/homeNav.vue')
|
component: () => import('@/views/Workshop/homeNav.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/stylist',
|
path: '/workshop/stylist',
|
||||||
name: 'StylistPage',
|
name: 'StylistPage',
|
||||||
redirect: '/workshop/stylist/index',
|
redirect: '/workshop/stylist/index',
|
||||||
component: () => import('@/views/stylist/container.vue'),
|
component: () => import('@/views/stylist/container.vue'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index',
|
path: 'index',
|
||||||
name: 'index',
|
name: 'index',
|
||||||
component: () => import('@/views/stylist/index.vue'),
|
component: () => import('@/views/stylist/index.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'sex',
|
path: 'sex',
|
||||||
name: 'sex',
|
name: 'sex',
|
||||||
component: () => import('@/views/stylist/sex.vue'),
|
component: () => import('@/views/stylist/sex.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'dressfor',
|
path: 'dressfor',
|
||||||
name: 'dressfor',
|
name: 'dressfor',
|
||||||
component: () => import('@/views/stylist/dressfor.vue'),
|
component: () => import('@/views/stylist/dressfor.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
}
|
||||||
|
]
|
||||||
]
|
},
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/workshop/selectStyle',
|
path: '/workshop/selectStyle',
|
||||||
name: 'selectStyle',
|
name: 'SelectStyle',
|
||||||
component: () => import('../views/Workshop/selectStyle.vue'),
|
component: () => import('../views/Workshop/selectStyle/index.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/selectModel',
|
path: '/workshop/selectModel',
|
||||||
name: 'SelectModel',
|
name: 'SelectModel',
|
||||||
component: () => import('../views/Workshop/selectModel.vue'),
|
component: () => import('../views/Workshop/selectModel.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(3) }
|
meta: { verify: () => VerifyIDs(3) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/product',
|
path: '/workshop/product',
|
||||||
name: 'product',
|
name: 'product',
|
||||||
component: () => import('../views/Workshop/product.vue'),
|
component: () => import('../views/Workshop/product.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(4) }
|
meta: { verify: () => VerifyIDs(4) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 推荐try on
|
// 推荐try on
|
||||||
path: '/workshop/recommended',
|
path: '/workshop/recommended',
|
||||||
name: 'recommended',
|
name: 'recommended',
|
||||||
component: () => import('../views/Workshop/recommended.vue'),
|
component: () => import('../views/Workshop/recommended.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(5) }
|
meta: { verify: () => VerifyIDs(5) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 上传照片1
|
// 上传照片1
|
||||||
path: '/workshop/uploadFace',
|
path: '/workshop/uploadFace',
|
||||||
name: 'uploadFace',
|
name: 'uploadFace',
|
||||||
component: () => import('../views/Workshop/uploadFace1.vue'),
|
component: () => import('../views/Workshop/uploadFace1.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(5) }
|
meta: { verify: () => VerifyIDs(5) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 上传照片2
|
// 上传照片2
|
||||||
path: '/workshop/uploadFace2',
|
path: '/workshop/uploadFace2',
|
||||||
name: 'uploadFace2',
|
name: 'uploadFace2',
|
||||||
component: () => import('../views/Workshop/uploadFace2.vue'),
|
component: () => import('../views/Workshop/uploadFace2.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(5) }
|
meta: { verify: () => VerifyIDs(5) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 自定义创作
|
// 自定义创作
|
||||||
path: '/workshop/customize',
|
path: '/workshop/customize',
|
||||||
name: 'customize',
|
name: 'customize',
|
||||||
component: () => import('../views/Workshop/customize.vue'),
|
component: () => import('../views/Workshop/customize.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(5) }
|
meta: { verify: () => VerifyIDs(5) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// library
|
// library
|
||||||
path: '/workshop/library',
|
path: '/workshop/library',
|
||||||
name: 'library',
|
name: 'library',
|
||||||
component: () => import('../views/Workshop/library.vue'),
|
component: () => import('../views/Workshop/library.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/profile',
|
path: '/workshop/profile',
|
||||||
name: 'profile',
|
name: 'profile',
|
||||||
component: () => import('../views/Workshop/profile.vue'),
|
component: () => import('../views/Workshop/profile.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(1) }
|
meta: { verify: () => VerifyIDs(1) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// creation
|
// creation
|
||||||
path: '/workshop/creation',
|
path: '/workshop/creation',
|
||||||
name: 'creation',
|
name: 'creation',
|
||||||
component: () => import('../views/Workshop/creation/index.vue'),
|
component: () => import('../views/Workshop/creation/index.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 完成创建
|
// 完成创建
|
||||||
path: '/workshop/end',
|
path: '/workshop/end',
|
||||||
name: 'end',
|
name: 'end',
|
||||||
component: () => import('../views/Workshop/end.vue'),
|
component: () => import('../views/Workshop/end.vue'),
|
||||||
meta: { verify: ()=> VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
MyEvent.add('clear-generate-state', () => useGenerateStore().clearGenerateData())
|
MyEvent.add('clear-generate-state', () => useGenerateStore().clearGenerateData())
|
||||||
|
MyEvent.add('clear-client-state', () => useGenerateStore().clearCustomerInfo())
|
||||||
|
|
||||||
export const useGenerateStore = defineStore({
|
export const useGenerateStore = defineStore({
|
||||||
id: 'generate', // 必须指明唯一的pinia仓库的id
|
id: 'generate', // 必须指明唯一的pinia仓库的id
|
||||||
@@ -14,7 +15,12 @@ export const useGenerateStore = defineStore({
|
|||||||
isLike: false, //是否喜欢
|
isLike: false, //是否喜欢
|
||||||
status: ''
|
status: ''
|
||||||
},
|
},
|
||||||
styleList: [{}, {}, {}, {}],
|
styleList: [
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
],
|
||||||
model: {
|
model: {
|
||||||
id: ''
|
id: ''
|
||||||
},
|
},
|
||||||
@@ -89,24 +95,17 @@ export const useGenerateStore = defineStore({
|
|||||||
...data,
|
...data,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearStyle() {
|
|
||||||
this.style = {
|
|
||||||
id: '',
|
|
||||||
path: '',
|
|
||||||
isLike: false,
|
|
||||||
taskId:'',
|
|
||||||
status: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//模特相关
|
//模特相关
|
||||||
selectModel(data: any) {
|
selectModel(data: any) {
|
||||||
this.model.id = data.id
|
this.model.id = data.id
|
||||||
},
|
},
|
||||||
setIsGenerate(isGenerate: boolean) {
|
|
||||||
this.isGenerate = isGenerate
|
|
||||||
},
|
|
||||||
clearProductData() {
|
clearProductData() {
|
||||||
this.styleList = [{}, {}, {}, {}]
|
this.styleList = [
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
{id:'',taskId:'',status:'',path:''},
|
||||||
|
]
|
||||||
this.style = {
|
this.style = {
|
||||||
id: '',
|
id: '',
|
||||||
path: '',
|
path: '',
|
||||||
@@ -117,12 +116,14 @@ export const useGenerateStore = defineStore({
|
|||||||
this.model = {
|
this.model = {
|
||||||
id: ''
|
id: ''
|
||||||
}
|
}
|
||||||
|
this.clearTryOn()
|
||||||
|
},
|
||||||
|
clearTryOn() {
|
||||||
this.originalTryOn = {
|
this.originalTryOn = {
|
||||||
id: '',
|
id: '',
|
||||||
isLike: false,
|
isLike: false,
|
||||||
tryOnUrl: ''
|
tryOnUrl: ''
|
||||||
}
|
}
|
||||||
this.isGenerate = false
|
|
||||||
},
|
},
|
||||||
/** 更新顾客照片信息 */
|
/** 更新顾客照片信息 */
|
||||||
updatePhotoInfo(data: any) {
|
updatePhotoInfo(data: any) {
|
||||||
@@ -176,7 +177,7 @@ export const useGenerateStore = defineStore({
|
|||||||
this.updatePhotoInfo({})
|
this.updatePhotoInfo({})
|
||||||
this.clearCustomizeInfo()
|
this.clearCustomizeInfo()
|
||||||
this.clearCustomizeInfoDemo()
|
this.clearCustomizeInfoDemo()
|
||||||
this.clearCustomerInfo()
|
// this.clearCustomerInfo()
|
||||||
this.setSessionId('')
|
this.setSessionId('')
|
||||||
},
|
},
|
||||||
setCustomerInfo(data: any) {
|
setCustomerInfo(data: any) {
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ export const useHGenerateStore = defineStore({
|
|||||||
this.customizeInfo.styleUrl = ''
|
this.customizeInfo.styleUrl = ''
|
||||||
this.customizeInfo.isRegenerated = ''
|
this.customizeInfo.isRegenerated = ''
|
||||||
this.customizeInfo.isFavorite = false
|
this.customizeInfo.isFavorite = false
|
||||||
|
},
|
||||||
|
/** 上传服装 */
|
||||||
|
uploadStyle(data: object) {
|
||||||
|
for (const key in data) {
|
||||||
|
this.style[key] = data[key]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
uploadCustomizeInfo(data: object) {
|
uploadCustomizeInfo(data: object) {
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
|||||||
token: '',
|
token: '',
|
||||||
generateParams: {
|
generateParams: {
|
||||||
stylist: '',
|
stylist: '',
|
||||||
sex: ''
|
sex: '',
|
||||||
|
stylistImage: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -37,7 +38,8 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
|||||||
const resetGenerateParams = () => {
|
const resetGenerateParams = () => {
|
||||||
state.value.generateParams = {
|
state.value.generateParams = {
|
||||||
stylist: '',
|
stylist: '',
|
||||||
sex: ''
|
sex: '',
|
||||||
|
stylistImage: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +51,7 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
|||||||
removeLocal('token')
|
removeLocal('token')
|
||||||
resetGenerateParams()
|
resetGenerateParams()
|
||||||
MyEvent.emit('clear-generate-state')
|
MyEvent.emit('clear-generate-state')
|
||||||
|
MyEvent.emit('clear-client-state')
|
||||||
MyEvent.emit('clearAllCache')
|
MyEvent.emit('clearAllCache')
|
||||||
resolve('')
|
resolve('')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ class MyEvent {
|
|||||||
MyEvent.list = MyEvent.list.filter(item => item.name != name && item.call != call)
|
MyEvent.list = MyEvent.list.filter(item => item.name != name && item.call != call)
|
||||||
}
|
}
|
||||||
emit(name, data) {
|
emit(name, data) {
|
||||||
console.log('Myevent触发--',name)
|
|
||||||
MyEvent.list.forEach(item => {
|
MyEvent.list.forEach(item => {
|
||||||
if (item.name == name) item.call(data)
|
if (item.name == name) item.call(data)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
import {
|
import {
|
||||||
getGenerateHistoricals,
|
getGenerateHistoricals,
|
||||||
setTryOnEffectFavorite,
|
setTryOnEffectFavorite,
|
||||||
cancelTryOnEffectFavorite
|
cancelTryOnEffectFavorite,
|
||||||
|
cancelStyleFavorite,
|
||||||
|
setStyleFavorite
|
||||||
} from '@/api/workshop'
|
} from '@/api/workshop'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
const query = computed(() => router.currentRoute.value.query)
|
const query = computed(() => router.currentRoute.value.query)
|
||||||
const visitRecordId = computed(() => query.value.visitRecordId) // 访问记录ID
|
const visitRecordId = computed(() => query.value.visitRecordId) // 访问记录ID
|
||||||
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
||||||
@@ -21,9 +23,6 @@
|
|||||||
// 是否单选模式
|
// 是否单选模式
|
||||||
isChooseOne: { type: Boolean, default: false }
|
isChooseOne: { type: Boolean, default: false }
|
||||||
})
|
})
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const list = reactive([])
|
const list = reactive([])
|
||||||
const size = ref(10)
|
const size = ref(10)
|
||||||
const page = computed(() => Math.ceil(list.length / size.value) + 1)
|
const page = computed(() => Math.ceil(list.length / size.value) + 1)
|
||||||
@@ -109,17 +108,22 @@
|
|||||||
}
|
}
|
||||||
// 详情页
|
// 详情页
|
||||||
const onDetailsItem = (v) => {
|
const onDetailsItem = (v) => {
|
||||||
if (v.isRegenerated) return
|
if (v.isRegenerated || !v.styleUrl) return
|
||||||
router.push({ query: { ...query.value, styleUrl: v.styleUrl } })
|
router.push({ query: { ...query.value, styleUrl: v.styleUrl } })
|
||||||
}
|
}
|
||||||
// 喜欢
|
// 喜欢
|
||||||
const isLoveLoading = ref(false)
|
const isLoveLoading = ref(false)
|
||||||
const onLoveItem = (v) => {
|
const onLoveItem = (v) => {
|
||||||
if (isLoveLoading.value) return
|
if (isLoveLoading.value) return
|
||||||
const http = v.isFavorite ? cancelTryOnEffectFavorite : setTryOnEffectFavorite
|
var http
|
||||||
|
if (navActive.value === 'Outfit') {
|
||||||
|
http = v.isFavorite ? cancelStyleFavorite : setStyleFavorite
|
||||||
|
} else {
|
||||||
|
http = v.isFavorite ? cancelTryOnEffectFavorite : setTryOnEffectFavorite
|
||||||
|
}
|
||||||
isLoveLoading.value = true
|
isLoveLoading.value = true
|
||||||
v.isFavorite = !v.isFavorite
|
v.isFavorite = !v.isFavorite
|
||||||
http(v.tryOnId)
|
http(v.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
isLoveLoading.value = false
|
isLoveLoading.value = false
|
||||||
})
|
})
|
||||||
@@ -157,7 +161,10 @@
|
|||||||
|
|
||||||
alert(`现在${isShare.value ? '可以' : '不可以'}分享`)
|
alert(`现在${isShare.value ? '可以' : '不可以'}分享`)
|
||||||
}
|
}
|
||||||
|
const onShareItem = (v) => {
|
||||||
|
const url = v.tryOnUrl || v.url
|
||||||
|
if (url) shareImageToWhatsapp(url)
|
||||||
|
}
|
||||||
const onDownloadItem = async (v) => {
|
const onDownloadItem = async (v) => {
|
||||||
if (isShare.value) {
|
if (isShare.value) {
|
||||||
await shareImageToWhatsapp(v.tryOnUrl)
|
await shareImageToWhatsapp(v.tryOnUrl)
|
||||||
@@ -243,6 +250,7 @@
|
|||||||
hGenerateStore.style.url = selectedItem.url
|
hGenerateStore.style.url = selectedItem.url
|
||||||
// selectedItem.isFavorite
|
// selectedItem.isFavorite
|
||||||
}
|
}
|
||||||
|
MyEvent.emit('clear-generate-state')
|
||||||
router.push({ name: 'HomeNav', query: { flowType: nav.flowType } })
|
router.push({ name: 'HomeNav', query: { flowType: nav.flowType } })
|
||||||
} else {
|
} else {
|
||||||
router.push({ name: 'end' })
|
router.push({ name: 'end' })
|
||||||
@@ -266,7 +274,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<my-list v-model:loading="loading" v-model:finish="finish" @load="onLoad">
|
<my-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
v-model:finish="finish"
|
||||||
|
@load="onLoad"
|
||||||
|
:empty="list.length === 0"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="item"
|
class="item"
|
||||||
v-for="(v, i) in list"
|
v-for="(v, i) in list"
|
||||||
@@ -284,9 +297,12 @@
|
|||||||
<div @click.stop="onLoveItem(v)">
|
<div @click.stop="onLoveItem(v)">
|
||||||
<SvgIcon :name="`love_${v.isFavorite ? '1' : '0'}`" size="27" />
|
<SvgIcon :name="`love_${v.isFavorite ? '1' : '0'}`" size="27" />
|
||||||
</div>
|
</div>
|
||||||
<div @click.stop="onDownloadItem(v)">
|
<!-- <div @click.stop="onDownloadItem(v)">
|
||||||
<SvgIcon name="download" size="27" v-show="!v.loading" />
|
<SvgIcon name="download" size="27" v-show="!v.loading" />
|
||||||
<van-loading color="#000" size="3rem" v-show="v.loading" />
|
<van-loading color="#000" size="3rem" v-show="v.loading" />
|
||||||
|
</div> -->
|
||||||
|
<div @click.stop="onShareItem(v)">
|
||||||
|
<SvgIcon name="share" size="27" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-selected" v-show="(isChooseSave || isChooseOne) && v.selected">
|
<div class="icon-selected" v-show="(isChooseSave || isChooseOne) && v.selected">
|
||||||
|
|||||||
@@ -7,16 +7,8 @@
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const styleUrl = computed(() => router.currentRoute.value.query.styleUrl)
|
const styleUrl = computed(() => router.currentRoute.value.query.styleUrl)
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
watch(
|
|
||||||
() => router.currentRoute.value,
|
|
||||||
() => emit('view-type', 1)
|
|
||||||
)
|
|
||||||
const isChooseOne = computed(() => route.query.flowType === FlowType.HISTORY)
|
const isChooseOne = computed(() => route.query.flowType === FlowType.HISTORY)
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
||||||
|
import gradientButton from '@/components/gradientButton.vue'
|
||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import {
|
import {
|
||||||
generateTryOnEffect,
|
generateTryOnEffect,
|
||||||
setTryOnEffectFavorite,
|
setTryOnEffectFavorite,
|
||||||
cancelTryOnEffectFavorite
|
cancelTryOnEffectFavorite
|
||||||
} from '@/api/workshop'
|
} from '@/api/workshop'
|
||||||
const emit = defineEmits(['viewType'])
|
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
||||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||||
@@ -115,12 +115,15 @@
|
|||||||
}
|
}
|
||||||
// 选择另一个穿搭
|
// 选择另一个穿搭
|
||||||
const onChooseOutfit = () => {
|
const onChooseOutfit = () => {
|
||||||
|
generateStore.clearTryOn()
|
||||||
router.push({ name: 'SelectStyle' })
|
router.push({ name: 'SelectStyle' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="loading" v-if="loading"><generate-loading /></div>
|
<div class="loading" v-if="loading">
|
||||||
|
<generate-loading title="Generating Results..." />
|
||||||
|
</div>
|
||||||
<div class="customize" v-else>
|
<div class="customize" v-else>
|
||||||
<div class="title">Customize your Look!</div>
|
<div class="title">Customize your Look!</div>
|
||||||
<p class="tip">Refine your Look</p>
|
<p class="tip">Refine your Look</p>
|
||||||
@@ -136,11 +139,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<img :src="customizeInfo.tryOnUrl" />
|
<img :src="customizeInfo.tryOnUrl" />
|
||||||
<div class="select-box">
|
<div class="history-icon"><SvgIcon name="history" size="42" /></div>
|
||||||
|
<!-- <div class="select-box">
|
||||||
<div class="icon"><SvgIcon name="history" size="35" /></div>
|
<div class="icon"><SvgIcon name="history" size="35" /></div>
|
||||||
<div class="label">History</div>
|
<div class="label">History</div>
|
||||||
<div class="icon"><SvgIcon name="xialajiantou" size="29" /></div>
|
<div class="icon"><SvgIcon name="xialajiantou" size="29" /></div>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<div @click="onLove">
|
<div @click="onLove">
|
||||||
<SvgIcon :name="`love_${customizeInfo.isFavorite ? 1 : 0}`" size="35" />
|
<SvgIcon :name="`love_${customizeInfo.isFavorite ? 1 : 0}`" size="35" />
|
||||||
@@ -153,9 +157,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="btns">
|
<div class="btns">
|
||||||
<button class="choose-outfit" v-if="!isHistoryFlow" @click="onChooseOutfit">
|
<button class="choose-outfit" v-if="!isHistoryFlow" @click="onChooseOutfit">
|
||||||
Choose Outfit
|
<span>Choose Outfit</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="finish" @click="onFinish">Finish</button>
|
<button class="finish" @click="onFinish"><span>Finish</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -230,7 +234,7 @@
|
|||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
width: 73rem;
|
width: 73rem;
|
||||||
height: 109.5rem;
|
height: 109.5rem;
|
||||||
border-radius: 2rem;
|
border-radius: 1rem;
|
||||||
// box-shadow: 1.3rem 1.4rem 2rem 0.2rem #0000004d;
|
// box-shadow: 1.3rem 1.4rem 2rem 0.2rem #0000004d;
|
||||||
border: 0.2rem solid #d9d9d9;
|
border: 0.2rem solid #d9d9d9;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -242,6 +246,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
> .history-icon {
|
||||||
|
top: 4.2rem;
|
||||||
|
left: 4.2rem;
|
||||||
|
}
|
||||||
> .select-box {
|
> .select-box {
|
||||||
top: 1.8rem;
|
top: 1.8rem;
|
||||||
left: 1.8rem;
|
left: 1.8rem;
|
||||||
@@ -262,15 +270,15 @@
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .icons {
|
> .icons {
|
||||||
bottom: 0.27rem;
|
bottom: 3.6rem;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 10rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
> div {
|
> div {
|
||||||
margin-right: 1.5rem;
|
margin-right: 2.4rem;
|
||||||
width: 6.2rem;
|
width: 6.2rem;
|
||||||
height: 6.2rem;
|
height: 6.2rem;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
@@ -290,22 +298,34 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
> button {
|
> button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: satoshiRegular;
|
font-family: satoshiMedium;
|
||||||
border: none;
|
border: none;
|
||||||
width: 34rem;
|
width: 34rem;
|
||||||
height: 9.2rem;
|
height: 9.2rem;
|
||||||
border-radius: 1.3rem;
|
border-radius: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
font-size: 3.8rem;
|
font-size: 4rem;
|
||||||
&:active {
|
&:active {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> :first-child.finish,
|
> :first-child.finish,
|
||||||
> .choose-outfit {
|
> .choose-outfit {
|
||||||
background-color: transparent;
|
|
||||||
color: #000;
|
color: #000;
|
||||||
border: 0.2rem solid #000;
|
background: linear-gradient(165deg, #b3b3b3 0%, #000 50%, #b3b3b3 100%);
|
||||||
|
position: relative;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 0.25rem solid transparent;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> :first-child.finish {
|
> :first-child.finish {
|
||||||
width: 87.5rem;
|
width: 87.5rem;
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const onExit = () => {
|
const onExit = () => {
|
||||||
console.log('exit')
|
console.log('exit')
|
||||||
}
|
}
|
||||||
@@ -16,7 +13,9 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="title">Thank you.</div>
|
<div class="title">Thank you.</div>
|
||||||
<div class="tip">
|
<div class="tip">
|
||||||
We are starting to learn your preferences, Looking forward to see you again,
|
We are starting to learn your preferences,
|
||||||
|
<br />
|
||||||
|
Looking forward to see you again,
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,21 +35,19 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
> .content {
|
> .content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 12.9rem;
|
top: 15.4rem;
|
||||||
left: 6rem;
|
left: 9.6rem;
|
||||||
// width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
> .title {
|
> .title {
|
||||||
font-family: satoshiBold;
|
font-family: satoshiBold;
|
||||||
font-size: 14.7rem;
|
font-size: 11rem;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
}
|
}
|
||||||
> .tip {
|
> .tip {
|
||||||
margin-top: 2.5rem;
|
margin-top: 2.5rem;
|
||||||
width: 58.2rem;
|
|
||||||
font-family: satoshiRegular;
|
font-family: satoshiRegular;
|
||||||
font-size: 4.1rem;
|
font-size: 4rem;
|
||||||
line-height: 132%;
|
line-height: 132%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ import { FlowType } from '@/types/enum'
|
|||||||
|
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
//})
|
//})
|
||||||
const emit = defineEmits([
|
|
||||||
'viewType'
|
|
||||||
])
|
|
||||||
|
|
||||||
// const data = reactive({
|
// const data = reactive({
|
||||||
// })
|
// })
|
||||||
@@ -22,9 +19,6 @@ const historicalReview = ()=>{
|
|||||||
router.push(`/workshop/creation?flowType=${FlowType.HISTORY}`)
|
router.push(`/workshop/creation?flowType=${FlowType.HISTORY}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
|
||||||
emit('viewType', 1)
|
|
||||||
})
|
|
||||||
onUnmounted(()=>{
|
onUnmounted(()=>{
|
||||||
})
|
})
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ const hGenerateStore = useHGenerateStore()
|
|||||||
|
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
//})
|
//})
|
||||||
const emit = defineEmits([
|
|
||||||
'view-type'
|
|
||||||
])
|
|
||||||
|
|
||||||
const navList = ref([])
|
const navList = ref([])
|
||||||
const navDisabledList = ref([])
|
const navDisabledList = ref([])
|
||||||
@@ -22,20 +19,6 @@ const navDisabledList = ref([])
|
|||||||
// const data = reactive({
|
// const data = reactive({
|
||||||
// })
|
// })
|
||||||
|
|
||||||
const clickSwitchVIPID = ()=>{
|
|
||||||
showConfirmDialog({
|
|
||||||
title: 'Switch VIP ID?',
|
|
||||||
message: 'You have unsaved changes. Your progress will be lost.',
|
|
||||||
confirmButtonText: 'Yes',
|
|
||||||
cancelButtonText: 'Cancel',
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
MyEvent.emit('clear-generate-state')
|
|
||||||
MyEvent.emit('clearAllCache')
|
|
||||||
router.push({ name: 'customer', query: { demo: 1 } })
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
const openFlow = (item: any)=>{
|
const openFlow = (item: any)=>{
|
||||||
item.click && item.click()
|
item.click && item.click()
|
||||||
const query = route.query
|
const query = route.query
|
||||||
@@ -50,10 +33,9 @@ const openFlow = (item: any)=>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
emit('view-type', 1)
|
|
||||||
let nav = [
|
let nav = [
|
||||||
{
|
{
|
||||||
path: 'selectStyle',
|
path: 'SelectStyle',
|
||||||
imgPath: new URL('@/assets/images/nav1.png',import.meta.url).href,
|
imgPath: new URL('@/assets/images/nav1.png',import.meta.url).href,
|
||||||
flowTypeList: [FlowType.H_TRYON,FlowType.H_AI],
|
flowTypeList: [FlowType.H_TRYON,FlowType.H_AI],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workshop">
|
<div class="workshop">
|
||||||
<header-title @clickProfile="handleClickProfile" />
|
<header-title @clickProfile="handleClickProfile" />
|
||||||
<RouteCache />
|
<RouteCache view-type="1" />
|
||||||
<footer-navigation v-if="notShowFooter" />
|
<footer-navigation v-if="notShowFooter" />
|
||||||
</div>
|
</div>
|
||||||
<profile ref="profileRef" />
|
<profile ref="profileRef" />
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { FormatDate } from '@/utils/tools'
|
import { FormatDate } from '@/utils/tools'
|
||||||
import { getCustomerPhotos, deleteCustomerPhoto } from '@/api/workshop'
|
import { getCustomerPhotos, deleteCustomerPhoto } from '@/api/workshop'
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
import { showConfirmDialog } from 'vant'
|
import { showConfirmDialog } from 'vant'
|
||||||
import { useGenerateStore } from '@/stores'
|
import { useGenerateStore } from '@/stores'
|
||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const finish = ref(false)
|
const finish = ref(false)
|
||||||
const list = reactive([])
|
const list = reactive([])
|
||||||
@@ -47,7 +43,7 @@
|
|||||||
cancelButtonText: 'Cancel'
|
cancelButtonText: 'Cancel'
|
||||||
}).catch(() => 0)
|
}).catch(() => 0)
|
||||||
if (res === 0) return
|
if (res === 0) return
|
||||||
console.log(obj,i)
|
console.log(obj, i)
|
||||||
deleteCustomerPhoto(obj.visitRecordId)
|
deleteCustomerPhoto(obj.visitRecordId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
list.splice(i, 1)
|
list.splice(i, 1)
|
||||||
@@ -68,7 +64,12 @@
|
|||||||
<div class="library">
|
<div class="library">
|
||||||
<div class="title">Library</div>
|
<div class="title">Library</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<my-list v-model:loading="loading" v-model:finish="finish" @load="onLoad">
|
<my-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
v-model:finish="finish"
|
||||||
|
:empty="list.length === 0"
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
<div class="item" v-for="(v, i) in list" :key="v.visitRecordId">
|
<div class="item" v-for="(v, i) in list" :key="v.visitRecordId">
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<img v-lazy="v.defaultImageUrl" />
|
<img v-lazy="v.defaultImageUrl" />
|
||||||
@@ -115,18 +116,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .title {
|
> .title {
|
||||||
font-family: satoshiRegular;
|
font-family: satoshiBold;
|
||||||
font-size: 9rem;
|
font-size: 8.6rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
margin: 5rem;
|
margin: 5rem 9.1rem;
|
||||||
}
|
}
|
||||||
> .list {
|
> .list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 3rem;
|
margin: 0 4.5rem;
|
||||||
> .my-list {
|
> .my-list {
|
||||||
padding: 0 3.8rem;
|
padding: 0 4.5rem;
|
||||||
--my-list-footer-margin: 2rem 0;
|
--my-list-footer-margin: 2rem 0;
|
||||||
> .item {
|
> .item {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -142,7 +143,7 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
> .image {
|
> .image {
|
||||||
width: 22.9rem;
|
width: 19.2rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 2rem;
|
border-radius: 2rem;
|
||||||
@@ -150,7 +151,7 @@
|
|||||||
> img {
|
> img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: contain;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,41 +160,33 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 90%;
|
height: 90%;
|
||||||
> .userID {
|
|
||||||
font-family: satoshiRegular;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 3.8rem;
|
|
||||||
line-height: 89%;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
> .datetime {
|
> .datetime {
|
||||||
margin-top: 1.8rem;
|
margin-top: 1rem;
|
||||||
font-family: satoshiRegular;
|
font-family: satoshiMedium;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
font-size: 3.2rem;
|
font-size: 3.2rem;
|
||||||
line-height: 89%;
|
line-height: 89%;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
> .lastopened {
|
> .lastopened {
|
||||||
margin-top: 1rem;
|
margin-top: 1.8rem;
|
||||||
font-family: satoshiRegular;
|
font-family: satoshiRegular;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: Regular;
|
|
||||||
font-size: 2.6rem;
|
font-size: 2.6rem;
|
||||||
line-height: 89%;
|
line-height: 89%;
|
||||||
color: #6f6f6f;
|
color: #6f6f6f;
|
||||||
}
|
}
|
||||||
> button {
|
> button {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
font-family: satoshiRegular;
|
||||||
width: 12.3rem;
|
width: 12.3rem;
|
||||||
height: 3.8rem;
|
height: 3.8rem;
|
||||||
border-radius: 0.5rem;
|
line-height: 100%;
|
||||||
box-sizing: content-box;
|
border-radius: 0.4rem;
|
||||||
border: 0.193rem solid #000;
|
border: 0.2rem solid #000;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
font-family: satoshiRegular;
|
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 2.576rem;
|
font-size: 2.4rem;
|
||||||
color: #000;
|
color: #000;
|
||||||
margin-right: 5rem;
|
margin-right: 5rem;
|
||||||
&:active {
|
&:active {
|
||||||
@@ -207,8 +200,8 @@
|
|||||||
right: 2rem;
|
right: 2rem;
|
||||||
width: 5.5rem;
|
width: 5.5rem;
|
||||||
height: 5.5rem;
|
height: 5.5rem;
|
||||||
border: 0.188rem solid #000;
|
border: 0.188rem solid rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 1.88rem;
|
border-radius: 0.9rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
|||||||
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
||||||
import { generateTryOnEffect, setTryOnEffectFavorite, cancelTryOnEffectFavorite, addTryOnEffectComment } from '@/api/workshop'
|
import { generateTryOnEffect, setTryOnEffectFavorite, cancelTryOnEffectFavorite, addTryOnEffectComment } from '@/api/workshop'
|
||||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||||
|
import gradientButton from '@/components/gradientButton.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
//})
|
//})
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
let data = reactive({
|
let data = reactive({
|
||||||
modelList:
|
modelList:
|
||||||
[
|
[
|
||||||
@@ -38,7 +38,7 @@ const onContinue = ()=>{
|
|||||||
if(!isHistoryFlow.value){
|
if(!isHistoryFlow.value){
|
||||||
router.push({ path: 'uploadFace', query: {...query.value} })
|
router.push({ path: 'uploadFace', query: {...query.value} })
|
||||||
}else{
|
}else{
|
||||||
router.push({ path: 'creation', query: {...query.value} })
|
router.push({ path: 'creation', query: {...query.value, active: FlowType.H_TRYON } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,6 @@ const startGenerate = ()=>{
|
|||||||
generateStore.originalTryOn.id = res.id
|
generateStore.originalTryOn.id = res.id
|
||||||
generateStore.originalTryOn.tryOnUrl = res.tryOnUrl
|
generateStore.originalTryOn.tryOnUrl = res.tryOnUrl
|
||||||
// generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式
|
// generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式
|
||||||
generateStore.setIsGenerate(false)
|
|
||||||
|
|
||||||
generateStore.clearCustomizeInfo()
|
generateStore.clearCustomizeInfo()
|
||||||
}).catch((error)=>{
|
}).catch((error)=>{
|
||||||
@@ -120,8 +119,7 @@ const handleSubmit = ()=>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emit('view-type', 1)
|
if (!generateStore.originalTryOn.id) {
|
||||||
if (generateStore.isGenerate) {
|
|
||||||
startGenerate()
|
startGenerate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -134,7 +132,7 @@ const { isLoading } = toRefs(data);
|
|||||||
<div class="product" v-if="!isLoading">
|
<div class="product" v-if="!isLoading">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
Go with this Look?
|
{{ isHistoryFlow?'Generate Result':'Go with this look?' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="selectContent">
|
<div class="selectContent">
|
||||||
@@ -150,14 +148,25 @@ const { isLoading } = toRefs(data);
|
|||||||
<div class="operation">
|
<div class="operation">
|
||||||
<div @click="setLike"><SvgIcon :name="`love_${generateStore.originalTryOn.isLike ? '1' : '0'}`" size="35" /></div>
|
<div @click="setLike"><SvgIcon :name="`love_${generateStore.originalTryOn.isLike ? '1' : '0'}`" size="35" /></div>
|
||||||
<div @click="feedback"><SvgIcon name="pen" size="40" /></div>
|
<div @click="feedback"><SvgIcon name="pen" size="40" /></div>
|
||||||
<div @click="startGenerate"><SvgIcon name="reload" size="35" /></div>
|
|
||||||
<!-- <div><SvgIcon name="download" size="35" /></div> -->
|
<!-- <div><SvgIcon name="download" size="35" /></div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="again">
|
<div class="btn">
|
||||||
<!-- <div @click="changeModel">Change Model</div> -->
|
<div class="btnItem style1" @click.stop="startGenerate()">
|
||||||
<button class="flex flex-center" @click="onContinue" style="margin-left: auto;">Continue</button>
|
<gradientButton>
|
||||||
|
<template #content>
|
||||||
|
<div class="text">
|
||||||
|
<span class="icon">
|
||||||
|
<SvgIcon name="reTry" size="40" />
|
||||||
|
</span>
|
||||||
|
Re-try
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</gradientButton>
|
||||||
|
</div>
|
||||||
|
<div class="btnItem style2" @click.stop="onContinue">{{ isHistoryFlow?'Finish':'Continue' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,18 +199,18 @@ const { isLoading } = toRefs(data);
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.product{
|
.product{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
> .text{
|
> .text{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 4.3rem;
|
margin-top: 4rem;
|
||||||
> .title{
|
> .title{
|
||||||
font-family: satoshiBold;
|
font-family: satoshiBold;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 9.6rem;
|
font-size: 8.6rem;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
@@ -210,9 +219,10 @@ const { isLoading } = toRefs(data);
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0 14.1rem;
|
padding: 0 14.1rem;
|
||||||
margin-top: 4.8rem;
|
margin-top: 4.3rem;
|
||||||
> .history{
|
> .history{
|
||||||
width: 30.2rem;
|
width: 30.2rem;
|
||||||
|
margin-left: calc(7% / 2);
|
||||||
height: 6.52rem;
|
height: 6.52rem;
|
||||||
border-radius: 0.91rem;
|
border-radius: 0.91rem;
|
||||||
border: 0.24rem solid #000;
|
border: 0.24rem solid #000;
|
||||||
@@ -229,7 +239,7 @@ const { isLoading } = toRefs(data);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .modelBox{
|
> .modelBox{
|
||||||
margin-top: 2.5rem;
|
margin-top: 4.4rem;
|
||||||
> .model{
|
> .model{
|
||||||
border: .2rem solid #D9D9D9;
|
border: .2rem solid #D9D9D9;
|
||||||
// height: 110rem;
|
// height: 110rem;
|
||||||
@@ -262,25 +272,40 @@ const { isLoading } = toRefs(data);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .again{
|
> .btn{
|
||||||
margin-top: 4.4rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 6.6rem;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
> button{
|
margin-top: 5.2rem;
|
||||||
border-radius: .7rem;
|
> div {
|
||||||
border: 3px solid #000;
|
border-radius: .96rem;
|
||||||
background-color: #000;
|
width: 33.7rem;
|
||||||
text-align: center;
|
font-size: 4.8rem;
|
||||||
color: #fff;
|
|
||||||
font-family: satoshiMedium;
|
font-family: satoshiMedium;
|
||||||
font-size: 3.6rem;
|
line-height: 9.2rem;
|
||||||
width: 24.6rem;
|
display: flex;
|
||||||
line-height: 6.7rem;
|
justify-content: center;
|
||||||
height: 6.7rem;
|
&.style1{
|
||||||
box-sizing: border-box;
|
--borderRadius: .96rem;
|
||||||
&:last-child{
|
--borderWidth: 2px;
|
||||||
margin-right: 0;
|
.text{
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
> .icon{
|
||||||
|
left: 4rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
&.style2{
|
||||||
|
color: #fff;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btnItem .text{
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,6 +354,7 @@ const { isLoading } = toRefs(data);
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
margin-bottom: 1.8rem;
|
margin-bottom: 1.8rem;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
> .info{
|
> .info{
|
||||||
font-size: 3.2rem;
|
font-size: 3.2rem;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, inject } from 'vue'
|
import { ref, reactive, onMounted, inject, watch } from 'vue'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { showConfirmDialog } from 'vant'
|
import { showConfirmDialog, showToast } from 'vant'
|
||||||
import { useUserInfoStore, useOverallStore } from '@/stores'
|
import { useUserInfoStore, useOverallStore, useGenerateStore } from '@/stores'
|
||||||
import { LogOut } from '@/api/login'
|
import { LogOut } from '@/api/login'
|
||||||
import { getCustomerList, type CustomerListParams,customerCheckin } from '@/api/workshop'
|
import { getCustomerList, type CustomerListParams, customerCheckin } from '@/api/workshop'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
import { encryptPassword } from '@/utils/tools'
|
||||||
|
import { updateUserInfo } from '@/api/login'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isCustomer?: boolean
|
isCustomer?: boolean
|
||||||
@@ -13,21 +15,30 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const userInfoStore = useUserInfoStore()
|
const userInfoStore = useUserInfoStore()
|
||||||
const overallStore = useOverallStore()
|
const overallStore = useOverallStore()
|
||||||
const emit = defineEmits(['view-type', 'selected-customer'])
|
const generateStore = useGenerateStore()
|
||||||
|
|
||||||
|
const emit = defineEmits(['selected-customer', 'change-visible'])
|
||||||
const show = ref(false)
|
const show = ref(false)
|
||||||
const isEdit = ref(false)
|
const isEdit = ref(false)
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: { msg: '', value: userInfoStore.state.userInfo.username },
|
name: { msg: '', value: '' },
|
||||||
email: { msg: '', value: userInfoStore.state.userInfo.email },
|
email: { msg: '', value: '' },
|
||||||
password: { show: false, msg: '', value: userInfoStore.state.userInfo.password }
|
password: { show: true, msg: '', value: '' }
|
||||||
})
|
})
|
||||||
const open = () => {
|
const open = () => {
|
||||||
|
form.name.value = userInfoStore.state.userInfo.username
|
||||||
|
form.email.value = userInfoStore.state.userInfo.email
|
||||||
|
form.password.value = ''
|
||||||
isEdit.value = false
|
isEdit.value = false
|
||||||
show.value = true
|
show.value = true
|
||||||
}
|
}
|
||||||
const close = () => {
|
const close = () => {
|
||||||
show.value = false
|
show.value = false
|
||||||
}
|
}
|
||||||
|
watch(show, (newVal) => {
|
||||||
|
emit('change-visible', newVal)
|
||||||
|
MyEvent.emit('change-profile-visible', newVal)
|
||||||
|
})
|
||||||
const onEditItem = (item) => {
|
const onEditItem = (item) => {
|
||||||
if (!form[item]) return
|
if (!form[item]) return
|
||||||
form[item].edit = true
|
form[item].edit = true
|
||||||
@@ -41,14 +52,38 @@ const switchCustomer = () => {
|
|||||||
handleShowPopup(true)
|
handleShowPopup(true)
|
||||||
}
|
}
|
||||||
const edit = () => {
|
const edit = () => {
|
||||||
|
form.password.value = ''
|
||||||
isEdit.value = true
|
isEdit.value = true
|
||||||
}
|
}
|
||||||
const confirm = () => {
|
const confirm = () => {
|
||||||
|
if (!isEdit.value) return
|
||||||
|
const password = form.password.value
|
||||||
|
const params = {
|
||||||
|
username: form.name.value,
|
||||||
|
email: form.email.value,
|
||||||
|
password
|
||||||
|
}
|
||||||
|
if (password) {
|
||||||
|
if (password.length < 6) {
|
||||||
|
return showToast('Password must be at least 6 characters')
|
||||||
|
} else {
|
||||||
|
params.password = encryptPassword(password)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.password = userInfoStore.state.userInfo.password
|
||||||
|
}
|
||||||
|
|
||||||
overallStore.setLoading(true)
|
overallStore.setLoading(true)
|
||||||
setTimeout(() => {
|
updateUserInfo(params).then((res) => {
|
||||||
overallStore.setLoading(false)
|
overallStore.setLoading(false)
|
||||||
|
showToast('Update success')
|
||||||
|
userInfoStore.setUserInfo({
|
||||||
|
...userInfoStore.state.userInfo,
|
||||||
|
...params
|
||||||
|
})
|
||||||
|
form.password.value = ''
|
||||||
isEdit.value = false
|
isEdit.value = false
|
||||||
}, 1000)
|
})
|
||||||
}
|
}
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
showConfirmDialog({
|
showConfirmDialog({
|
||||||
@@ -135,8 +170,13 @@ const onScroll = (e: Event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 打开customer选择时关闭profile弹窗 如果不是点击confirem关闭则重新打开profile弹窗
|
// 打开customer选择时关闭profile弹窗 如果不是点击confirem关闭则重新打开profile弹窗
|
||||||
const handleShowPopup = (flag: boolean) => {
|
let isCustomerOnly = false
|
||||||
|
const handleShowPopup = (flag: boolean, customer: boolean) => {
|
||||||
|
console.log(flag,customer)
|
||||||
|
// customer: 是否是顾客页面只展示customer选择弹窗
|
||||||
|
isCustomerOnly = customer
|
||||||
showSwitchCustomerPopup.value = flag
|
showSwitchCustomerPopup.value = flag
|
||||||
|
if (props.isCustomer) return
|
||||||
show.value = !flag
|
show.value = !flag
|
||||||
if (flag) {
|
if (flag) {
|
||||||
loadCustomers(true)
|
loadCustomers(true)
|
||||||
@@ -152,12 +192,13 @@ const handleSelectCustomer = () => {
|
|||||||
if (selectedCustomer) {
|
if (selectedCustomer) {
|
||||||
emit('selected-customer', selectedCustomer)
|
emit('selected-customer', selectedCustomer)
|
||||||
}
|
}
|
||||||
if(!props.isCustomer){
|
if (!isCustomerOnly) {
|
||||||
// show.value = true
|
// show.value = true
|
||||||
customerCheckin({ nickname: selectedCustomer.name }).then((res) => {
|
customerCheckin({ nickname: selectedCustomer.name }).then((res) => {
|
||||||
useUserInfoStore().resetGenerateParams()
|
useUserInfoStore().resetGenerateParams()
|
||||||
MyEvent.emit('clear-generate-state')
|
MyEvent.emit('clear-generate-state')
|
||||||
useUserInfoStore().setCustomerInfo(res)
|
useGenerateStore().setCustomerInfo(res)
|
||||||
|
router.push({ path: '/workshop/home' })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
showSwitchCustomerPopup.value = false
|
showSwitchCustomerPopup.value = false
|
||||||
@@ -168,15 +209,15 @@ const handleFetchCustomerList = () => {
|
|||||||
}
|
}
|
||||||
MyEvent.add('update-customer-list', handleFetchCustomerList)
|
MyEvent.add('update-customer-list', handleFetchCustomerList)
|
||||||
|
|
||||||
const openSwitchCustomerPopup = (flag = true) => {
|
// const openSwitchCustomerPopup = (flag = true) => {
|
||||||
showSwitchCustomerPopup.value = flag
|
// showSwitchCustomerPopup.value = flag
|
||||||
}
|
// }
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
handleFetchCustomerList()
|
handleFetchCustomerList()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({ open, close, openSwitchCustomerPopup })
|
defineExpose({ open, close, handleShowPopup })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -186,7 +227,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
<span class="title">Profile</span>
|
<span class="title">Profile</span>
|
||||||
<van-icon name="cross" class="close" @click="close" />
|
<van-icon name="cross" class="close" @click="close" />
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<form class="box" @submit.prevent.stop="confirm">
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<div class="label">Your Name</div>
|
<div class="label">Your Name</div>
|
||||||
<label class="input">
|
<label class="input">
|
||||||
@@ -196,6 +237,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
placeholder="Enter your name"
|
placeholder="Enter your name"
|
||||||
v-model="form.name.value"
|
v-model="form.name.value"
|
||||||
:readonly="!isEdit"
|
:readonly="!isEdit"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<!-- <div class="icon" v-if="isEdit" @click.stop="onSaveItem('name')">
|
<!-- <div class="icon" v-if="isEdit" @click.stop="onSaveItem('name')">
|
||||||
<SvgIcon name="confirmation" size="37" />
|
<SvgIcon name="confirmation" size="37" />
|
||||||
@@ -226,7 +268,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
</label>
|
</label>
|
||||||
<p class="error" v-show="form.email.msg">{{ form.email.msg }}</p>
|
<p class="error" v-show="form.email.msg">{{ form.email.msg }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<div class="form-item" v-if="isEdit">
|
||||||
<div class="label">Password</div>
|
<div class="label">Password</div>
|
||||||
<label class="input">
|
<label class="input">
|
||||||
<div class="icon" @click="form.password.show = !form.password.show">
|
<div class="icon" @click="form.password.show = !form.password.show">
|
||||||
@@ -249,7 +291,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="isEdit">
|
<template v-if="isEdit">
|
||||||
<button class="confirm" @click="confirm">Confirm</button>
|
<button type="submit" class="confirm">Confirm</button>
|
||||||
<p class="tip">Powered by AiDLab for Lane Crawford</p>
|
<p class="tip">Powered by AiDLab for Lane Crawford</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -257,7 +299,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
<button class="edit" @click="edit">Edit Profile</button>
|
<button class="edit" @click="edit">Edit Profile</button>
|
||||||
<button class="logout" @click="logout">Log out</button>
|
<button class="logout" @click="logout">Log out</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
<van-popup
|
<van-popup
|
||||||
@@ -269,7 +311,7 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
>
|
>
|
||||||
<div class="popup-title flex">
|
<div class="popup-title flex">
|
||||||
<div class="title-txt">Saved Customer ID</div>
|
<div class="title-txt">Saved Customer ID</div>
|
||||||
<SvgIcon name="close_nocolor" color="#a1a1a1" size="40" @click="handleShowPopup(false)" />
|
<SvgIcon name="close_nocolor" color="#a1a1a1" size="40" @click="handleShowPopup(false,false)" />
|
||||||
</div>
|
</div>
|
||||||
<div ref="customerListEl" class="cusomter-list" @scroll="onScroll">
|
<div ref="customerListEl" class="cusomter-list" @scroll="onScroll">
|
||||||
<div
|
<div
|
||||||
@@ -289,12 +331,13 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
|||||||
<img v-show="item.checked" class="checked-icon" src="@/assets/images/checked.png" />
|
<img v-show="item.checked" class="checked-icon" src="@/assets/images/checked.png" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="list-footer">
|
||||||
<div class="list-footer">
|
<div v-if="pager.loading">Loading...</div>
|
||||||
<div v-if="pager.loading">Loading...</div>
|
<div v-else-if="pager.noMore">No more</div>
|
||||||
<div v-else-if="pager.noMore">No more</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="confirm-btn" @click="handleSelectCustomer">Confirm</div>
|
<div class="confirm-btn" @click="handleSelectCustomer">Confirm</div>
|
||||||
|
<div class="van-safe-area-bottom"></div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useGenerateStore } from '@/stores'
|
import { useGenerateStore } from '@/stores'
|
||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const clickNext = () => {
|
const clickNext = () => {
|
||||||
generateStore.updatePhotoInfo({})
|
generateStore.updatePhotoInfo({})
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import { useGenerateStore } from '@/stores'
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
//})
|
//})
|
||||||
const emit = defineEmits([
|
|
||||||
'view-type'
|
|
||||||
])
|
|
||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
let data = reactive({
|
let data = reactive({
|
||||||
modelList:
|
modelList:
|
||||||
@@ -45,9 +43,7 @@ let data = reactive({
|
|||||||
const setSelectedModelId = (item)=>{
|
const setSelectedModelId = (item)=>{
|
||||||
generateStore.selectModel(item)
|
generateStore.selectModel(item)
|
||||||
}
|
}
|
||||||
onMounted(()=>{
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const toProduct = ()=>{
|
const toProduct = ()=>{
|
||||||
router.push({ path: 'product' })
|
router.push({ path: 'product' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,301 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, onUnmounted, reactive, toRefs, computed, ref } from "vue";
|
|
||||||
import SelectItem from "@/components/selectStyle/selectItem.vue";
|
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
|
||||||
import { useGenerateStore, useUserInfoStore, useHGenerateStore } from '@/stores'
|
|
||||||
import { showToast } from 'vant';
|
|
||||||
import { shareImageToWhatsapp } from '@/utils/tools'
|
|
||||||
import { generateRequestOutfit, getRequestOutfit, setStyleFavorite, cancelStyleFavorite, retrieveAndRegenerate } from '@/api/workshop'
|
|
||||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
|
||||||
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
|
||||||
import gradientButton from '@/components/gradientButton.vue'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
//const props = defineProps({
|
|
||||||
//})
|
|
||||||
const emit = defineEmits([
|
|
||||||
'view-type'
|
|
||||||
])
|
|
||||||
const generateStore = useGenerateStore()
|
|
||||||
const userInfoStore = useUserInfoStore()
|
|
||||||
const hGenerateStore = useHGenerateStore()
|
|
||||||
|
|
||||||
const query = computed(() => route.query)
|
|
||||||
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
|
||||||
const isLoading = ref(false)
|
|
||||||
const loadingTitle= ref('Analyzing the Outfit...')
|
|
||||||
// const loadingTitle = computed(()=>{
|
|
||||||
// let str = ''
|
|
||||||
// if(!select.value.status)str = 'Analyzing the Outfit...'
|
|
||||||
// if(select.value.status == 'RUNNING')str = 'Generating Results...'
|
|
||||||
// if(select.value.status == 'PENDING')str = 'Almost there...'
|
|
||||||
// return str
|
|
||||||
// })
|
|
||||||
|
|
||||||
let data = reactive({
|
|
||||||
select:computed(()=>generateStore.style),
|
|
||||||
// styleList:computed(()=>generateStore.styleList),
|
|
||||||
})
|
|
||||||
|
|
||||||
let getGenerateTime = null as any
|
|
||||||
|
|
||||||
const updateStyle = ()=>{
|
|
||||||
// generateStore.updateStyle(item)
|
|
||||||
// data.styleList[index] = {}
|
|
||||||
requestOutfit({num:1})
|
|
||||||
}
|
|
||||||
const setLikeStyle = (likeStyle)=>{
|
|
||||||
if(!select.value.id)return
|
|
||||||
if(likeStyle){
|
|
||||||
cancelStyleFavorite(select.value.id).then(()=>{
|
|
||||||
select.value.isLike = false
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
setStyleFavorite(select.value.id).then(()=>{
|
|
||||||
select.value.isLike = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setDownload = ()=>{
|
|
||||||
if(select.value.path)shareImageToWhatsapp(select.value.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
const toProduct = ()=>{
|
|
||||||
// if(generateStore.style.id){
|
|
||||||
// generateStore.setIsGenerate(true)
|
|
||||||
// }
|
|
||||||
if(!isHistoryFlow.value){
|
|
||||||
router.push({ path: 'product', query: {...query.value} })
|
|
||||||
}else{
|
|
||||||
router.push({ path: 'creation', query: {...query.value, active: FlowType.H_OUTFIT} })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestOutfit = async ({num})=>{
|
|
||||||
let rv = await new Promise<void>((resolve, reject) => {
|
|
||||||
if(isHistoryFlow.value){
|
|
||||||
retrieveAndRegenerate({tryOnEffectsId:hGenerateStore.originalTryOn.id}).then((rv:any)=>{
|
|
||||||
resolve(rv)
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
let value = {
|
|
||||||
"customerId": generateStore.customerId,
|
|
||||||
"checkInId": generateStore.visitRecordId,
|
|
||||||
"stylist": userInfoStore.state.generateParams.stylist,
|
|
||||||
"gender": userInfoStore.state.generateParams.sex,
|
|
||||||
"sessionId": generateStore.sessionId,
|
|
||||||
num,
|
|
||||||
}
|
|
||||||
generateRequestOutfit(value).then((rv:any)=>{
|
|
||||||
resolve(rv)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
isLoading.value = true
|
|
||||||
generateStore.clearStyle()
|
|
||||||
data.select.taskId = rv[0]
|
|
||||||
getRequestOutfitList(rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRequestOutfitList = (generateList)=>{
|
|
||||||
let value = {requestIDs:generateList.join(',')}
|
|
||||||
getRequestOutfit(value).then((rv:any)=>{
|
|
||||||
rv.forEach((item)=>{
|
|
||||||
data.select.id = item.id
|
|
||||||
data.select.path = item.path
|
|
||||||
data.select.status = item.status
|
|
||||||
})
|
|
||||||
|
|
||||||
if(['RUNNING','PENDING'].includes(data.select.status)){
|
|
||||||
getGenerateTime = setTimeout(()=>{
|
|
||||||
getRequestOutfitList([data.select.taskId])
|
|
||||||
},3000)
|
|
||||||
}else{
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(()=>{
|
|
||||||
// generateStore.clearProductData()
|
|
||||||
|
|
||||||
emit('view-type', 1)
|
|
||||||
// if(!data.styleList[0]?.id)getRequestOutfitList(0)
|
|
||||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
|
||||||
if(data.select.status == 'SUCCEEDED'){
|
|
||||||
return
|
|
||||||
}else if(!data.select?.taskId){
|
|
||||||
requestOutfit({num:1})
|
|
||||||
}else if(data.select.status != 'SUCCEEDED'){
|
|
||||||
isLoading.value = true
|
|
||||||
// let generateList = [data.styleList[0].taskId]
|
|
||||||
getRequestOutfitList([data.select.taskId])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
onUnmounted(()=>{
|
|
||||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
|
||||||
})
|
|
||||||
defineExpose({})
|
|
||||||
const { select } = toRefs(data);
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="selectStyle">
|
|
||||||
<div class="text">
|
|
||||||
<div class="title">
|
|
||||||
Outfit Result
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
Refine your Look
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="selectContent">
|
|
||||||
<!-- {{ select }} -->
|
|
||||||
<div class="imgBox">
|
|
||||||
<img :src="select.path" alt="">
|
|
||||||
</div>
|
|
||||||
<div class="btn">
|
|
||||||
<div class="like" @click.stop="setLikeStyle(select.isLike)">
|
|
||||||
<SvgIcon :name="`love_${select.isLike?1:0}`" size="35" />
|
|
||||||
</div>
|
|
||||||
<div class="down" @click.stop="setDownload()">
|
|
||||||
<SvgIcon name="download" size="35" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="btn">
|
|
||||||
<div class="btnItem style1" @click.stop="updateStyle()">
|
|
||||||
<gradientButton>
|
|
||||||
<template #content>
|
|
||||||
<div class="text">
|
|
||||||
<span class="icon">
|
|
||||||
<SvgIcon name="reTry" size="40" />
|
|
||||||
</span>
|
|
||||||
Re-try
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
</gradientButton>
|
|
||||||
</div>
|
|
||||||
<div class="btnItem style2" @click.stop="toProduct">Continue</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="footer placeholder"></div> -->
|
|
||||||
<div class="loading-container" v-if="isLoading">
|
|
||||||
<GenerateLoading :title="loadingTitle"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.header-title {
|
|
||||||
// --header-title-background: #f6f6f6;
|
|
||||||
}
|
|
||||||
.loading-container{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 2;
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.selectStyle{
|
|
||||||
width: 100%;
|
|
||||||
flex: 1;
|
|
||||||
// height: 100%;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
overflow: hidden;
|
|
||||||
> .text{
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 8.5rem;
|
|
||||||
margin-bottom: 8.5rem;
|
|
||||||
> .title{
|
|
||||||
font-family: satoshiBold;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 8.6rem;
|
|
||||||
line-height: 124%;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
> .info{
|
|
||||||
font-size: 4rem;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 124%;
|
|
||||||
margin-top: 3.2rem;
|
|
||||||
color: rgba(0, 0, 0, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.selectContent{
|
|
||||||
// padding: 0 4rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
overflow: auto;
|
|
||||||
width: 73.7rem;
|
|
||||||
margin-bottom: 19.4rem;
|
|
||||||
> .imgBox{
|
|
||||||
height: 73.7rem;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 4.4rem;
|
|
||||||
> img{
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .btn{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 2rem;
|
|
||||||
> div{
|
|
||||||
color: #000;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 7rem;
|
|
||||||
height: 7rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: #fff;
|
|
||||||
&:hover{
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .btn{
|
|
||||||
display: flex;
|
|
||||||
gap: 6.6rem;
|
|
||||||
justify-content: center;
|
|
||||||
> div {
|
|
||||||
border-radius: .96rem;
|
|
||||||
width: 33.7rem;
|
|
||||||
font-size: 4.8rem;
|
|
||||||
font-family: satoshiMedium;
|
|
||||||
line-height: 9.2rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
&.style1{
|
|
||||||
--borderRadius: .96rem;
|
|
||||||
--borderWidth: 2px;
|
|
||||||
.text{
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
> .icon{
|
|
||||||
left: 4rem;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.style2{
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
391
src/views/Workshop/selectStyle/index.vue
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, reactive, toRefs, computed, ref } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { useGenerateStore, useUserInfoStore, useHGenerateStore } from '@/stores'
|
||||||
|
import { showToast } from 'vant'
|
||||||
|
import { shareImageToWhatsapp } from '@/utils/tools'
|
||||||
|
import {
|
||||||
|
generateRequestOutfit,
|
||||||
|
getRequestOutfit,
|
||||||
|
setStyleFavorite,
|
||||||
|
cancelStyleFavorite,
|
||||||
|
retrieveAndRegenerate
|
||||||
|
} from '@/api/workshop'
|
||||||
|
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||||
|
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
||||||
|
import gradientButton from '@/components/gradientButton.vue'
|
||||||
|
import StyleListDom from '@/views/Workshop/selectStyle/styleList.vue'
|
||||||
|
import { useStreamChat } from '@/hooks/useStreamChat'
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
//const props = defineProps({
|
||||||
|
//})
|
||||||
|
const generateStore = useGenerateStore()
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const hGenerateStore = useHGenerateStore()
|
||||||
|
|
||||||
|
const query = computed(() => route.query)
|
||||||
|
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
||||||
|
const isLoading = ref(false)
|
||||||
|
// const loadingTitle= ref('Analyzing the Outfit...')
|
||||||
|
const loadingTitle = computed(() => {
|
||||||
|
let str = 'Analyzing the Outfit...'
|
||||||
|
if (!select.value.status) str = 'Analyzing the Outfit...'
|
||||||
|
if (select.value.status == 'RUNNING') str = 'Generating Results...'
|
||||||
|
if (select.value.status == 'PENDING' || select.value.status == 'ALMOST_DONE')
|
||||||
|
str = 'Almost there...'
|
||||||
|
return str
|
||||||
|
})
|
||||||
|
|
||||||
|
let data = reactive({
|
||||||
|
select: computed(() => generateStore.style),
|
||||||
|
styleList: computed(() => generateStore.styleList)
|
||||||
|
})
|
||||||
|
let dataDom = reactive({
|
||||||
|
styleListVue: null
|
||||||
|
})
|
||||||
|
|
||||||
|
let getGenerateTime = null as any
|
||||||
|
|
||||||
|
const updateStyle = () => {
|
||||||
|
// generateStore.updateStyle(item)
|
||||||
|
// data.styleList[index] = {}
|
||||||
|
requestOutfit({ num: 4 })
|
||||||
|
}
|
||||||
|
const setLikeStyle = (likeStyle) => {
|
||||||
|
if (!select.value.id) return
|
||||||
|
if (likeStyle) {
|
||||||
|
cancelStyleFavorite(select.value.id).then(() => {
|
||||||
|
select.value.isLike = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setStyleFavorite(select.value.id).then(() => {
|
||||||
|
select.value.isLike = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDownload = () => {
|
||||||
|
if (select.value.path) shareImageToWhatsapp(select.value.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toProduct = () => {
|
||||||
|
// if(generateStore.style.id){
|
||||||
|
// generateStore.setIsGenerate(true)
|
||||||
|
// }
|
||||||
|
router.push({ path: 'product', query: { ...query.value } })
|
||||||
|
// if(!isHistoryFlow.value){
|
||||||
|
// router.push({ path: 'product', query: {...query.value} })
|
||||||
|
// }else{
|
||||||
|
// router.push({ path: 'creation', query: {...query.value, active: FlowType.H_OUTFIT} })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestOutfit = async ({ num }) => {
|
||||||
|
isLoading.value = true
|
||||||
|
let rv: any = await new Promise<void>((resolve, reject) => {
|
||||||
|
if (isHistoryFlow.value) {
|
||||||
|
retrieveAndRegenerate({
|
||||||
|
tryOnEffectsId: hGenerateStore.originalTryOn.id,
|
||||||
|
checkInId: generateStore.visitRecordId
|
||||||
|
}).then((rv: any) => {
|
||||||
|
resolve(rv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let value = {
|
||||||
|
customerId: generateStore.customerId,
|
||||||
|
checkInId: generateStore.visitRecordId,
|
||||||
|
stylist: userInfoStore.state.generateParams.stylist,
|
||||||
|
gender: userInfoStore.state.generateParams.sex,
|
||||||
|
sessionId: generateStore.sessionId,
|
||||||
|
num
|
||||||
|
}
|
||||||
|
generateRequestOutfit(value).then((rv: any) => {
|
||||||
|
resolve(rv)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
generateStore.clearProductData()
|
||||||
|
data.select.taskId = rv[0]
|
||||||
|
rv.forEach((item, index) => (data.styleList[index].taskId = item))
|
||||||
|
getRequestOutfitList(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRequestOutfitList = (generateList) => {
|
||||||
|
let value = { requestIDs: generateList.join(',') }
|
||||||
|
getRequestOutfit(value).then((rv: any) => {
|
||||||
|
let selectIndex = rv.findIndex((item) => item.requestId == data.select.taskId)
|
||||||
|
console.log(selectIndex)
|
||||||
|
if (selectIndex != -1) {
|
||||||
|
data.select.id = rv[selectIndex].id
|
||||||
|
data.select.path = rv[selectIndex].path
|
||||||
|
data.select.status = rv[selectIndex].status
|
||||||
|
}
|
||||||
|
rv.forEach((item) => {
|
||||||
|
let index = data.styleList.findIndex(
|
||||||
|
(styleListItem) => styleListItem?.taskId == item.requestId
|
||||||
|
)
|
||||||
|
data.styleList[index] = {
|
||||||
|
id: item.id,
|
||||||
|
taskId: item.requestId,
|
||||||
|
status: item.status,
|
||||||
|
path: item.path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (['SUCCEEDED'].includes(data.select.status)) {
|
||||||
|
isLoading.value = false
|
||||||
|
if (isHistoryFlow.value) {
|
||||||
|
hGenerateStore.uploadStyle({
|
||||||
|
id: data.select.id,
|
||||||
|
path: data.select.path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.styleList.filter((item) => item?.status == 'FAILED').length > 0) {
|
||||||
|
showToast({
|
||||||
|
message: 'One of the outfits failed to generate. Please try generating again.',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
const taskIdList = data.styleList
|
||||||
|
.filter((item) => item?.taskId && item?.status !== 'SUCCEEDED' && item?.status !== 'FAILED')
|
||||||
|
.map((item) => item.taskId)
|
||||||
|
if (taskIdList.length > 0) {
|
||||||
|
getGenerateTime = setTimeout(() => {
|
||||||
|
getRequestOutfitList(taskIdList)
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const styleListInit = () => {
|
||||||
|
dataDom.styleListVue.init(data.select)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 useStreamChat,在流式请求成功后执行原本的逻辑
|
||||||
|
const { fetchMessage, isGenerating } = useStreamChat()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// generateStore.clearProductData()
|
||||||
|
// if(!data.styleList[0]?.id)getRequestOutfitList(0)
|
||||||
|
if (getGenerateTime) clearTimeout(getGenerateTime)
|
||||||
|
|
||||||
|
// 检查是否有从 dressfor 传递过来的消息
|
||||||
|
const message = query.value.message as string
|
||||||
|
const sessionId = query.value.sessionId as string
|
||||||
|
|
||||||
|
if (message && sessionId) {
|
||||||
|
// 有消息,说明是从 dressfor 跳转过来的,先发起流式请求
|
||||||
|
generateStore.setSessionId(sessionId)
|
||||||
|
fetchMessage(message, sessionId)
|
||||||
|
.then(() => {
|
||||||
|
console.log('对话请求完成')
|
||||||
|
// 清除 URL 参数避免返回时再次触发
|
||||||
|
router.replace({ path: '/workshop/selectStyle' })
|
||||||
|
// 开始生成outfit
|
||||||
|
requestOutfit({ num: 4 })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 错误处理
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 原本的逻辑
|
||||||
|
const taskIdList = data.styleList
|
||||||
|
.filter((item) => item?.taskId && item?.status !== 'SUCCEEDED')
|
||||||
|
.map((item) => item.taskId)
|
||||||
|
if (data.select.status == 'SUCCEEDED' && taskIdList.length == 0) {
|
||||||
|
return
|
||||||
|
} else if (!data.select?.taskId) {
|
||||||
|
requestOutfit({ num: 4 })
|
||||||
|
} else if (data.select.status != 'SUCCEEDED' || taskIdList.length > 0) {
|
||||||
|
if (data.select.status != 'SUCCEEDED') isLoading.value = true
|
||||||
|
getRequestOutfitList(taskIdList)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (getGenerateTime) clearTimeout(getGenerateTime)
|
||||||
|
})
|
||||||
|
defineExpose({})
|
||||||
|
const { select } = toRefs(data)
|
||||||
|
const { styleListVue } = toRefs(dataDom)
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="selectStyle">
|
||||||
|
<div class="text">
|
||||||
|
<div class="title">Outfit Result</div>
|
||||||
|
<div class="info">Refine your Look</div>
|
||||||
|
</div>
|
||||||
|
<div class="selectContent">
|
||||||
|
<!-- {{ select }} -->
|
||||||
|
<div class="imgBox">
|
||||||
|
<img :src="select.path" alt="" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!isHistoryFlow" class="chooseMore" @click.stop="styleListInit">
|
||||||
|
<gradientButton>
|
||||||
|
<template #content>
|
||||||
|
<div class="text">Choose More</div>
|
||||||
|
</template>
|
||||||
|
</gradientButton>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div class="btn" v-else>
|
||||||
|
<div class="like" @click.stop="setLikeStyle(select.isLike)">
|
||||||
|
<SvgIcon :name="`love_${select.isLike ? 1 : 0}`" size="35" />
|
||||||
|
</div>
|
||||||
|
<div class="down" @click.stop="setDownload()">
|
||||||
|
<SvgIcon name="download" size="35" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<div class="btnItem style1" @click.stop="updateStyle()">
|
||||||
|
<gradientButton>
|
||||||
|
<template #content>
|
||||||
|
<div class="text">
|
||||||
|
<span class="icon">
|
||||||
|
<SvgIcon name="reTry" size="40" />
|
||||||
|
</span>
|
||||||
|
Re-try
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</gradientButton>
|
||||||
|
</div>
|
||||||
|
<div class="btnItem style2" @click.stop="toProduct">Continue</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="footer placeholder"></div> -->
|
||||||
|
<div class="loading-container" v-if="isGenerating || isLoading">
|
||||||
|
<GenerateLoading :title="loadingTitle" />
|
||||||
|
</div>
|
||||||
|
<StyleListDom ref="styleListVue"></StyleListDom>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.header-title {
|
||||||
|
// --header-title-background: #f6f6f6;
|
||||||
|
}
|
||||||
|
.loading-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 2;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.selectStyle {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
// height: 100%;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
overflow: hidden;
|
||||||
|
> .text {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8.5rem;
|
||||||
|
margin-bottom: 8.5rem;
|
||||||
|
> .title {
|
||||||
|
font-family: satoshiBold;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 8.6rem;
|
||||||
|
line-height: 124%;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
> .info {
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 124%;
|
||||||
|
margin-top: 3.2rem;
|
||||||
|
color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.selectContent {
|
||||||
|
// padding: 0 4rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 73.7rem;
|
||||||
|
margin-bottom: 19rem;
|
||||||
|
> .imgBox {
|
||||||
|
height: 73.7rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 5.6rem;
|
||||||
|
> img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .chooseMore {
|
||||||
|
--borderRadius: 5.4rem;
|
||||||
|
--borderWidth: 2px;
|
||||||
|
width: 24.8rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
height: 7.6rem;
|
||||||
|
.text {
|
||||||
|
font-size: 3.1rem;
|
||||||
|
color: #000;
|
||||||
|
font-family: satoshiMedium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 2rem;
|
||||||
|
> div {
|
||||||
|
color: #000;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 7rem;
|
||||||
|
height: 7rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: #fff;
|
||||||
|
&:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .btn {
|
||||||
|
display: flex;
|
||||||
|
gap: 6.6rem;
|
||||||
|
justify-content: center;
|
||||||
|
> div {
|
||||||
|
border-radius: 0.96rem;
|
||||||
|
width: 33.7rem;
|
||||||
|
font-size: 4.8rem;
|
||||||
|
font-family: satoshiMedium;
|
||||||
|
line-height: 9.2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
&.style1 {
|
||||||
|
--borderRadius: 0.96rem;
|
||||||
|
--borderWidth: 2px;
|
||||||
|
.text {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
> .icon {
|
||||||
|
left: 4rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.style2 {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btnItem .text {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
121
src/views/Workshop/selectStyle/styleList.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||||
|
import SelectItem from "@/components/selectStyle/selectItem.vue";
|
||||||
|
import { useGenerateStore, } from '@/stores'
|
||||||
|
//const props = defineProps({
|
||||||
|
//})
|
||||||
|
//const emit = defineEmits([
|
||||||
|
//])
|
||||||
|
const generateStore = useGenerateStore()
|
||||||
|
|
||||||
|
|
||||||
|
let data = reactive({
|
||||||
|
showStyleList:false,
|
||||||
|
list:computed(()=>generateStore.styleList),
|
||||||
|
selectStyle:computed(()=>generateStore.style),
|
||||||
|
oldSelectStyle:{} as any,
|
||||||
|
})
|
||||||
|
|
||||||
|
const close = ()=>{
|
||||||
|
showStyleList.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = (item)=>{
|
||||||
|
data.showStyleList = true
|
||||||
|
data.oldSelectStyle = JSON.parse(JSON.stringify(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = ()=>{
|
||||||
|
if(data.selectStyle.id != data.oldSelectStyle.id){
|
||||||
|
data.selectStyle.id = data.oldSelectStyle.id
|
||||||
|
data.selectStyle.path = data.oldSelectStyle.path
|
||||||
|
data.selectStyle.status = data.oldSelectStyle.status
|
||||||
|
data.selectStyle.taskId = data.oldSelectStyle.taskId
|
||||||
|
data.selectStyle.isLike = false
|
||||||
|
}
|
||||||
|
generateStore.clearTryOn()
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const setStyle = (item)=>{
|
||||||
|
data.oldSelectStyle = item
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
})
|
||||||
|
defineExpose({init})
|
||||||
|
const {showStyleList,list,oldSelectStyle} = toRefs(data);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<van-popup
|
||||||
|
class="user-popup"
|
||||||
|
v-model:show="showStyleList"
|
||||||
|
round
|
||||||
|
position="bottom"
|
||||||
|
teleport="body"
|
||||||
|
>
|
||||||
|
<div class="profile">
|
||||||
|
<div class="header">
|
||||||
|
<span class="title">Outfit Result</span>
|
||||||
|
<van-icon name="cross" class="close" @click="close" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<SelectItem :selectList="list" :select="oldSelectStyle" @selectItem="setStyle"></SelectItem>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div @click="confirm">Confirm</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-popup>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.van-popup {
|
||||||
|
max-height: 90%;
|
||||||
|
--van-popup-round-radius: 7.8rem;
|
||||||
|
}
|
||||||
|
.profile {
|
||||||
|
margin: 11.1rem 0 5.6rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
> .header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 8rem 0 5.2rem;
|
||||||
|
margin-bottom: 8rem;
|
||||||
|
> .title {
|
||||||
|
font-family: satoshiBold;
|
||||||
|
font-size: 4.6rem;
|
||||||
|
color: #181725;
|
||||||
|
}
|
||||||
|
> .close {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 5rem;
|
||||||
|
color: #a1a1a1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .content{
|
||||||
|
width: 96.2rem;
|
||||||
|
}
|
||||||
|
> .bottom{
|
||||||
|
margin: 3rem 0 4rem;
|
||||||
|
width: 100%;
|
||||||
|
> div{
|
||||||
|
width: 24.8rem;
|
||||||
|
line-height: 6.7rem;
|
||||||
|
font-family: satoshiMedium;
|
||||||
|
font-size: 3.6rem;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: .7rem;
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 4.2rem;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,10 +4,6 @@
|
|||||||
import { useGenerateStore } from '@/stores'
|
import { useGenerateStore } from '@/stores'
|
||||||
import { IsHistoryFlow } from '@/types/enum'
|
import { IsHistoryFlow } from '@/types/enum'
|
||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const query = computed(() => route.query)
|
const query = computed(() => route.query)
|
||||||
@@ -94,14 +90,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
bottom: 19.7rem;
|
bottom: 19.7rem;
|
||||||
> button {
|
> button {
|
||||||
width: 35rem;
|
margin: 0 7.5rem;
|
||||||
height: 8.3rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 0.7rem;
|
|
||||||
margin: 0 1.8rem;
|
|
||||||
&.sandblasted-blurred {
|
|
||||||
// border-width: 0.2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
const hGenerateStore = useHGenerateStore()
|
const hGenerateStore = useHGenerateStore()
|
||||||
|
|
||||||
const emit = defineEmits(['view-type'])
|
|
||||||
onMounted(() => {
|
|
||||||
emit('view-type', 1)
|
|
||||||
})
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const query = computed(() => route.query)
|
const query = computed(() => route.query)
|
||||||
@@ -146,11 +142,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
> button {
|
> button {
|
||||||
width: 35rem;
|
|
||||||
height: 8.3rem;
|
|
||||||
margin: 0 5rem;
|
margin: 0 5rem;
|
||||||
// border-radius: 4.3rem;
|
|
||||||
border-width: 0.25rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const setTl1 = ()=>{
|
|||||||
nextTick(()=>{
|
nextTick(()=>{
|
||||||
let el = dotBox.value
|
let el = dotBox.value
|
||||||
let width = el.offsetWidth + el.parentElement.offsetWidth
|
let width = el.offsetWidth + el.parentElement.offsetWidth
|
||||||
let time = el.parentElement.offsetWidth / 35
|
let time = el.parentElement.offsetWidth / el.offsetWidth / 2
|
||||||
console.log(time,width)
|
console.log(time,width)
|
||||||
tl1 = gsap.timeline();
|
tl1 = gsap.timeline();
|
||||||
tl1.to(el,time,
|
tl1.to(el,time,
|
||||||
@@ -113,7 +113,7 @@ onMounted(() => {
|
|||||||
.loading-image {
|
.loading-image {
|
||||||
width: 36.4rem;
|
width: 36.4rem;
|
||||||
height: 36.4rem;
|
height: 36.4rem;
|
||||||
animation: rotate 1s linear infinite;
|
animation: rotate 1.5s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-shadow {
|
.loading-shadow {
|
||||||
@@ -136,7 +136,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.loading-dot{
|
.loading-dot{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
aspect-ratio: 1/1;
|
aspect-ratio: 1.2/1;
|
||||||
background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent);
|
background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -146,11 +146,14 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes rotate {
|
@keyframes rotate {
|
||||||
from {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: translateY(0px);
|
||||||
}
|
}
|
||||||
to {
|
50% {
|
||||||
transform: rotate(360deg);
|
transform: translateY(-100px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -197,7 +197,9 @@ const startRecording = () => {
|
|||||||
// 检查浏览器支持
|
// 检查浏览器支持
|
||||||
if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) {
|
if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) {
|
||||||
// alert('您的浏览器不支持语音识别功能')
|
// alert('您的浏览器不支持语音识别功能')
|
||||||
showToast('Your browser does not support speech recognition, please try again with another browser')
|
showToast(
|
||||||
|
'Your browser does not support speech recognition, please try again with another browser'
|
||||||
|
)
|
||||||
isRecording.value = false
|
isRecording.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -296,7 +298,7 @@ const stopRecording = () => {
|
|||||||
.shortcut-item {
|
.shortcut-item {
|
||||||
font-size: 4.2rem;
|
font-size: 4.2rem;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
font-family: 'robotoRegular';
|
font-family: 'satoshiMedium';
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
height: 8.1rem;
|
height: 8.1rem;
|
||||||
line-height: 8.1rem;
|
line-height: 8.1rem;
|
||||||
@@ -351,8 +353,8 @@ const stopRecording = () => {
|
|||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
font-family: 'robotoRegular';
|
font-family: 'satoshiMedium';
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
line-height: 4.8rem; /* 设置行高等于实际渲染高度,实现垂直居中 */
|
line-height: 4.8rem; /* 设置行高等于实际渲染高度,实现垂直居中 */
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: #000;
|
color: #000;
|
||||||
@@ -361,8 +363,8 @@ const stopRecording = () => {
|
|||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: #888;
|
color: #888;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
font-family: 'robotoRegular';
|
font-family: 'satoshiMedium';
|
||||||
word-spacing: -5px;
|
word-spacing: -5px;
|
||||||
line-height: 4.8rem;
|
line-height: 4.8rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
<div class="chat-message" :class="{ 'user-message': isMyself, 'ai-message': !isMyself }">
|
<div class="chat-message" :class="{ 'user-message': isMyself, 'ai-message': !isMyself }">
|
||||||
<!-- AI消息显示头像 -->
|
<!-- AI消息显示头像 -->
|
||||||
<div v-if="!isMyself" class="chat-avatar">
|
<div v-if="!isMyself" class="chat-avatar">
|
||||||
<img src="@/assets/images/asistant.png" alt="AI Avatar" />
|
<img class="avatar" :src="thumb" alt="AI Avatar" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 消息内容 -->
|
<!-- 消息内容 -->
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
<div class="message-text" :class="{ streaming: isStreaming }">
|
<div class="message-text" :class="{ streaming: isStreaming }">
|
||||||
<div v-html="content"></div>
|
<div v-html="content"></div>
|
||||||
<span v-if="isStreaming" class="streaming-cursor">|</span>
|
<!-- <span v-if="isStreaming" class="streaming-cursor">|</span> -->
|
||||||
|
<WaveLoading v-if="isStreaming" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div v-if="!isMyself" class="message-actions flex">
|
<!-- <div v-if="!isMyself" class="message-actions flex">
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
@@ -27,9 +28,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
|
import WaveLoading from '@/components/WaveLoading.vue'
|
||||||
|
import { useUserInfoStore } from '@/stores'
|
||||||
|
|
||||||
const md = new MarkdownIt()
|
const md = new MarkdownIt()
|
||||||
|
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const thumb = computed(() => {
|
||||||
|
return userInfoStore.state.generateParams.stylistImage
|
||||||
|
})
|
||||||
|
|
||||||
// 定义消息类型
|
// 定义消息类型
|
||||||
interface ChatMessage {
|
interface ChatMessage {
|
||||||
sessionId: string | number
|
sessionId: string | number
|
||||||
@@ -95,13 +103,15 @@ const actionList: ActionItem[] = [
|
|||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
color: #000;
|
||||||
.message-text {
|
.message-text {
|
||||||
font-size: 4.2rem;
|
font-size: 4rem;
|
||||||
font-family: 'robotoRegular';
|
font-family: 'satoshiMedium';
|
||||||
line-height: 121%;
|
line-height: 121%;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
padding: 3.43rem 4.35rem;
|
padding: 3.43rem 4.35rem;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.user-message {
|
&.user-message {
|
||||||
@@ -126,6 +136,10 @@ const actionList: ActionItem[] = [
|
|||||||
color: #000;
|
color: #000;
|
||||||
border-radius: 0 2rem 2rem 2rem;
|
border-radius: 0 2rem 2rem 2rem;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
:deep(strong){
|
||||||
|
font-family: 'satoshiBold';
|
||||||
|
font-size: 4.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,16 +147,17 @@ const actionList: ActionItem[] = [
|
|||||||
|
|
||||||
.chat-avatar {
|
.chat-avatar {
|
||||||
width: 7.8rem;
|
width: 7.8rem;
|
||||||
height: 7.4rem;
|
height: 7.8rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 1.88rem;
|
margin-right: 1.88rem;
|
||||||
border: .2rem solid #000;
|
overflow: hidden;
|
||||||
padding: 1.4rem;
|
// border: 0.2rem solid #000;
|
||||||
|
// padding: 1.4rem;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 4.9rem;
|
width: 100%;
|
||||||
height: 4.9rem;
|
// height: 100%;
|
||||||
border-radius: 50%;
|
// border-radius: 50%;
|
||||||
// object-fit: cover;
|
// object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="asistant-container flex flex-column">
|
<div class="asistant-container flex flex-column">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<HeaderTitle hasSetting styleType="3" />
|
<HeaderTitle hasSetting styleType="3" @clickProfile="handleClickProfile" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content flex-1">
|
<div class="content flex-1">
|
||||||
<NoticeList
|
<NoticeList
|
||||||
@@ -14,15 +14,17 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<InputArea @send-message="handleSendMessage" />
|
<InputArea @send-message="handleSendMessage" />
|
||||||
<div class="continue flex">
|
<div class="continue flex">
|
||||||
<div class="btn flex flex-center" @click="handleContinue">Continue</div>
|
<div class="btn flex flex-center" @click="handleContinue">Generate</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Profile ref="profileRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||||
import NoticeList from './components/NoticeList.vue'
|
import NoticeList from './components/NoticeList.vue'
|
||||||
import InputArea from './components/InputArea.vue'
|
import InputArea from './components/InputArea.vue'
|
||||||
|
import Profile from '../Workshop/profile.vue'
|
||||||
import { ref, onMounted, onUnmounted, onActivated } from 'vue'
|
import { ref, onMounted, onUnmounted, onActivated } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useUserInfoStore, useGenerateStore } from '@/stores'
|
import { useUserInfoStore, useGenerateStore } from '@/stores'
|
||||||
@@ -54,6 +56,11 @@ interface ChatMessage {
|
|||||||
self?: boolean
|
self?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const profileRef = ref<InstanceType<typeof Profile> | null>(null)
|
||||||
|
const handleClickProfile = () => {
|
||||||
|
profileRef.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
const noticeListRef = ref<NoticeListRef | null>(null)
|
const noticeListRef = ref<NoticeListRef | null>(null)
|
||||||
const messageList = ref<ChatMessage[]>([])
|
const messageList = ref<ChatMessage[]>([])
|
||||||
|
|
||||||
@@ -74,6 +81,8 @@ const sendPrefilledMessage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
console.log('1111111111111');
|
||||||
|
|
||||||
sessionId.value = Math.floor(Date.now() / 1000).toString()
|
sessionId.value = Math.floor(Date.now() / 1000).toString()
|
||||||
generateStore.setSessionId(sessionId.value)
|
generateStore.setSessionId(sessionId.value)
|
||||||
})
|
})
|
||||||
@@ -272,7 +281,7 @@ const handleContinue = () => {
|
|||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.asistant-container {
|
.asistant-container {
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +309,8 @@ const handleContinue = () => {
|
|||||||
border-radius: 0.7rem;
|
border-radius: 0.7rem;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
width: 24.6rem;
|
width: 24.6rem;
|
||||||
height: 5.9rem;
|
height: 6.7rem;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@
|
|||||||
<div class="login-page">
|
<div class="login-page">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="back-button" @click="goBack">
|
<div class="back-button" @click="goBack">
|
||||||
<SvgIcon name="left" size="50" color="#fff" />
|
<SvgIcon name="left" size="50" color="#fff" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">Log in.</div>
|
<div class="title">Staff Login.</div>
|
||||||
<p class="subtitle">Redefine the styling experience with AI.</p>
|
<p class="subtitle">
|
||||||
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p>
|
<span>Experience our personalised styling journey with</span>
|
||||||
|
<br />
|
||||||
|
<span>Lane Crawford.</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
@@ -32,7 +35,11 @@
|
|||||||
<img :src="google" class="google-icon" />
|
<img :src="google" class="google-icon" />
|
||||||
Sign in with Google
|
Sign in with Google
|
||||||
</div> -->
|
</div> -->
|
||||||
<GoogleLogin @googelLogin="handleGoogleLogin" ref="googleLoginRef" @click="clickGooleLogin"></GoogleLogin>
|
<GoogleLogin
|
||||||
|
@googelLogin="handleGoogleLogin"
|
||||||
|
ref="googleLoginRef"
|
||||||
|
@click="clickGooleLogin"
|
||||||
|
></GoogleLogin>
|
||||||
<div class="sign-up-button" @click="handleSignup">Don’t have an account? Sign Up</div>
|
<div class="sign-up-button" @click="handleSignup">Don’t have an account? Sign Up</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,7 +190,7 @@ const handleSignup = () => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
font-size: 3.4rem;
|
font-size: 3.4rem;
|
||||||
.c-svg{
|
.c-svg {
|
||||||
width: initial;
|
width: initial;
|
||||||
height: initial;
|
height: initial;
|
||||||
}
|
}
|
||||||
@@ -191,12 +198,14 @@ const handleSignup = () => {
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin-top: 1.42rem;
|
margin-top: 1.42rem;
|
||||||
padding-left: 15.5rem;
|
padding-left: 14.5rem;
|
||||||
|
padding-right: 14.3rem;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
.title {
|
.title {
|
||||||
font-size: 11rem;
|
font-size: 11rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
font-weight: 700;
|
||||||
margin-bottom: 0.8rem;
|
margin-bottom: 0.8rem;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: 'satoshiBold';
|
font-family: 'satoshiBold';
|
||||||
@@ -226,11 +235,7 @@ const handleSignup = () => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: calc(100% - 28.4rem);
|
width: calc(100% - 28.4rem);
|
||||||
height: 107.8rem;
|
height: 107.8rem;
|
||||||
background: radial-gradient(
|
background: radial-gradient(100% 100% at 0% 0%, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.2) 100%);
|
||||||
100% 100% at 0% 0%,
|
|
||||||
rgba(0, 0, 0, 0.4) 0%,
|
|
||||||
rgba(0, 0, 0, 0.2) 100%
|
|
||||||
);
|
|
||||||
backdrop-filter: blur(35px);
|
backdrop-filter: blur(35px);
|
||||||
-webkit-backdrop-filter: blur(35px);
|
-webkit-backdrop-filter: blur(35px);
|
||||||
-moz-backdrop-filter: blur(35px);
|
-moz-backdrop-filter: blur(35px);
|
||||||
|
|||||||
@@ -4,14 +4,17 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<!-- 返回按钮 -->
|
<!-- 返回按钮 -->
|
||||||
<div class="back-button" @click="goBack">
|
<div class="back-button" @click="goBack">
|
||||||
<img src="@/assets/images/arrow_left.png" class="back-icon" />
|
<SvgIcon name="left" size="50" color="#fff" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 标题区域 -->
|
<!-- 标题区域 -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">Log in.</div>
|
<div class="title">Staff Login.</div>
|
||||||
<p class="subtitle">Redefine the styling experience with AI.</p>
|
<p class="subtitle">
|
||||||
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p>
|
<span>Experience our personalised styling journey with</span>
|
||||||
|
<br />
|
||||||
|
<span>Lane Crawford.</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
@@ -130,9 +133,9 @@ const handleSuccess = (data: any) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
font-size: 3.4rem;
|
font-size: 3.4rem;
|
||||||
.back-icon {
|
.c-svg {
|
||||||
width: 2.83rem;
|
width: initial;
|
||||||
height: 3.47rem;
|
height: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
<div class="login-page">
|
<div class="login-page">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="back-button" @click="goBack">
|
<div class="back-button" @click="goBack">
|
||||||
<img src="@/assets/images/arrow_left.png" class="back-icon" />
|
<SvgIcon name="left" size="50" color="#fff" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">Sign up.</div>
|
<div class="title">Staff Sign up.</div>
|
||||||
<p class="subtitle">Redefine the styling experience with AI.</p>
|
<p class="subtitle">Start my personalised styling journey with Lane Crawford.</p>
|
||||||
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
@@ -30,7 +29,12 @@
|
|||||||
|
|
||||||
<button type="button" class="login-button" @click="handleConfirm">Sign Up</button>
|
<button type="button" class="login-button" @click="handleConfirm">Sign Up</button>
|
||||||
|
|
||||||
<GoogleLogin text="Sign up with Google" @googelLogin="handleGoogleSignup" ref="googleSignupRef" @click="clickGooleLogin" />
|
<GoogleLogin
|
||||||
|
text="Sign up with Google"
|
||||||
|
@googelLogin="handleGoogleSignup"
|
||||||
|
ref="googleSignupRef"
|
||||||
|
@click="clickGooleLogin"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,15 +187,16 @@ const handleGoogleSignup = async (accessToken: string) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
font-size: 3.4rem;
|
font-size: 3.4rem;
|
||||||
.back-icon {
|
.c-svg {
|
||||||
width: 2.83rem;
|
width: initial;
|
||||||
height: 3.47rem;
|
height: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
margin-top: 1.42rem;
|
margin-top: 1.42rem;
|
||||||
padding-left: 15.5rem;
|
padding-left: 14.5rem;
|
||||||
|
padding-right: 14.3rem;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
.title {
|
.title {
|
||||||
@@ -281,7 +286,6 @@ const handleGoogleSignup = async (accessToken: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
<div class="welcome-page safe-area-top safe-area-bottom">
|
<div class="welcome-page safe-area-top safe-area-bottom">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
AI STYLING <br />
|
Styling <br />
|
||||||
ASSISTANT
|
Assistant
|
||||||
</div>
|
</div>
|
||||||
<p class="subtitle">Redefine the styling experience with AI.</p>
|
<p class="subtitle">
|
||||||
|
<span>Experience our personalised styling journey with</span>
|
||||||
|
<br>
|
||||||
|
<span> Lane Crawford.</span>
|
||||||
|
</p>
|
||||||
<div class="btn-container flex">
|
<div class="btn-container flex">
|
||||||
<div class="log btn" @click="goToLogin">Log in</div>
|
<div class="log btn" @click="goToLogin">Log in</div>
|
||||||
<div class="sign btn" @click="goToSignup">Sign up</div>
|
<div class="sign btn" @click="goToSignup">Sign up</div>
|
||||||
@@ -49,15 +53,17 @@ const goToLogin = () => {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin-bottom: 31.5rem;
|
margin-bottom: 31.5rem;
|
||||||
.title {
|
.title {
|
||||||
font-family: 'boskaRegular';
|
font-family: 'satoshiMedium';
|
||||||
line-height: 93%;
|
line-height: 120%;
|
||||||
letter-spacing: -0.02em;
|
font-size: 11rem;
|
||||||
|
// letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 3.2rem;
|
font-size: 3.2rem;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
margin: 3.2rem 0 6rem;
|
margin: 3.2rem 0 6rem;
|
||||||
list-style: 124%;
|
font-weight: 400;
|
||||||
|
line-height: 124%;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
}
|
}
|
||||||
.btn-container {
|
.btn-container {
|
||||||
|
|||||||
@@ -5,7 +5,11 @@
|
|||||||
>
|
>
|
||||||
<div class="setting flex flex-between">
|
<div class="setting flex flex-between">
|
||||||
<SvgIcon name="left" size="70" @click.stop="handleBack" />
|
<SvgIcon name="left" size="70" @click.stop="handleBack" />
|
||||||
<SvgIcon name="profile_white" size="55" @click="handleOpenProfile" />
|
<SvgIcon
|
||||||
|
:name="profileVisible ? 'profileFilledWhite' : 'profile_white'"
|
||||||
|
size="55"
|
||||||
|
@click="handleOpenProfile"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="pageMode === 'entry'">
|
<template v-if="pageMode === 'entry'">
|
||||||
<div class="content flex-1 flex flex-center flex-column">
|
<div class="content flex-1 flex flex-center flex-column">
|
||||||
@@ -25,8 +29,8 @@
|
|||||||
<div class="text">
|
<div class="text">
|
||||||
<div class="form-title">{{ formTitle }}</div>
|
<div class="form-title">{{ formTitle }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<p>Redefine the styling experience with AI.</p>
|
<p>Unlock personalized styling insights.</p>
|
||||||
<p>Use Styling Angel to speed up your fashion journey.</p>
|
<p>Enter a client profile to begin curating their next look with Styling Angel.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -58,19 +62,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<Profile ref="profileRef" @selected-customer="handleSelectCustomer" is-customer />
|
<Profile
|
||||||
|
ref="profileRef"
|
||||||
|
@change-visible="handleChangeVisible"
|
||||||
|
@selected-customer="handleSelectCustomer"
|
||||||
|
is-customer
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useGenerateStore, useUserInfoStore } from '@/stores'
|
import { useGenerateStore, useUserInfoStore } from '@/stores'
|
||||||
import { showToast } from 'vant'
|
import { showToast, closeToast } from 'vant'
|
||||||
import { customerCheckin, createCustomer, type CreateCustomerParams } from '@/api/workshop'
|
import { customerCheckin, createCustomer, type CreateCustomerParams } from '@/api/workshop'
|
||||||
import Profile from '../Workshop/profile.vue'
|
import Profile from '../Workshop/profile.vue'
|
||||||
import MyEvent from '@/utils/myEvent'
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
|
|
||||||
const profileRef = ref<typeof Profile>(null)
|
const profileRef = ref<typeof Profile>(null)
|
||||||
const handleOpenProfile = () => {
|
const handleOpenProfile = () => {
|
||||||
profileRef.value.open()
|
profileRef.value.open()
|
||||||
@@ -78,11 +86,12 @@ const handleOpenProfile = () => {
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
type PageMode = 'form' | 'entry' | 'create'
|
type PageMode = 'entry' | 'form' | 'create'
|
||||||
const pageMode = ref<PageMode>('entry')
|
const pageMode = ref<PageMode>('entry')
|
||||||
const formTitle = computed(() => {
|
const formTitle = computed(() => {
|
||||||
return pageMode.value === 'entry' ? 'Customer ID' : 'Create Profile'
|
return pageMode.value === 'entry' || pageMode.value === 'form' ? 'Customer ID' : 'Create Profile'
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleChangeMode = (mode: PageMode) => {
|
const handleChangeMode = (mode: PageMode) => {
|
||||||
@@ -96,57 +105,58 @@ const customerData = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
if (pageMode.value === 'form') {
|
if (loading.value) return
|
||||||
if (customerData.value.nickname === '') {
|
const nickname = (customerData.value.nickname || '').trim()
|
||||||
showToast({
|
const vipId = (customerData.value.vipId || '').trim()
|
||||||
message: 'please input the nickname'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
customerCheckin({ nickname: customerData.value.nickname }).then((res) => {
|
if (!nickname) {
|
||||||
useUserInfoStore().resetGenerateParams()
|
showToast({ message: 'please input the nickname' })
|
||||||
generateStore.setCustomerInfo(res)
|
return
|
||||||
router.push('/workshop/home')
|
}
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (customerData.value.vipId === '') {
|
|
||||||
showToast({
|
|
||||||
message: 'please input the VIP ID'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (customerData.value.nickname === '') {
|
|
||||||
showToast({
|
|
||||||
message: 'please input the nickname'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
createCustomer({
|
if (pageMode.value === 'create' && !vipId) {
|
||||||
nickname: customerData.value.nickname,
|
showToast({ message: 'please input the VIP ID' })
|
||||||
vipId: customerData.value.vipId
|
return
|
||||||
} as CreateCustomerParams).then((res) => {
|
}
|
||||||
showToast({
|
|
||||||
message: 'Customer created successfully'
|
loading.value = true
|
||||||
})
|
showToast({ message: 'Processing...', duration: 0, type: 'loading' })
|
||||||
handleBack()
|
try {
|
||||||
|
if (pageMode.value === 'create') {
|
||||||
|
await createCustomer({ nickname, vipId } as CreateCustomerParams)
|
||||||
|
showToast({ message: 'Customer created successfully' })
|
||||||
MyEvent.emit('update-customer-list')
|
MyEvent.emit('update-customer-list')
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const res = await customerCheckin({ nickname })
|
||||||
|
useUserInfoStore().resetGenerateParams()
|
||||||
|
generateStore.setCustomerInfo(res)
|
||||||
|
MyEvent.emit('clear-generate-state')
|
||||||
|
router.push('/workshop/home')
|
||||||
|
} catch (err: any) {
|
||||||
|
showToast({ message: err?.message || 'Operation failed' })
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
closeToast()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleShowPopup = (flag: Boolean) => {
|
const handleShowPopup = (flag: Boolean) => {
|
||||||
// showPopup.value = flag
|
// showPopup.value = flag
|
||||||
profileRef.value.openSwitchCustomerPopup(flag)
|
profileRef.value.handleShowPopup(flag,true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectCustomer = (value) => {
|
const handleSelectCustomer = (value) => {
|
||||||
if (value) {
|
if (value && pageMode.value === 'form') {
|
||||||
customerData.value.nickname = value.name
|
customerData.value.nickname = value.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const profileVisible = ref(false)
|
||||||
|
const handleChangeVisible = (visible: boolean) => {
|
||||||
|
profileVisible.value = visible
|
||||||
|
}
|
||||||
|
|
||||||
const handleBack = (e?: Event) => {
|
const handleBack = (e?: Event) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -179,7 +189,7 @@ const handleBack = (e?: Event) => {
|
|||||||
|
|
||||||
.setting {
|
.setting {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
padding: 3.17rem 4.9rem 0 8.4rem;
|
padding: 16.4rem 4.9rem 0 8.4rem;
|
||||||
font-size: 7rem;
|
font-size: 7rem;
|
||||||
.c-svg {
|
.c-svg {
|
||||||
width: initial;
|
width: initial;
|
||||||
@@ -213,6 +223,7 @@ const handleBack = (e?: Event) => {
|
|||||||
line-height: 8.3rem;
|
line-height: 8.3rem;
|
||||||
font-size: 4.8rem;
|
font-size: 4.8rem;
|
||||||
border: 0.2rem solid #fff;
|
border: 0.2rem solid #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dressfor-container flex">
|
<div class="dressfor-container flex">
|
||||||
<div class="content flex-1 flex flex-column">
|
<div class="content flex-1 flex flex-column">
|
||||||
<div class="loading-container flex flex-center">
|
<!-- 移除始终显示的 loading,改为按需显示 -->
|
||||||
|
<!-- <div class="loading-container flex flex-center">
|
||||||
<Icon class="icon-element" title="" />
|
<Icon class="icon-element" title="" />
|
||||||
</div>
|
</div> -->
|
||||||
<div class="text">
|
<!-- <div class="text">
|
||||||
What are you <br />
|
What are you <br />
|
||||||
dressing for?
|
dressing for?
|
||||||
|
</div> -->
|
||||||
|
<div class="slogan flex flex-center">
|
||||||
|
<img class="text" src="@/assets/images/dressfor.png" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="start-btn" @click="handleStart">Start</div> -->
|
<!-- <div class="start-btn" @click="handleStart">Start</div> -->
|
||||||
<div class="chatbox flex flex-center">
|
<!-- <div class="chatbox flex flex-center">
|
||||||
<div class="input-box flex">
|
<div class="input-box flex">
|
||||||
<div class="input-wrapper flex-1 flex">
|
<div class="input-wrapper flex-1 flex">
|
||||||
<input
|
<input
|
||||||
@@ -32,13 +36,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="send flex flex-center" @click="handleSendMessage">
|
<div class="send flex flex-center" @click="handleSendMessage">
|
||||||
<SvgIcon class="send-icon" name="send" size="26" color="#000000" />
|
<SvgIcon class="send-icon" name="send_bold" size="26" color="#6d6868" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="tag-container flex flex-column flex-center">
|
<div class="tag-container flex flex-column flex-center">
|
||||||
<div class="tag-list short flex flex-justify-center">
|
<div class="tag-list short flex">
|
||||||
<div
|
<div
|
||||||
class="tag-item"
|
class="tag-item"
|
||||||
|
:class="{ active: item === inputValue }"
|
||||||
v-for="item in tagListShort"
|
v-for="item in tagListShort"
|
||||||
:key="item"
|
:key="item"
|
||||||
@click="handleClickTag(item)"
|
@click="handleClickTag(item)"
|
||||||
@@ -46,33 +51,53 @@
|
|||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-list long flex flex-justify-center">
|
<!-- <div class="tag-list long flex flex-justify-center">
|
||||||
<div
|
<div
|
||||||
class="tag-item"
|
class="tag-item"
|
||||||
v-for="item in tagListLong"
|
v-for="item in tagListLong"
|
||||||
|
:class="{ active: item === inputValue }"
|
||||||
:key="item"
|
:key="item"
|
||||||
@click="handleClickTag(item)"
|
@click="handleClickTag(item)"
|
||||||
>
|
>
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onUnmounted, nextTick, watch } from 'vue'
|
import { ref, onUnmounted, nextTick, watch } from 'vue'
|
||||||
|
import { useUserInfoStore, useGenerateStore } from '@/stores'
|
||||||
import { showToast, closeToast } from 'vant'
|
import { showToast, closeToast } from 'vant'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||||
import FooterNavigation from '@/components/FooterNavigation.vue'
|
import FooterNavigation from '@/components/FooterNavigation.vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import AudioVisualizer from '@/views/asistant/components/AudioVisualizer.vue'
|
import AudioVisualizer from '@/views/asistant/components/AudioVisualizer.vue'
|
||||||
import Icon from '../asistant/components/GenerateLoading.vue'
|
import Icon from '../asistant/components/GenerateLoading.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const generateStore = useGenerateStore()
|
||||||
|
const tagListShort = [
|
||||||
|
'Casual',
|
||||||
|
'Formal',
|
||||||
|
'Activewear',
|
||||||
|
'Resort',
|
||||||
|
'Business casual',
|
||||||
|
'Evening',
|
||||||
|
'Outdoor',
|
||||||
|
'Business',
|
||||||
|
'Cocktail',
|
||||||
|
|
||||||
const tagListShort = ['Silk Slip Dress', 'Business Casual', 'Suggest Shoe Styles']
|
'Bridal',
|
||||||
const tagListLong = ['Linen Suit For Summer Gaka', 'Recomment Evening Bags']
|
'Festival',
|
||||||
|
'Travel',
|
||||||
|
'Athleisure',
|
||||||
|
'Beach',
|
||||||
|
'Ski'
|
||||||
|
]
|
||||||
|
// const tagListLong = ['Linen Suit For Summer Gaka', 'Recomment Evening Bags']
|
||||||
|
|
||||||
const inputValue = ref('')
|
const inputValue = ref('')
|
||||||
const isRecording = ref(false)
|
const isRecording = ref(false)
|
||||||
@@ -101,11 +126,6 @@ const handleSendMessage = () => {
|
|||||||
showToast('Please enter a message')
|
showToast('Please enter a message')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
router.push({
|
|
||||||
path: '/asistant',
|
|
||||||
query: message ? { message } : undefined
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickAudio = () => {
|
const handleClickAudio = () => {
|
||||||
@@ -195,6 +215,16 @@ const stopRecording = () => {
|
|||||||
|
|
||||||
const handleClickTag = (tag: string) => {
|
const handleClickTag = (tag: string) => {
|
||||||
inputValue.value = tag
|
inputValue.value = tag
|
||||||
|
const sessionId = Math.floor(Date.now() / 1000).toString()
|
||||||
|
generateStore.setSessionId(sessionId.value)
|
||||||
|
// 直接跳转到 selectStyle 页面,传递消息和 sessionId
|
||||||
|
router.push({
|
||||||
|
path: '/workshop/selectStyle',
|
||||||
|
query: {
|
||||||
|
message: tag,
|
||||||
|
sessionId
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -218,7 +248,21 @@ onUnmounted(() => {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
padding: 6rem 0 0 0;
|
padding: 15.9rem 0 0 0;
|
||||||
|
.loading-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 2;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
.loading-container {
|
.loading-container {
|
||||||
:deep(.loading-image) {
|
:deep(.loading-image) {
|
||||||
@@ -227,94 +271,88 @@ onUnmounted(() => {
|
|||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
:deep(.loading-shadow) {
|
:deep(.loading-shadow) {
|
||||||
|
background-color: #000;
|
||||||
width: 9.2rem;
|
width: 9.2rem;
|
||||||
height: 2.4rem;
|
height: 2.4rem;
|
||||||
filter: blur(6px);
|
filter: blur(6px);
|
||||||
opacity: 0.5;
|
opacity: 0.2;
|
||||||
margin: 2.4rem 0 0;
|
margin: 2.4rem 0 0;
|
||||||
// background-color: #d9d9d9;
|
// background-color: #d9d9d9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.text {
|
.text {
|
||||||
font-family: 'satoshiBold';
|
padding-top: 8.9rem;
|
||||||
font-size: 9.6rem;
|
padding-bottom: 7.7rem;
|
||||||
text-align: center;
|
width: 60rem;
|
||||||
padding-top: 9rem;
|
|
||||||
padding-bottom: 14rem;
|
|
||||||
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.12;
|
|
||||||
background: #b3b3b3;
|
|
||||||
background: linear-gradient(120deg, #b3b3b3 1%, rgba(0, 0, 0, 0) 48%),
|
|
||||||
linear-gradient(344deg, #b3b3b2 16%, #000000 66%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
background-clip: text;
|
|
||||||
}
|
|
||||||
.chatbox {
|
|
||||||
height: 9.3rem;
|
|
||||||
// background-color: #fff;
|
|
||||||
column-gap: 2.29rem;
|
|
||||||
.input-box {
|
|
||||||
width: 59.8rem;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #efefef;
|
|
||||||
// border: 2px solid #5f5f5f;
|
|
||||||
border-radius: 1rem;
|
|
||||||
color: #222222;
|
|
||||||
font-size: 3.2rem;
|
|
||||||
font-family: 'satoshiRegular';
|
|
||||||
padding: 0 2.6rem;
|
|
||||||
column-gap: 2.6rem;
|
|
||||||
overflow: hidden;
|
|
||||||
.input-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.recording-visualizer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
:deep(.audio-visualizer) {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
:deep(.visualizer-container) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.input-item {
|
|
||||||
// width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
background-color: #efefef;
|
|
||||||
}
|
|
||||||
.audio-icon {
|
|
||||||
width: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.send {
|
|
||||||
width: 7.6rem;
|
|
||||||
height: 7.6rem;
|
|
||||||
background-color: #efefef;
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// .chatbox {
|
||||||
|
// height: 9.3rem;
|
||||||
|
// // background-color: #fff;
|
||||||
|
// column-gap: 2.29rem;
|
||||||
|
// .input-box {
|
||||||
|
// width: 59.8rem;
|
||||||
|
// height: 100%;
|
||||||
|
// background-color: #efefef;
|
||||||
|
// // border: 2px solid #5f5f5f;
|
||||||
|
// border-radius: 1rem;
|
||||||
|
// color: #222222;
|
||||||
|
// font-size: 3.2rem;
|
||||||
|
// font-family: 'satoshiRegular';
|
||||||
|
// padding: 0 2.6rem;
|
||||||
|
// column-gap: 2.6rem;
|
||||||
|
// overflow: hidden;
|
||||||
|
// .input-wrapper {
|
||||||
|
// overflow: hidden;
|
||||||
|
// }
|
||||||
|
// .recording-visualizer {
|
||||||
|
// display: flex;
|
||||||
|
// align-items: center;
|
||||||
|
// height: 100%;
|
||||||
|
// :deep(.audio-visualizer) {
|
||||||
|
// width: 100%;
|
||||||
|
// padding: 0;
|
||||||
|
// }
|
||||||
|
// :deep(.visualizer-container) {
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .input-item {
|
||||||
|
// // width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// outline: none;
|
||||||
|
// border: none;
|
||||||
|
// background-color: #efefef;
|
||||||
|
// }
|
||||||
|
// .audio-icon {
|
||||||
|
// width: initial;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .send {
|
||||||
|
// width: 7.6rem;
|
||||||
|
// height: 7.6rem;
|
||||||
|
// background-color: #efefef;
|
||||||
|
// border-radius: 1rem;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
.tag-container {
|
.tag-container {
|
||||||
row-gap: 3.1rem;
|
// padding: 5.7rem 0;
|
||||||
padding-top: 5.7rem;
|
margin: 0 auto;
|
||||||
|
width: 65.8rem;
|
||||||
.tag-list {
|
.tag-list {
|
||||||
color: #000;
|
color: #000;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
&.short {
|
justify-content: space-between;
|
||||||
column-gap: 1.91rem;
|
align-content: flex-start;
|
||||||
}
|
gap: 3rem;
|
||||||
&.long {
|
&::after {
|
||||||
column-gap: 3.1rem;
|
content: '';
|
||||||
padding-left: 2.1rem;
|
flex-grow: 1;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-item {
|
.tag-item {
|
||||||
height: 6.8rem;
|
height: 6.8rem;
|
||||||
|
min-width: 12rem;
|
||||||
line-height: 6.8rem;
|
line-height: 6.8rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
@@ -323,6 +361,9 @@ onUnmounted(() => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 4.6rem;
|
border-radius: 4.6rem;
|
||||||
padding: 0 2.15rem;
|
padding: 0 2.15rem;
|
||||||
|
&.active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">CHOOSE YOUR STYLIST</div>
|
<div class="title">Choose Stylist.</div>
|
||||||
|
<div class="sub-title">What style are you looking for?</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carousel-container" v-show="!showVideo">
|
<div class="carousel-container" v-show="!showVideo">
|
||||||
@@ -34,8 +35,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Continue按钮 -->
|
<!-- Continue按钮 -->
|
||||||
<div class="continue-button" @click="handleContinue" v-if="!$route.query?.demo">Continue</div>
|
<button
|
||||||
<van-dialog
|
class="sandblasted-blurred continue-button flex flex-center"
|
||||||
|
@click="handleContinue"
|
||||||
|
v-if="!$route.query?.demo"
|
||||||
|
>
|
||||||
|
<span>Continue</span>
|
||||||
|
</button>
|
||||||
|
<!-- <van-dialog
|
||||||
class="video-dialog"
|
class="video-dialog"
|
||||||
:show-confirm-button="false"
|
:show-confirm-button="false"
|
||||||
:show-cancel-button="false"
|
:show-cancel-button="false"
|
||||||
@@ -47,7 +54,7 @@
|
|||||||
<van-icon name="cross" class="close-icon" />
|
<van-icon name="cross" class="close-icon" />
|
||||||
</div>
|
</div>
|
||||||
<Video ref="videoRef" />
|
<Video ref="videoRef" />
|
||||||
</van-dialog>
|
</van-dialog> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -57,9 +64,15 @@ import { useRouter } from 'vue-router'
|
|||||||
import Video from './components/Video.vue'
|
import Video from './components/Video.vue'
|
||||||
import { useUserInfoStore } from '@/stores'
|
import { useUserInfoStore } from '@/stores'
|
||||||
import male from '@/assets/images/male.png'
|
import male from '@/assets/images/male.png'
|
||||||
|
import maleThumb from '@/assets/images/male_thumb.png'
|
||||||
import female from '@/assets/images/female.png'
|
import female from '@/assets/images/female.png'
|
||||||
|
import femaleThumb from '@/assets/images/female_thumb.png'
|
||||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||||
import FooterNavigation from '@/components/FooterNavigation.vue'
|
import FooterNavigation from '@/components/FooterNavigation.vue'
|
||||||
|
import mini from '@/assets/images/mini.jpg'
|
||||||
|
import miniThumb from '@/assets/images/mini_thumb.jpg'
|
||||||
|
import Crystal from '@/assets/images/Crystal.jpg'
|
||||||
|
import CrystalThumb from '@/assets/images/Crystal_thumb.jpg'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userInfoStore = useUserInfoStore()
|
const userInfoStore = useUserInfoStore()
|
||||||
@@ -68,30 +81,34 @@ const stylists = ref<any[]>([
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
value: 'crystal',
|
value: 'crystal',
|
||||||
name: 'Vera Lo',
|
name: 'Crystal',
|
||||||
description: 'Contemporary, Classic, Simple Silhouettes, Statement Pieces',
|
description: 'Contemporary, Classic, Simple Silhouettes, Statement Pieces',
|
||||||
image: female
|
image: Crystal,
|
||||||
|
thumb: CrystalThumb
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
value: 'mini',
|
value: 'mini',
|
||||||
name: 'Sarah Chen',
|
name: 'Mini',
|
||||||
description: 'Modern, Edgy, Bold Colors, Street Style',
|
description: 'Modern, Edgy, Bold Colors, Street Style',
|
||||||
image: male
|
image: mini,
|
||||||
|
thumb: miniThumb
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
value: 'crystal',
|
value: 'vera',
|
||||||
name: 'Emma Wilson',
|
name: 'Vera',
|
||||||
description: 'Elegant, Feminine, Vintage Inspired, Soft Tones',
|
description: 'Elegant, Feminine, Vintage Inspired, Soft Tones',
|
||||||
image: female
|
image: female,
|
||||||
|
thumb: femaleThumb
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
value: 'mini',
|
value: 'edi',
|
||||||
name: 'Alex Johnson',
|
name: 'Edi',
|
||||||
description: 'Minimalist, Professional, Neutral Palette, Clean Lines',
|
description: 'Minimalist, Professional, Neutral Palette, Clean Lines',
|
||||||
image: male
|
image: male,
|
||||||
|
thumb: maleThumb
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
const currentChoosed = ref(1)
|
const currentChoosed = ref(1)
|
||||||
@@ -119,20 +136,22 @@ const handleClickStylist = (item: any) => {
|
|||||||
|
|
||||||
const handleContinue = () => {
|
const handleContinue = () => {
|
||||||
const generateParams = userInfoStore.getGenerateParams()
|
const generateParams = userInfoStore.getGenerateParams()
|
||||||
generateParams.stylist =
|
const selected = stylists.value.find((item) => item.id === currentChoosed.value)
|
||||||
stylists.value.find((item) => item.id === currentChoosed.value)?.value || ''
|
generateParams.stylist = selected?.value
|
||||||
|
generateParams.stylistImage = selected?.thumb
|
||||||
|
|
||||||
userInfoStore.setGenerateParams(generateParams)
|
userInfoStore.setGenerateParams(generateParams)
|
||||||
|
|
||||||
router.push('/workshop/stylist/sex')
|
router.push('/workshop/stylist/sex')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听showVideo变化,关闭时暂停视频
|
// 监听showVideo变化,关闭时暂停视频
|
||||||
watch(showVideo, (newValue) => {
|
// watch(showVideo, (newValue) => {
|
||||||
if (!newValue && videoRef.value) {
|
// if (!newValue && videoRef.value) {
|
||||||
videoRef.value.pause()
|
// videoRef.value.pause()
|
||||||
videoRef.value.reset()
|
// videoRef.value.reset()
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@@ -157,18 +176,24 @@ watch(showVideo, (newValue) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
padding-top: 6rem;
|
padding-top: 10rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 4rem;
|
margin-bottom: 4rem;
|
||||||
.title {
|
.title {
|
||||||
font-size: 15rem;
|
font-size: 11rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: white;
|
color: white;
|
||||||
font-family: 'boskaRegular';
|
|
||||||
line-height: 96%;
|
line-height: 96%;
|
||||||
|
font-family: 'satoshiBold';
|
||||||
|
letter-spacing: -0.04rem;
|
||||||
|
margin-bottom: 3.2rem;
|
||||||
|
}
|
||||||
|
.sub-title {
|
||||||
|
font-family: 'satoshiRegular';
|
||||||
|
font-size: 4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,9 +304,9 @@ watch(showVideo, (newValue) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.continue-button {
|
button.sandblasted-blurred.continue-button {
|
||||||
height: 5.9rem;
|
height: 6.7rem;
|
||||||
box-sizing: border-box;
|
width: 24.6rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 6.4rem;
|
bottom: 6.4rem;
|
||||||
right: 7.6rem;
|
right: 7.6rem;
|
||||||
@@ -291,11 +316,9 @@ watch(showVideo, (newValue) => {
|
|||||||
color: white;
|
color: white;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
display: flex;
|
box-sizing: border-box;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.van-overlay) {
|
:deep(.van-overlay) {
|
||||||
|
|||||||
@@ -55,14 +55,18 @@ const handleSelect = (value: string) => {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
padding: 6rem 12.4rem 0 8.5rem;
|
padding: 6rem 12.4rem 0 8.5rem;
|
||||||
.text {
|
.text {
|
||||||
font-family: 'robotoBold';
|
font-family: 'satoshiBold';
|
||||||
font-size: 13rem;
|
font-weight: 700;
|
||||||
|
font-size: 11rem;
|
||||||
line-height: 106%;
|
line-height: 106%;
|
||||||
|
letter-spacing: -0.02rem;
|
||||||
|
margin-bottom: 4.6rem;
|
||||||
}
|
}
|
||||||
.desc {
|
.desc {
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
font-size: 6.4rem;
|
font-size: 6rem;
|
||||||
line-height: 132%;
|
line-height: 132%;
|
||||||
|
letter-spacing: 0.02rem;
|
||||||
}
|
}
|
||||||
.select-list {
|
.select-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -76,7 +80,7 @@ const handleSelect = (value: string) => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
font-size: 4.8rem;
|
font-size: 4.8rem;
|
||||||
width: 29.7rem;
|
width: 35rem;
|
||||||
height: 8.3rem;
|
height: 8.3rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0', // 允许局域网内的IP访问
|
host: '0.0.0.0', // 允许局域网内的IP访问
|
||||||
port: 8060, // 根据环境设置端口
|
port: 8066, // 根据环境设置端口
|
||||||
open: false, // 自动打开浏览器
|
open: false, // 自动打开浏览器
|
||||||
strictPort: true, // 如果端口已被占用,则尝试下一个可用端口
|
strictPort: true, // 如果端口已被占用,则尝试下一个可用端口
|
||||||
hmr: {
|
hmr: {
|
||||||
|
|||||||