Compare commits

...

31 Commits

Author SHA1 Message Date
df5cfc5eba feat: 修改请求时机
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-02-27 15:02:08 +08:00
8100459c4e feat: dressfor页面&生成outfit逻辑修改
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-02-27 13:44:19 +08:00
10ee247b8d bugfix: 图片引入
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-29 14:23:57 +08:00
7f34ce80b9 feat: 测试部署
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-29 14:04:32 +08:00
57359d1067 Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-29 13:13:04 +08:00
9101116430 chore: 更换设计师图片 2026-01-29 13:11:36 +08:00
X1627315083
7a87c6cd11 Merge branches 'main' and 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-19 13:40:54 +08:00
X1627315083
c8dc6cf8d1 对话页面指定标识字体加粗 2026-01-19 13:39:51 +08:00
f092a76162 Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-14 09:49:25 +08:00
48a32a60a1 bugfix: customer页面选择顾客列表 2026-01-14 09:41:51 +08:00
X1627315083
f157c6ead3 调整设计师名字
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-13 16:09:16 +08:00
X1627315083
592792d071 fix
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-13 14:48:56 +08:00
X1627315083
aace73d5c4 修改设计师名字最后两个改为sera、edi
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-13 14:39:38 +08:00
X1627315083
02dcfba4ba fix
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2026-01-07 11:48:31 +08:00
X1627315083
465fa5e6ae fix
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-31 10:12:46 +08:00
X1627315083
0ec9e4dc46 调整outfit流程
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-31 10:07:08 +08:00
X1627315083
e230b4c83f Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 15:57:20 +08:00
X1627315083
cf15e371ab 修改product页面标题颜色 2025-12-30 15:53:27 +08:00
李志鹏
ef314931eb 1
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 15:36:49 +08:00
李志鹏
71a3946aae 1
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 15:35:49 +08:00
李志鹏
38f027d3ad Merge branch 'main' of http://18.167.251.121:10003/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 14:58:22 +08:00
李志鹏
d497471e09 调整 2025-12-30 14:58:15 +08:00
92c571b5f9 Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 14:51:39 +08:00
6156c88429 style: 样式修改 2025-12-30 14:51:19 +08:00
X1627315083
319130dc6a 调整product的history标签位置
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 14:18:10 +08:00
李志鹏
6a2b456bf3 selectStyle
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 13:31:45 +08:00
X1627315083
135adb3907 Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 13:10:53 +08:00
089a266c35 Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/lanecarford_front
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
2025-12-30 13:05:10 +08:00
a9732a659e feat: 对话加载动画 2025-12-30 11:54:13 +08:00
X1627315083
24e221902e 反推新增参数 2025-12-30 11:40:39 +08:00
X1627315083
9f338a772d 修复魔改页面无法跳转到outfit页面 2025-12-30 09:45:26 +08:00
27 changed files with 1233 additions and 942 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
src/assets/images/mini.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View File

@@ -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;

View 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
View 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
}
}

View File

@@ -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) }
}, },

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -115,6 +115,7 @@
} }
// 选择另一个穿搭 // 选择另一个穿搭
const onChooseOutfit = () => { const onChooseOutfit = () => {
generateStore.clearTryOn()
router.push({ name: 'SelectStyle' }) router.push({ name: 'SelectStyle' })
} }
</script> </script>
@@ -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;
@@ -271,14 +272,13 @@
} }
> .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;
@@ -298,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;
} }
@@ -324,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 {

View File

@@ -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%;
} }
} }

View File

@@ -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],
}, },

View File

@@ -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;

View File

@@ -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;
@@ -353,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;

File diff suppressed because it is too large Load Diff

View File

