Compare commits
53 Commits
55ed8474be
...
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 |
@@ -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 |
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/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 |
@@ -5,9 +5,8 @@
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
title: { type: String, default: 'STYLING ASSISTANT' },
|
title: { type: String, default: 'STYLING ASSISTANT' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['clickReturn', 'clickProfile'])
|
const emit = defineEmits(['clickReturn', 'clickProfile'])
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
const handleProfileVisibleChange = (visible) => {
|
const handleProfileVisibleChange = (visible) => {
|
||||||
profileVisible.value = visible
|
profileVisible.value = visible
|
||||||
}
|
}
|
||||||
MyEvent.add('change-profile-visible',handleProfileVisibleChange)
|
MyEvent.add('change-profile-visible', handleProfileVisibleChange)
|
||||||
|
|
||||||
const handleClickReturn = () => {
|
const handleClickReturn = () => {
|
||||||
router.back()
|
router.back()
|
||||||
@@ -61,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;
|
||||||
|
|||||||
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>
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,7 +120,7 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/workshop/selectStyle',
|
path: '/workshop/selectStyle',
|
||||||
name: 'selectStyle',
|
name: 'SelectStyle',
|
||||||
component: () => import('../views/Workshop/selectStyle/index.vue'),
|
component: () => import('../views/Workshop/selectStyle/index.vue'),
|
||||||
meta: { verify: () => VerifyIDs(2) }
|
meta: { verify: () => VerifyIDs(2) }
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -95,28 +95,10 @@ export const useGenerateStore = defineStore({
|
|||||||
...data,
|
...data,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearStyle() {
|
|
||||||
this.style = {
|
|
||||||
id: '',
|
|
||||||
path: '',
|
|
||||||
isLike: false,
|
|
||||||
taskId:'',
|
|
||||||
status: ''
|
|
||||||
}
|
|
||||||
this.styleList = [
|
|
||||||
{id:'',taskId:'',status:'',path:''},
|
|
||||||
{id:'',taskId:'',status:'',path:''},
|
|
||||||
{id:'',taskId:'',status:'',path:''},
|
|
||||||
{id:'',taskId:'',status:'',path:''},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
//模特相关
|
//模特相关
|
||||||
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:''},
|
||||||
@@ -134,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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
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'
|
import MyEvent from '@/utils/myEvent'
|
||||||
@@ -106,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
|
||||||
})
|
})
|
||||||
@@ -154,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)
|
||||||
@@ -287,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,7 +7,6 @@
|
|||||||
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'
|
||||||
@@ -116,6 +115,7 @@
|
|||||||
}
|
}
|
||||||
// 选择另一个穿搭
|
// 选择另一个穿搭
|
||||||
const onChooseOutfit = () => {
|
const onChooseOutfit = () => {
|
||||||
|
generateStore.clearTryOn()
|
||||||
router.push({ name: 'SelectStyle' })
|
router.push({ name: 'SelectStyle' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -139,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" />
|
||||||
@@ -233,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;
|
||||||
@@ -245,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;
|
||||||
@@ -265,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;
|
||||||
@@ -293,13 +298,13 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -319,7 +324,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 0.25rem solid transparent;
|
border: 0.25rem solid transparent;
|
||||||
border-radius: 1.3rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> :first-child.finish {
|
> :first-child.finish {
|
||||||
|
|||||||
@@ -13,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>
|
||||||
@@ -33,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%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const openFlow = (item: any)=>{
|
|||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
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],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
|
|
||||||
> .title {
|
> .title {
|
||||||
font-family: satoshiBold;
|
font-family: satoshiBold;
|
||||||
font-size: 8.9rem;
|
font-size: 8.6rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
margin: 5rem 9.1rem;
|
margin: 5rem 9.1rem;
|
||||||
@@ -143,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;
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
> img {
|
> img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: contain;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,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 {
|
||||||
@@ -208,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;
|
||||||
|
|||||||
@@ -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 } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,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">
|
||||||
@@ -166,7 +166,7 @@ const { isLoading } = toRefs(data);
|
|||||||
</template>
|
</template>
|
||||||
</gradientButton>
|
</gradientButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="btnItem style2" @click.stop="onContinue">Continue</div>
|
<div class="btnItem style2" @click.stop="onContinue">{{ isHistoryFlow?'Finish':'Continue' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,6 +222,7 @@ const { isLoading } = toRefs(data);
|
|||||||
margin-top: 4.3rem;
|
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;
|
||||||
@@ -303,6 +304,9 @@ const { isLoading } = toRefs(data);
|
|||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.btnItem .text{
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,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,14 +1,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, reactive, toRefs, computed, ref } from "vue";
|
import { onMounted, onUnmounted, reactive, toRefs, computed, ref } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useGenerateStore, useUserInfoStore, useHGenerateStore } from '@/stores'
|
import { useGenerateStore, useUserInfoStore, useHGenerateStore } from '@/stores'
|
||||||
import { showToast } from 'vant';
|
import { showToast } from 'vant'
|
||||||
import { shareImageToWhatsapp } from '@/utils/tools'
|
import { shareImageToWhatsapp } from '@/utils/tools'
|
||||||
import { generateRequestOutfit, getRequestOutfit, setStyleFavorite, cancelStyleFavorite, retrieveAndRegenerate } from '@/api/workshop'
|
import {
|
||||||
|
generateRequestOutfit,
|
||||||
|
getRequestOutfit,
|
||||||
|
setStyleFavorite,
|
||||||
|
cancelStyleFavorite,
|
||||||
|
retrieveAndRegenerate
|
||||||
|
} from '@/api/workshop'
|
||||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||||
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
||||||
import gradientButton from '@/components/gradientButton.vue'
|
import gradientButton from '@/components/gradientButton.vue'
|
||||||
import StyleListDom from '@/views/Workshop/selectStyle/styleList.vue'
|
import StyleListDom from '@/views/Workshop/selectStyle/styleList.vue'
|
||||||
|
import { useStreamChat } from '@/hooks/useStreamChat'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
@@ -21,318 +28,364 @@ const query = computed(() => route.query)
|
|||||||
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
// const loadingTitle= ref('Analyzing the Outfit...')
|
// const loadingTitle= ref('Analyzing the Outfit...')
|
||||||
const loadingTitle = computed(()=>{
|
const loadingTitle = computed(() => {
|
||||||
let str = ''
|
let str = 'Analyzing the Outfit...'
|
||||||
if(!select.value.status)str = 'Analyzing the Outfit...'
|
if (!select.value.status) str = 'Analyzing the Outfit...'
|
||||||
if(select.value.status == 'RUNNING')str = 'Generating Results...'
|
if (select.value.status == 'RUNNING') str = 'Generating Results...'
|
||||||
if(select.value.status == 'PENDING')str = 'Almost there...'
|
if (select.value.status == 'PENDING' || select.value.status == 'ALMOST_DONE')
|
||||||
return str
|
str = 'Almost there...'
|
||||||
|
return str
|
||||||
})
|
})
|
||||||
|
|
||||||
let data = reactive({
|
let data = reactive({
|
||||||
select:computed(()=>generateStore.style),
|
select: computed(() => generateStore.style),
|
||||||
styleList:computed(()=>generateStore.styleList),
|
styleList: computed(() => generateStore.styleList)
|
||||||
})
|
})
|
||||||
let dataDom = reactive({
|
let dataDom = reactive({
|
||||||
styleListVue:null,
|
styleListVue: null
|
||||||
})
|
})
|
||||||
|
|
||||||
let getGenerateTime = null as any
|
let getGenerateTime = null as any
|
||||||
|
|
||||||
const updateStyle = ()=>{
|
const updateStyle = () => {
|
||||||
// generateStore.updateStyle(item)
|
// generateStore.updateStyle(item)
|
||||||
// data.styleList[index] = {}
|
// data.styleList[index] = {}
|
||||||
requestOutfit({num:4})
|
requestOutfit({ num: 4 })
|
||||||
}
|
}
|
||||||
const setLikeStyle = (likeStyle)=>{
|
const setLikeStyle = (likeStyle) => {
|
||||||
if(!select.value.id)return
|
if (!select.value.id) return
|
||||||
if(likeStyle){
|
if (likeStyle) {
|
||||||
cancelStyleFavorite(select.value.id).then(()=>{
|
cancelStyleFavorite(select.value.id).then(() => {
|
||||||
select.value.isLike = false
|
select.value.isLike = false
|
||||||
})
|
})
|
||||||
}else{
|
} else {
|
||||||
setStyleFavorite(select.value.id).then(()=>{
|
setStyleFavorite(select.value.id).then(() => {
|
||||||
select.value.isLike = true
|
select.value.isLike = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setDownload = ()=>{
|
const setDownload = () => {
|
||||||
if(select.value.path)shareImageToWhatsapp(select.value.path)
|
if (select.value.path) shareImageToWhatsapp(select.value.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
const toProduct = ()=>{
|
const toProduct = () => {
|
||||||
// if(generateStore.style.id){
|
// if(generateStore.style.id){
|
||||||
// generateStore.setIsGenerate(true)
|
// generateStore.setIsGenerate(true)
|
||||||
// }
|
// }
|
||||||
if(!isHistoryFlow.value){
|
router.push({ path: 'product', query: { ...query.value } })
|
||||||
router.push({ path: 'product', query: {...query.value} })
|
// if(!isHistoryFlow.value){
|
||||||
}else{
|
// router.push({ path: 'product', query: {...query.value} })
|
||||||
router.push({ path: 'creation', query: {...query.value, active: FlowType.H_OUTFIT} })
|
// }else{
|
||||||
}
|
// router.push({ path: 'creation', query: {...query.value, active: FlowType.H_OUTFIT} })
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestOutfit = async ({num})=>{
|
const requestOutfit = async ({ num }) => {
|
||||||
let rv:any = 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
|
isLoading.value = true
|
||||||
generateStore.clearStyle()
|
let rv: any = await new Promise<void>((resolve, reject) => {
|
||||||
data.select.taskId = rv[0]
|
if (isHistoryFlow.value) {
|
||||||
rv.forEach((item,index)=>data.styleList[index].taskId = item)
|
retrieveAndRegenerate({
|
||||||
getRequestOutfitList(rv)
|
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)=>{
|
const getRequestOutfitList = (generateList) => {
|
||||||
let value = {requestIDs:generateList.join(',')}
|
let value = { requestIDs: generateList.join(',') }
|
||||||
getRequestOutfit(value).then((rv:any)=>{
|
getRequestOutfit(value).then((rv: any) => {
|
||||||
let selectIndex = rv.findIndex((item)=>item.requestId == data.select.taskId)
|
let selectIndex = rv.findIndex((item) => item.requestId == data.select.taskId)
|
||||||
if(selectIndex != -1){
|
console.log(selectIndex)
|
||||||
data.select.id = rv[selectIndex].id
|
if (selectIndex != -1) {
|
||||||
data.select.path = rv[selectIndex].path
|
data.select.id = rv[selectIndex].id
|
||||||
data.select.status = rv[selectIndex].status
|
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)
|
rv.forEach((item) => {
|
||||||
data.styleList[index] = {
|
let index = data.styleList.findIndex(
|
||||||
id: item.id,
|
(styleListItem) => styleListItem?.taskId == item.requestId
|
||||||
taskId: item.requestId,
|
)
|
||||||
status: item.status,
|
data.styleList[index] = {
|
||||||
path: item.path,
|
id: item.id,
|
||||||
}
|
taskId: item.requestId,
|
||||||
})
|
status: item.status,
|
||||||
|
path: item.path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if(['SUCCEEDED'].includes(data.select.status))isLoading.value = false
|
if (['SUCCEEDED'].includes(data.select.status)) {
|
||||||
const taskIdList = data.styleList
|
isLoading.value = false
|
||||||
.filter(item => item?.taskId && item?.status !== 'SUCCEEDED')
|
if (isHistoryFlow.value) {
|
||||||
.map(item => item.taskId);
|
hGenerateStore.uploadStyle({
|
||||||
if(taskIdList.length > 0){
|
id: data.select.id,
|
||||||
getGenerateTime = setTimeout(()=>{
|
path: data.select.path
|
||||||
getRequestOutfitList(taskIdList)
|
})
|
||||||
},3000)
|
}
|
||||||
}
|
}
|
||||||
})
|
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 = ()=>{
|
const styleListInit = () => {
|
||||||
dataDom.styleListVue.init(data.select)
|
dataDom.styleListVue.init(data.select)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
// 使用 useStreamChat,在流式请求成功后执行原本的逻辑
|
||||||
// generateStore.clearProductData()
|
const { fetchMessage, isGenerating } = useStreamChat()
|
||||||
// if(!data.styleList[0]?.id)getRequestOutfitList(0)
|
|
||||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
onMounted(() => {
|
||||||
const taskIdList = data.styleList
|
// generateStore.clearProductData()
|
||||||
.filter(item => item?.taskId && item?.status !== 'SUCCEEDED')
|
// if(!data.styleList[0]?.id)getRequestOutfitList(0)
|
||||||
.map(item => item.taskId);
|
if (getGenerateTime) clearTimeout(getGenerateTime)
|
||||||
if(data.select.status == 'SUCCEEDED' && taskIdList.length == 0){
|
|
||||||
return
|
// 检查是否有从 dressfor 传递过来的消息
|
||||||
}else if(!data.select?.taskId){
|
const message = query.value.message as string
|
||||||
requestOutfit({num:4})
|
const sessionId = query.value.sessionId as string
|
||||||
}else if(data.select.status != 'SUCCEEDED' || taskIdList.length > 0){
|
|
||||||
if(data.select.status != 'SUCCEEDED')isLoading.value = true
|
if (message && sessionId) {
|
||||||
getRequestOutfitList(taskIdList)
|
// 有消息,说明是从 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(()=>{
|
onUnmounted(() => {
|
||||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
if (getGenerateTime) clearTimeout(getGenerateTime)
|
||||||
})
|
})
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
const { select } = toRefs(data);
|
const { select } = toRefs(data)
|
||||||
const { styleListVue } = toRefs(dataDom);
|
const { styleListVue } = toRefs(dataDom)
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="selectStyle">
|
<div class="selectStyle">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<div class="title">
|
<div class="title">Outfit Result</div>
|
||||||
Outfit Result
|
<div class="info">Refine your Look</div>
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
Refine your Look
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="selectContent">
|
|
||||||
<!-- {{ select }} -->
|
|
||||||
<div class="imgBox">
|
|
||||||
<img :src="select.path" alt="">
|
|
||||||
</div>
|
|
||||||
<div class="chooseMore" @click.stop="styleListInit">
|
|
||||||
<gradientButton>
|
|
||||||
<template #content>
|
|
||||||
<div class="text">
|
|
||||||
Choose More
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</gradientButton>
|
|
||||||
<div></div>
|
|
||||||
</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>
|
</div>
|
||||||
<StyleListDom ref="styleListVue"></StyleListDom>
|
<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>
|
</template>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.header-title {
|
.header-title {
|
||||||
// --header-title-background: #f6f6f6;
|
// --header-title-background: #f6f6f6;
|
||||||
}
|
}
|
||||||
.loading-container{
|
.loading-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.selectStyle{
|
.selectStyle {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
// height: 100%;
|
// height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #f6f6f6;
|
background-color: #f6f6f6;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
> .text{
|
> .text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 8.5rem;
|
margin-top: 8.5rem;
|
||||||
margin-bottom: 8.5rem;
|
margin-bottom: 8.5rem;
|
||||||
> .title{
|
> .title {
|
||||||
font-family: satoshiBold;
|
font-family: satoshiBold;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 8.6rem;
|
font-size: 8.6rem;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
> .info{
|
> .info {
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 124%;
|
line-height: 124%;
|
||||||
margin-top: 3.2rem;
|
margin-top: 3.2rem;
|
||||||
color: rgba(0, 0, 0, 0.6);
|
color: rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.selectContent{
|
.selectContent {
|
||||||
// padding: 0 4rem;
|
// padding: 0 4rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 73.7rem;
|
width: 73.7rem;
|
||||||
margin-bottom: 19rem;
|
margin-bottom: 19rem;
|
||||||
> .imgBox{
|
> .imgBox {
|
||||||
height: 73.7rem;
|
height: 73.7rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 5.6rem;
|
margin-bottom: 5.6rem;
|
||||||
> img{
|
> img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .chooseMore{
|
> .chooseMore {
|
||||||
--borderRadius: 5.4rem;
|
--borderRadius: 5.4rem;
|
||||||
--borderWidth: 2px;
|
--borderWidth: 2px;
|
||||||
width: 24.8rem;
|
width: 24.8rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: 7.6rem;
|
height: 7.6rem;
|
||||||
.text{
|
.text {
|
||||||
font-size: 3.1rem;
|
font-size: 3.1rem;
|
||||||
font-family: satoshiMedium;
|
color: #000;
|
||||||
}
|
font-family: satoshiMedium;
|
||||||
}
|
}
|
||||||
> .btn{
|
}
|
||||||
display: flex;
|
> .btn {
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: flex-end;
|
align-items: center;
|
||||||
gap: 2rem;
|
justify-content: flex-end;
|
||||||
> div{
|
gap: 2rem;
|
||||||
color: #000;
|
> div {
|
||||||
border-radius: 50%;
|
color: #000;
|
||||||
width: 7rem;
|
border-radius: 50%;
|
||||||
height: 7rem;
|
width: 7rem;
|
||||||
padding: 1rem;
|
height: 7rem;
|
||||||
background-color: #fff;
|
padding: 1rem;
|
||||||
&:hover{
|
background-color: #fff;
|
||||||
color: #000;
|
&:hover {
|
||||||
}
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> .btn{
|
}
|
||||||
display: flex;
|
> .btn {
|
||||||
gap: 6.6rem;
|
display: flex;
|
||||||
justify-content: center;
|
gap: 6.6rem;
|
||||||
> div {
|
justify-content: center;
|
||||||
border-radius: .96rem;
|
> div {
|
||||||
width: 33.7rem;
|
border-radius: 0.96rem;
|
||||||
font-size: 4.8rem;
|
width: 33.7rem;
|
||||||
font-family: satoshiMedium;
|
font-size: 4.8rem;
|
||||||
line-height: 9.2rem;
|
font-family: satoshiMedium;
|
||||||
display: flex;
|
line-height: 9.2rem;
|
||||||
justify-content: center;
|
display: flex;
|
||||||
&.style1{
|
justify-content: center;
|
||||||
--borderRadius: .96rem;
|
&.style1 {
|
||||||
--borderWidth: 2px;
|
--borderRadius: 0.96rem;
|
||||||
.text{
|
--borderWidth: 2px;
|
||||||
width: 100%;
|
.text {
|
||||||
text-align: center;
|
width: 100%;
|
||||||
> .icon{
|
text-align: center;
|
||||||
left: 4rem;
|
> .icon {
|
||||||
top: 50%;
|
left: 4rem;
|
||||||
transform: translateY(-50%);
|
top: 50%;
|
||||||
position: absolute;
|
transform: translateY(-50%);
|
||||||
}
|
position: absolute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.style2{
|
}
|
||||||
color: #fff;
|
&.style2 {
|
||||||
background-color: #000;
|
color: #fff;
|
||||||
}
|
background-color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.btnItem .text {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -33,6 +33,7 @@ const confirm = ()=>{
|
|||||||
data.selectStyle.taskId = data.oldSelectStyle.taskId
|
data.selectStyle.taskId = data.oldSelectStyle.taskId
|
||||||
data.selectStyle.isLike = false
|
data.selectStyle.isLike = false
|
||||||
}
|
}
|
||||||
|
generateStore.clearTryOn()
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
<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,6 +28,7 @@
|
|||||||
<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'
|
import { useUserInfoStore } from '@/stores'
|
||||||
|
|
||||||
const md = new MarkdownIt()
|
const md = new MarkdownIt()
|
||||||
@@ -101,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 {
|
||||||
@@ -132,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -17,12 +17,14 @@
|
|||||||
<div class="btn flex flex-center" @click="handleContinue">Generate</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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<!-- 标题区域 -->
|
<!-- 标题区域 -->
|
||||||
@@ -133,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,7 +2,7 @@
|
|||||||
<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">
|
||||||
@@ -187,9 +187,9 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ const router = useRouter()
|
|||||||
const generateStore = useGenerateStore()
|
const generateStore = useGenerateStore()
|
||||||
const loading = ref(false)
|
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' || pageMode.value === 'form' ? 'Customer ID' : 'Create Profile'
|
return pageMode.value === 'entry' || pageMode.value === 'form' ? 'Customer ID' : 'Create Profile'
|
||||||
@@ -143,11 +143,11 @@ const handleConfirm = async () => {
|
|||||||
|
|
||||||
const handleShowPopup = (flag: Boolean) => {
|
const handleShowPopup = (flag: Boolean) => {
|
||||||
// showPopup.value = flag
|
// showPopup.value = flag
|
||||||
profileRef.value.handleShowPopup(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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,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;
|
||||||
|
|||||||
@@ -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,11 +36,11 @@
|
|||||||
/>
|
/>
|
||||||
</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 }"
|
:class="{ active: item === inputValue }"
|
||||||
@@ -47,7 +51,7 @@
|
|||||||
{{ 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"
|
||||||
@@ -57,24 +61,43 @@
|
|||||||
>
|
>
|
||||||
{{ 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)
|
||||||
@@ -103,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 = () => {
|
||||||
@@ -197,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(() => {
|
||||||
@@ -221,6 +249,20 @@ onUnmounted(() => {
|
|||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
padding: 15.9rem 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) {
|
||||||
@@ -239,87 +281,78 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.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: radial-gradient(80.79% 50% at 50% 50%, #d1c7c2 0%, rgba(255, 255, 255, 0) 100%),
|
|
||||||
radial-gradient(99.56% 93.08% at 99.56% 93.08%, #e6e6e6 0%, #000000 100%)
|
|
||||||
/* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */,
|
|
||||||
linear-gradient(120.09deg, #b3b3b3 0%, rgba(255, 255, 255, 0) 35.41%);
|
|
||||||
-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';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">Choose Stylist.</div>
|
<div class="title">Choose Stylist.</div>
|
||||||
<div class="sub-title">What style are you looking for? </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">
|
||||||
@@ -35,7 +35,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Continue按钮 -->
|
<!-- Continue按钮 -->
|
||||||
<div class="continue-button" @click="handleContinue" v-if="!$route.query?.demo">Continue</div>
|
<button
|
||||||
|
class="sandblasted-blurred continue-button flex flex-center"
|
||||||
|
@click="handleContinue"
|
||||||
|
v-if="!$route.query?.demo"
|
||||||
|
>
|
||||||
|
<span>Continue</span>
|
||||||
|
</button>
|
||||||
<!-- <van-dialog
|
<!-- <van-dialog
|
||||||
class="video-dialog"
|
class="video-dialog"
|
||||||
:show-confirm-button="false"
|
:show-confirm-button="false"
|
||||||
@@ -63,6 +69,10 @@ import female from '@/assets/images/female.png'
|
|||||||
import femaleThumb from '@/assets/images/female_thumb.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()
|
||||||
@@ -71,31 +81,31 @@ 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: femaleThumb
|
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: maleThumb
|
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
|
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
|
thumb: maleThumb
|
||||||
@@ -181,7 +191,7 @@ const handleContinue = () => {
|
|||||||
letter-spacing: -0.04rem;
|
letter-spacing: -0.04rem;
|
||||||
margin-bottom: 3.2rem;
|
margin-bottom: 3.2rem;
|
||||||
}
|
}
|
||||||
.sub-title{
|
.sub-title {
|
||||||
font-family: 'satoshiRegular';
|
font-family: 'satoshiRegular';
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
}
|
}
|
||||||
@@ -294,9 +304,9 @@ const handleContinue = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.continue-button {
|
button.sandblasted-blurred.continue-button {
|
||||||
height: 6.7rem;
|
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;
|
||||||
@@ -306,11 +316,9 @@ const handleContinue = () => {
|
|||||||
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) {
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||