@@ -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,322 +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 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="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;
color: #000; color: #000;
font-family: satoshiMedium; font-family: satoshiMedium;
} }
} }
> .btn{ > .btn {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
gap: 2rem; gap: 2rem;
> div{ > div {
color: #000; color: #000;
border-radius: 50%; border-radius: 50%;
width: 7rem; width: 7rem;
height: 7rem; height: 7rem;
padding: 1rem; padding: 1rem;
background-color: #fff; background-color: #fff;
&:hover{ &:hover {
color: #000; color: #000;
} }
} }
} }
} }
> .btn{ > .btn {
display: flex; display: flex;
gap: 6.6rem; gap: 6.6rem;
justify-content: center; justify-content: center;
> div { > div {
border-radius: .96rem; border-radius: 0.96rem;
width: 33.7rem; width: 33.7rem;
font-size: 4.8rem; font-size: 4.8rem;
font-family: satoshiMedium; font-family: satoshiMedium;
line-height: 9.2rem; line-height: 9.2rem;
display: flex; display: flex;
justify-content: center; justify-content: center;
&.style1{ &.style1 {
--borderRadius: .96rem; --borderRadius: 0.96rem;
--borderWidth: 2px; --borderWidth: 2px;
.text{ .text {
width: 100%; width: 100%;
text-align: center; text-align: center;
> .icon{ > .icon {
left: 4rem; left: 4rem;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
position: absolute; position: absolute;
} }
} }
} }
&.style2{ &.style2 {
color: #fff; color: #fff;
background-color: #000; background-color: #000;
} }
} }
.btnItem .text{ .btnItem .text {
color: #000; color: #000;
} }
} }
} }
</style> </style>

View File

@@ -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();
} }

View File

@@ -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;
} }

View File

@@ -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;
}
} }
} }
} }

View File

@@ -81,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)
}) })

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -143,7 +143,7 @@ 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) => {
@@ -189,7 +189,7 @@ const handleBack = (e?: Event) => {
.setting { .setting {
z-index: 1; z-index: 1;
padding: 3.67rem 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;

View File

@@ -1,9 +1,10 @@
<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?
@@ -12,7 +13,7 @@
<img class="text" src="@/assets/images/dressfor.png" alt="" /> <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
@@ -37,9 +38,9 @@
<div class="send flex flex-center" @click="handleSendMessage"> <div class="send flex flex-center" @click="handleSendMessage">
<SvgIcon class="send-icon" name="send_bold" size="26" color="#6d6868" /> <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 }"
@@ -50,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"
@@ -60,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)
@@ -106,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 = () => {
@@ -200,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(() => {
@@ -224,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) {
@@ -246,70 +285,74 @@ onUnmounted(() => {
padding-bottom: 7.7rem; padding-bottom: 7.7rem;
width: 60rem; width: 60rem;
} }
.chatbox { // .chatbox {
height: 9.3rem; // height: 9.3rem;
// background-color: #fff; // // background-color: #fff;
column-gap: 2.29rem; // column-gap: 2.29rem;
.input-box { // .input-box {
width: 59.8rem; // width: 59.8rem;
height: 100%; // height: 100%;
background-color: #efefef; // background-color: #efefef;
// border: 2px solid #5f5f5f; // // border: 2px solid #5f5f5f;
border-radius: 1rem; // border-radius: 1rem;
color: #222222; // color: #222222;
font-size: 3.2rem; // font-size: 3.2rem;
font-family: 'satoshiRegular'; // font-family: 'satoshiRegular';
padding: 0 2.6rem; // padding: 0 2.6rem;
column-gap: 2.6rem; // column-gap: 2.6rem;
overflow: hidden; // overflow: hidden;
.input-wrapper { // .input-wrapper {
overflow: hidden; // overflow: hidden;
} // }
.recording-visualizer { // .recording-visualizer {
display: flex; // display: flex;
align-items: center; // align-items: center;
height: 100%; // height: 100%;
:deep(.audio-visualizer) { // :deep(.audio-visualizer) {
width: 100%; // width: 100%;
padding: 0; // padding: 0;
} // }
:deep(.visualizer-container) { // :deep(.visualizer-container) {
height: 100%; // height: 100%;
} // }
} // }
.input-item { // .input-item {
// width: 100%; // // width: 100%;
height: 100%; // height: 100%;
outline: none; // outline: none;
border: none; // border: none;
background-color: #efefef; // background-color: #efefef;
} // }
.audio-icon { // .audio-icon {
width: initial; // width: initial;
} // }
} // }
.send { // .send {
width: 7.6rem; // width: 7.6rem;
height: 7.6rem; // height: 7.6rem;
background-color: #efefef; // background-color: #efefef;
border-radius: 1rem; // 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';

View File

@@ -69,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()
@@ -77,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

View File

@@ -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: {