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

This commit is contained in:
李志鹏
2025-12-23 17:29:56 +08:00
20 changed files with 243 additions and 170 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -3,7 +3,7 @@
const router = useRouter() const router = useRouter()
defineProps({ defineProps({
title: { type: String, default: 'AI STYLING ASSISTANT' } title: { type: String, default: 'STYLING ASSISTANT' }
}) })
const emit = defineEmits(['clickReturn', 'clickProfile']) const emit = defineEmits(['clickReturn', 'clickProfile'])

View File

@@ -1,16 +1,16 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import { useGenerateStore } from '@/stores/modules/generate' import { useGenerateStore } from '@/stores/modules/generate'
const VerifyIDs = (num: number) => { const VerifyIDs = (num: number) => {
return true return true
const ids = [ const ids = [
!!useGenerateStore().customerId, !!useGenerateStore().customerId,
!!useGenerateStore().visitRecordId, !!useGenerateStore().visitRecordId,
!!useGenerateStore().styleId, !!useGenerateStore().styleId,
// !!useGenerateStore().modelPhotoId, // !!useGenerateStore().modelPhotoId,
true, true,
!!useGenerateStore().originalTryOnId, !!useGenerateStore().originalTryOnId
]; ]
return ids.splice(0, num).every(id => id) ? true : "/stylist/customer"; return ids.splice(0, num).every((id) => id) ? true : '/stylist/customer'
} }
/** /**
@@ -48,8 +48,8 @@ const router = createRouter({
component: () => import('@/views/login/LoginPage.vue') component: () => import('@/views/login/LoginPage.vue')
}, },
{ {
path:'/reset', path: '/reset',
name:'ResetPage', name: 'ResetPage',
component: () => import('@/views/login/ResetPage.vue') component: () => import('@/views/login/ResetPage.vue')
}, },
{ {
@@ -62,16 +62,16 @@ const router = createRouter({
name: 'WelcomePage', name: 'WelcomePage',
component: () => import('@/views/login/WelcomePage.vue') component: () => import('@/views/login/WelcomePage.vue')
}, },
{ {
path: '/customer', path: '/customer',
name: 'customer', name: 'customer',
component: () => import('@/views/login/customer.vue'), component: () => import('@/views/login/customer.vue')
}, },
{ {
path: '/asistant', path: '/asistant',
name: 'asistant', name: 'asistant',
component: () => import('../views/asistant/index.vue'), component: () => import('../views/asistant/index.vue'),
meta: { cache: true, verify: ()=> VerifyIDs(2) } meta: { cache: true, verify: () => VerifyIDs(2) }
}, },
{ {
path: '/workshop', path: '/workshop',
@@ -82,115 +82,114 @@ const router = createRouter({
// path: '/workshop', // path: '/workshop',
// redirect: '/workshop/selectStyle' // redirect: '/workshop/selectStyle'
// }, // },
{ {
path: '/workshop/home', path: '/workshop/home',
name: 'Home', name: 'Home',
component: () => import('@/views/Workshop/home.vue') component: () => import('@/views/Workshop/home.vue')
}, },
{ {
path: '/workshop/homeNav', path: '/workshop/homeNav',
name: 'HomeNav', name: 'HomeNav',
component: () => import('@/views/Workshop/homeNav.vue') component: () => import('@/views/Workshop/homeNav.vue')
}, },
{ {
path: '/workshop/stylist', path: '/workshop/stylist',
name: 'StylistPage', name: 'StylistPage',
redirect: '/workshop/stylist/index', redirect: '/workshop/stylist/index',
component: () => import('@/views/stylist/container.vue'), component: () => import('@/views/stylist/container.vue'),
children: [ children: [
{ {
path: 'index', path: 'index',
name: 'index', name: 'index',
component: () => import('@/views/stylist/index.vue'), component: () => import('@/views/stylist/index.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, },
{ {
path: 'sex', path: 'sex',
name: 'sex', name: 'sex',
component: () => import('@/views/stylist/sex.vue'), component: () => import('@/views/stylist/sex.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, },
{ {
path: 'dressfor', path: 'dressfor',
name: 'dressfor', name: 'dressfor',
component: () => import('@/views/stylist/dressfor.vue'), component: () => import('@/views/stylist/dressfor.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, }
]
] },
},
{ {
path: '/workshop/selectStyle', path: '/workshop/selectStyle',
name: 'selectStyle', name: 'selectStyle',
component: () => import('../views/Workshop/selectStyle.vue'), component: () => import('../views/Workshop/selectStyle.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, },
{ {
path: '/workshop/selectModel', path: '/workshop/selectModel',
name: 'SelectModel', name: 'SelectModel',
component: () => import('../views/Workshop/selectModel.vue'), component: () => import('../views/Workshop/selectModel.vue'),
meta: { verify: ()=> VerifyIDs(3) } meta: { verify: () => VerifyIDs(3) }
}, },
{ {
path: '/workshop/product', path: '/workshop/product',
name: 'product', name: 'product',
component: () => import('../views/Workshop/product.vue'), component: () => import('../views/Workshop/product.vue'),
meta: { verify: ()=> VerifyIDs(4) } meta: { verify: () => VerifyIDs(4) }
}, },
{ {
// 推荐try on // 推荐try on
path: '/workshop/recommended', path: '/workshop/recommended',
name: 'recommended', name: 'recommended',
component: () => import('../views/Workshop/recommended.vue'), component: () => import('../views/Workshop/recommended.vue'),
meta: { verify: ()=> VerifyIDs(5) } meta: { verify: () => VerifyIDs(5) }
}, },
{ {
// 上传照片1 // 上传照片1
path: '/workshop/uploadFace', path: '/workshop/uploadFace',
name: 'uploadFace', name: 'uploadFace',
component: () => import('../views/Workshop/uploadFace1.vue'), component: () => import('../views/Workshop/uploadFace1.vue'),
meta: { verify: ()=> VerifyIDs(5) } meta: { verify: () => VerifyIDs(5) }
}, },
{ {
// 上传照片2 // 上传照片2
path: '/workshop/uploadFace2', path: '/workshop/uploadFace2',
name: 'uploadFace2', name: 'uploadFace2',
component: () => import('../views/Workshop/uploadFace2.vue'), component: () => import('../views/Workshop/uploadFace2.vue'),
meta: { verify: ()=> VerifyIDs(5) } meta: { verify: () => VerifyIDs(5) }
}, },
{ {
// 自定义创作 // 自定义创作
path: '/workshop/customize', path: '/workshop/customize',
name: 'customize', name: 'customize',
component: () => import('../views/Workshop/customize.vue'), component: () => import('../views/Workshop/customize.vue'),
meta: { verify: ()=> VerifyIDs(5) } meta: { verify: () => VerifyIDs(5) }
}, },
{ {
// library // library
path: '/workshop/library', path: '/workshop/library',
name: 'library', name: 'library',
component: () => import('../views/Workshop/library.vue'), component: () => import('../views/Workshop/library.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, },
{ {
path: '/workshop/profile', path: '/workshop/profile',
name: 'profile', name: 'profile',
component: () => import('../views/Workshop/profile.vue'), component: () => import('../views/Workshop/profile.vue'),
meta: { verify: ()=> VerifyIDs(1) } meta: { verify: () => VerifyIDs(1) }
}, },
{ {
// creation // creation
path: '/workshop/creation', path: '/workshop/creation',
name: 'creation', name: 'creation',
component: () => import('../views/Workshop/creation/index.vue'), component: () => import('../views/Workshop/creation/index.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
}, },
{ {
// 完成创建 // 完成创建
path: '/workshop/end', path: '/workshop/end',
name: 'end', name: 'end',
component: () => import('../views/Workshop/end.vue'), component: () => import('../views/Workshop/end.vue'),
meta: { verify: ()=> VerifyIDs(2) } meta: { verify: () => VerifyIDs(2) }
} }
] ]
} }

View File

@@ -2,6 +2,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import MyEvent from '@/utils/myEvent' import MyEvent from '@/utils/myEvent'
MyEvent.add('clear-generate-state', () => useGenerateStore().clearGenerateData()) MyEvent.add('clear-generate-state', () => useGenerateStore().clearGenerateData())
MyEvent.add('clear-client-state', () => useGenerateStore().clearCustomerInfo())
export const useGenerateStore = defineStore({ export const useGenerateStore = defineStore({
id: 'generate', // 必须指明唯一的pinia仓库的id id: 'generate', // 必须指明唯一的pinia仓库的id
@@ -176,7 +177,7 @@ export const useGenerateStore = defineStore({
this.updatePhotoInfo({}) this.updatePhotoInfo({})
this.clearCustomizeInfo() this.clearCustomizeInfo()
this.clearCustomizeInfoDemo() this.clearCustomizeInfoDemo()
this.clearCustomerInfo() // this.clearCustomerInfo()
this.setSessionId('') this.setSessionId('')
}, },
setCustomerInfo(data: any) { setCustomerInfo(data: any) {

View File

@@ -9,7 +9,8 @@ export const useUserInfoStore = defineStore('userInfo', () => {
token: '', token: '',
generateParams: { generateParams: {
stylist: '', stylist: '',
sex: '' sex: '',
stylistImage: ''
} }
}) })
@@ -37,7 +38,8 @@ export const useUserInfoStore = defineStore('userInfo', () => {
const resetGenerateParams = () => { const resetGenerateParams = () => {
state.value.generateParams = { state.value.generateParams = {
stylist: '', stylist: '',
sex: '' sex: '',
stylistImage: ''
} }
} }
@@ -49,6 +51,7 @@ export const useUserInfoStore = defineStore('userInfo', () => {
removeLocal('token') removeLocal('token')
resetGenerateParams() resetGenerateParams()
MyEvent.emit('clear-generate-state') MyEvent.emit('clear-generate-state')
MyEvent.emit('clear-client-state')
MyEvent.emit('clearAllCache') MyEvent.emit('clearAllCache')
resolve('') resolve('')
}) })

View File

@@ -10,6 +10,7 @@
cancelTryOnEffectFavorite cancelTryOnEffectFavorite
} from '@/api/workshop' } from '@/api/workshop'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import MyEvent from '@/utils/myEvent'
const router = useRouter() const router = useRouter()
const emit = defineEmits(['view-type']) const emit = defineEmits(['view-type'])
const query = computed(() => router.currentRoute.value.query) const query = computed(() => router.currentRoute.value.query)
@@ -243,6 +244,7 @@
hGenerateStore.style.url = selectedItem.url hGenerateStore.style.url = selectedItem.url
// selectedItem.isFavorite // selectedItem.isFavorite
} }
MyEvent.emit('clear-generate-state')
router.push({ name: 'HomeNav', query: { flowType: nav.flowType } }) router.push({ name: 'HomeNav', query: { flowType: nav.flowType } })
} else { } else {
router.push({ name: 'end' }) router.push({ name: 'end' })

View File

@@ -5,6 +5,7 @@ import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
import { useGenerateStore, useHGenerateStore } from '@/stores' import { useGenerateStore, useHGenerateStore } from '@/stores'
import { generateTryOnEffect, setTryOnEffectFavorite, cancelTryOnEffectFavorite, addTryOnEffectComment } from '@/api/workshop' import { generateTryOnEffect, setTryOnEffectFavorite, cancelTryOnEffectFavorite, addTryOnEffectComment } from '@/api/workshop'
import { FlowType, IsHistoryFlow } from '@/types/enum' import { FlowType, IsHistoryFlow } from '@/types/enum'
import gradientButton from '@/components/gradientButton.vue'
const router = useRouter() const router = useRouter()
//const props = defineProps({ //const props = defineProps({
@@ -64,7 +65,6 @@ const startGenerate = ()=>{
generateStore.originalTryOn.id = res.id generateStore.originalTryOn.id = res.id
generateStore.originalTryOn.tryOnUrl = res.tryOnUrl generateStore.originalTryOn.tryOnUrl = res.tryOnUrl
// generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式 // generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式
generateStore.setIsGenerate(false)
generateStore.clearCustomizeInfo() generateStore.clearCustomizeInfo()
}).catch((error)=>{ }).catch((error)=>{
@@ -121,7 +121,7 @@ const handleSubmit = ()=>{
onMounted(() => { onMounted(() => {
emit('view-type', 1) emit('view-type', 1)
if (generateStore.isGenerate) { if (!generateStore.originalTryOn.id) {
startGenerate() startGenerate()
} }
}) })
@@ -134,7 +134,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? Generate Result
</div> </div>
</div> </div>
<div class="selectContent"> <div class="selectContent">
@@ -150,14 +150,25 @@ const { isLoading } = toRefs(data);
<div class="operation"> <div class="operation">
<div @click="setLike"><SvgIcon :name="`love_${generateStore.originalTryOn.isLike ? '1' : '0'}`" size="35" /></div> <div @click="setLike"><SvgIcon :name="`love_${generateStore.originalTryOn.isLike ? '1' : '0'}`" size="35" /></div>
<div @click="feedback"><SvgIcon name="pen" size="40" /></div> <div @click="feedback"><SvgIcon name="pen" size="40" /></div>
<div @click="startGenerate"><SvgIcon name="reload" size="35" /></div>
<!-- <div><SvgIcon name="download" size="35" /></div> --> <!-- <div><SvgIcon name="download" size="35" /></div> -->
</div> </div>
</div> </div>
</div> </div>
<div class="again"> <div class="btn">
<!-- <div @click="changeModel">Change Model</div> --> <div class="btnItem style1" @click.stop="startGenerate()">
<button class="flex flex-center" @click="onContinue" style="margin-left: auto;">Continue</button> <gradientButton>
<template #content>
<div class="text">
<span class="icon">
<SvgIcon name="reTry" size="40" />
</span>
Re-try
</div>
</template>
</gradientButton>
</div>
<div class="btnItem style2" @click.stop="onContinue">Continue</div>
</div> </div>
</div> </div>
</div> </div>
@@ -190,18 +201,18 @@ const { isLoading } = toRefs(data);
<style lang="less" scoped> <style lang="less" scoped>
.product{ .product{
width: 100%; width: 100%;
// height: 100%; height: 100%;
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
> .text{ > .text{
text-align: center; text-align: center;
width: 100%; width: 100%;
margin-top: 4.3rem; margin-top: 4rem;
> .title{ > .title{
font-family: satoshiBold; font-family: satoshiBold;
font-weight: 700; font-weight: 700;
font-size: 9.6rem; font-size: 8.6rem;
line-height: 124%; line-height: 124%;
color: #000; color: #000;
} }
@@ -210,7 +221,7 @@ const { isLoading } = toRefs(data);
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
padding: 0 14.1rem; padding: 0 14.1rem;
margin-top: 4.8rem; margin-top: 4.3rem;
> .history{ > .history{
width: 30.2rem; width: 30.2rem;
height: 6.52rem; height: 6.52rem;
@@ -229,7 +240,7 @@ const { isLoading } = toRefs(data);
} }
} }
> .modelBox{ > .modelBox{
margin-top: 2.5rem; margin-top: 4.4rem;
> .model{ > .model{
border: .2rem solid #D9D9D9; border: .2rem solid #D9D9D9;
// height: 110rem; // height: 110rem;
@@ -262,24 +273,36 @@ const { isLoading } = toRefs(data);
} }
} }
} }
> .again{ > .btn{
margin-top: 4.4rem;
display: flex; display: flex;
gap: 6.6rem;
justify-content: center; justify-content: center;
> button{ margin-top: 5.2rem;
border-radius: .7rem; > div {
border: 3px solid #000; border-radius: .96rem;
background-color: #000; width: 33.7rem;
text-align: center; font-size: 4.8rem;
color: #fff;
font-family: satoshiMedium; font-family: satoshiMedium;
font-size: 3.6rem; line-height: 9.2rem;
width: 24.6rem; display: flex;
line-height: 6.7rem; justify-content: center;
height: 6.7rem; &.style1{
box-sizing: border-box; --borderRadius: .96rem;
&:last-child{ --borderWidth: 2px;
margin-right: 0; .text{
width: 100%;
text-align: center;
> .icon{
left: 4rem;
top: 50%;
transform: translateY(-50%);
position: absolute;
}
}
}
&.style2{
color: #fff;
background-color: #000;
} }
} }
} }

View File

@@ -24,14 +24,14 @@ const hGenerateStore = useHGenerateStore()
const query = computed(() => route.query) 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 = ''
// 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')str = 'Almost there...'
// return str return str
// }) })
let data = reactive({ let data = reactive({
select:computed(()=>generateStore.style), select:computed(()=>generateStore.style),

View File

@@ -68,7 +68,7 @@ const setTl1 = ()=>{
nextTick(()=>{ nextTick(()=>{
let el = dotBox.value let el = dotBox.value
let width = el.offsetWidth + el.parentElement.offsetWidth let width = el.offsetWidth + el.parentElement.offsetWidth
let time = el.parentElement.offsetWidth / 35 let time = el.parentElement.offsetWidth / el.offsetWidth / 2
console.log(time,width) console.log(time,width)
tl1 = gsap.timeline(); tl1 = gsap.timeline();
tl1.to(el,time, tl1.to(el,time,
@@ -136,7 +136,7 @@ onMounted(() => {
.loading-dot{ .loading-dot{
height: 100%; height: 100%;
aspect-ratio: 1/1; aspect-ratio: 1.2/1;
background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent); background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent);
border-radius: 50%; border-radius: 50%;
position: absolute; position: absolute;

View File

@@ -2,7 +2,7 @@
<div class="chat-message" :class="{ 'user-message': isMyself, 'ai-message': !isMyself }"> <div class="chat-message" :class="{ 'user-message': isMyself, 'ai-message': !isMyself }">
<!-- AI消息显示头像 --> <!-- AI消息显示头像 -->
<div v-if="!isMyself" class="chat-avatar"> <div v-if="!isMyself" class="chat-avatar">
<img src="@/assets/images/asistant.png" alt="AI Avatar" /> <img class="avatar" :src="thumb" alt="AI Avatar" />
</div> </div>
<!-- 消息内容 --> <!-- 消息内容 -->
@@ -27,9 +27,15 @@
<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 { useUserInfoStore } from '@/stores'
const md = new MarkdownIt() const md = new MarkdownIt()
const userInfoStore = useUserInfoStore()
const thumb = computed(() => {
return userInfoStore.state.generateParams.stylistImage
})
// 定义消息类型 // 定义消息类型
interface ChatMessage { interface ChatMessage {
sessionId: string | number sessionId: string | number
@@ -133,16 +139,17 @@ const actionList: ActionItem[] = [
.chat-avatar { .chat-avatar {
width: 7.8rem; width: 7.8rem;
height: 7.4rem; height: 7.8rem;
border-radius: 50%; border-radius: 50%;
margin-right: 1.88rem; margin-right: 1.88rem;
border: .2rem solid #000; overflow: hidden;
padding: 1.4rem; // border: 0.2rem solid #000;
// padding: 1.4rem;
img { img {
width: 4.9rem; width: 100%;
height: 4.9rem; // height: 100%;
border-radius: 50%; // border-radius: 50%;
// object-fit: cover; // object-fit: cover;
} }
} }

View File

@@ -14,7 +14,7 @@
<div class="footer"> <div class="footer">
<InputArea @send-message="handleSendMessage" /> <InputArea @send-message="handleSendMessage" />
<div class="continue flex"> <div class="continue flex">
<div class="btn flex flex-center" @click="handleContinue">Continue</div> <div class="btn flex flex-center" @click="handleContinue">Generate</div>
</div> </div>
</div> </div>
</div> </div>
@@ -272,7 +272,7 @@ const handleContinue = () => {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.asistant-container { .asistant-container {
height: 100vh; height: 100%;
overflow: hidden; overflow: hidden;
} }
@@ -300,7 +300,8 @@ const handleContinue = () => {
border-radius: 0.7rem; border-radius: 0.7rem;
background-color: #000; background-color: #000;
width: 24.6rem; width: 24.6rem;
height: 5.9rem; height: 6.7rem;
box-sizing: border-box;
} }
} }
} }

View File

@@ -2,13 +2,16 @@
<div class="login-page"> <div class="login-page">
<div class="content"> <div class="content">
<div class="back-button" @click="goBack"> <div class="back-button" @click="goBack">
<SvgIcon name="left" size="50" color="#fff" /> <SvgIcon name="left" size="50" color="#fff" />
</div> </div>
<div class="header"> <div class="header">
<div class="title">Log in.</div> <div class="title">Staff Login.</div>
<p class="subtitle">Redefine the styling experience with AI.</p> <p class="subtitle">
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p> <span>Experience our personalised styling journey with</span>
<br />
<span>Lane Crawford.</span>
</p>
</div> </div>
<div class="login-container"> <div class="login-container">
@@ -32,7 +35,11 @@
<img :src="google" class="google-icon" /> <img :src="google" class="google-icon" />
Sign in with Google Sign in with Google
</div> --> </div> -->
<GoogleLogin @googelLogin="handleGoogleLogin" ref="googleLoginRef" @click="clickGooleLogin"></GoogleLogin> <GoogleLogin
@googelLogin="handleGoogleLogin"
ref="googleLoginRef"
@click="clickGooleLogin"
></GoogleLogin>
<div class="sign-up-button" @click="handleSignup">Dont have an account? Sign Up</div> <div class="sign-up-button" @click="handleSignup">Dont have an account? Sign Up</div>
</div> </div>
</div> </div>
@@ -183,7 +190,7 @@ const handleSignup = () => {
cursor: pointer; cursor: pointer;
z-index: 3; z-index: 3;
font-size: 3.4rem; font-size: 3.4rem;
.c-svg{ .c-svg {
width: initial; width: initial;
height: initial; height: initial;
} }
@@ -191,12 +198,14 @@ const handleSignup = () => {
.header { .header {
margin-top: 1.42rem; margin-top: 1.42rem;
padding-left: 15.5rem; padding-left: 14.5rem;
padding-right: 14.3rem;
color: white; color: white;
font-family: 'satoshiRegular'; font-family: 'satoshiRegular';
.title { .title {
font-size: 11rem; font-size: 11rem;
font-weight: bold; font-weight: bold;
font-weight: 700;
margin-bottom: 0.8rem; margin-bottom: 0.8rem;
color: white; color: white;
font-family: 'satoshiBold'; font-family: 'satoshiBold';
@@ -226,11 +235,7 @@ const handleSignup = () => {
position: relative; position: relative;
width: calc(100% - 28.4rem); width: calc(100% - 28.4rem);
height: 107.8rem; height: 107.8rem;
background: radial-gradient( background: radial-gradient(100% 100% at 0% 0%, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.2) 100%);
100% 100% at 0% 0%,
rgba(0, 0, 0, 0.4) 0%,
rgba(0, 0, 0, 0.2) 100%
);
backdrop-filter: blur(35px); backdrop-filter: blur(35px);
-webkit-backdrop-filter: blur(35px); -webkit-backdrop-filter: blur(35px);
-moz-backdrop-filter: blur(35px); -moz-backdrop-filter: blur(35px);

View File

@@ -9,9 +9,12 @@
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="header"> <div class="header">
<div class="title">Log in.</div> <div class="title">Staff Login.</div>
<p class="subtitle">Redefine the styling experience with AI.</p> <p class="subtitle">
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p> <span>Experience our personalised styling journey with</span>
<br />
<span>Lane Crawford.</span>
</p>
</div> </div>
<div class="login-container"> <div class="login-container">

View File

@@ -6,9 +6,8 @@
</div> </div>
<div class="header"> <div class="header">
<div class="title">Sign up.</div> <div class="title">Staff Sign up.</div>
<p class="subtitle">Redefine the styling experience with AI.</p> <p class="subtitle">Start my personalised styling journey with Lane Crawford.</p>
<p class="subtitle">Use Styling Angel to speed up your fashion journey.</p>
</div> </div>
<div class="login-container"> <div class="login-container">
@@ -30,7 +29,12 @@
<button type="button" class="login-button" @click="handleConfirm">Sign Up</button> <button type="button" class="login-button" @click="handleConfirm">Sign Up</button>
<GoogleLogin text="Sign up with Google" @googelLogin="handleGoogleSignup" ref="googleSignupRef" @click="clickGooleLogin" /> <GoogleLogin
text="Sign up with Google"
@googelLogin="handleGoogleSignup"
ref="googleSignupRef"
@click="clickGooleLogin"
/>
</div> </div>
</div> </div>
</div> </div>
@@ -191,7 +195,8 @@ const handleGoogleSignup = async (accessToken: string) => {
.header { .header {
margin-top: 1.42rem; margin-top: 1.42rem;
padding-left: 15.5rem; padding-left: 14.5rem;
padding-right: 14.3rem;
color: white; color: white;
font-family: 'satoshiRegular'; font-family: 'satoshiRegular';
.title { .title {
@@ -281,7 +286,6 @@ const handleGoogleSignup = async (accessToken: string) => {
} }
} }
.footer { .footer {
position: relative; position: relative;
text-align: center; text-align: center;

View File

@@ -2,10 +2,14 @@
<div class="welcome-page safe-area-top safe-area-bottom"> <div class="welcome-page safe-area-top safe-area-bottom">
<div class="content"> <div class="content">
<div class="title"> <div class="title">
AI STYLING <br /> Styling <br />
ASSISTANT Assistant
</div> </div>
<p class="subtitle">Redefine the styling experience with AI.</p> <p class="subtitle">
<span>Experience our personalised styling journey with</span>
<br>
<span> Lane Crawford.</span>
</p>
<div class="btn-container flex"> <div class="btn-container flex">
<div class="log btn" @click="goToLogin">Log in</div> <div class="log btn" @click="goToLogin">Log in</div>
<div class="sign btn" @click="goToSignup">Sign up</div> <div class="sign btn" @click="goToSignup">Sign up</div>
@@ -49,15 +53,16 @@ const goToLogin = () => {
font-weight: 400; font-weight: 400;
margin-bottom: 31.5rem; margin-bottom: 31.5rem;
.title { .title {
font-family: 'boskaRegular'; font-family: 'satoshiMedium';
line-height: 93%; line-height: 120%;
letter-spacing: -0.02em; // letter-spacing: -0.02em;
} }
.subtitle { .subtitle {
font-size: 3.2rem; font-size: 3.2rem;
font-family: 'satoshiRegular'; font-family: 'satoshiRegular';
margin: 3.2rem 0 6rem; margin: 3.2rem 0 6rem;
list-style: 124%; font-weight: 400;
line-height: 124%;
letter-spacing: 0.08em; letter-spacing: 0.08em;
} }
.btn-container { .btn-container {

View File

@@ -25,8 +25,8 @@
<div class="text"> <div class="text">
<div class="form-title">{{ formTitle }}</div> <div class="form-title">{{ formTitle }}</div>
<div class="description"> <div class="description">
<p>Redefine the styling experience with AI.</p> <p>Unlock personalized styling insights.</p>
<p>Use Styling Angel to speed up your fashion journey.</p> <p>Enter a client profile to begin curating their next look with Styling Angel.</p>
</div> </div>
</div> </div>
@@ -70,7 +70,6 @@ import { customerCheckin, createCustomer, type CreateCustomerParams } from '@/ap
import Profile from '../Workshop/profile.vue' import Profile from '../Workshop/profile.vue'
import MyEvent from '@/utils/myEvent' import MyEvent from '@/utils/myEvent'
const profileRef = ref<typeof Profile>(null) const profileRef = ref<typeof Profile>(null)
const handleOpenProfile = () => { const handleOpenProfile = () => {
profileRef.value.open() profileRef.value.open()
@@ -107,6 +106,7 @@ const handleConfirm = async () => {
customerCheckin({ nickname: customerData.value.nickname }).then((res) => { customerCheckin({ nickname: customerData.value.nickname }).then((res) => {
useUserInfoStore().resetGenerateParams() useUserInfoStore().resetGenerateParams()
generateStore.setCustomerInfo(res) generateStore.setCustomerInfo(res)
MyEvent.emit('clear-generate-state')
router.push('/workshop/home') router.push('/workshop/home')
}) })
} else { } else {
@@ -213,6 +213,7 @@ const handleBack = (e?: Event) => {
line-height: 8.3rem; line-height: 8.3rem;
font-size: 4.8rem; font-size: 4.8rem;
border: 0.2rem solid #fff; border: 0.2rem solid #fff;
box-sizing: border-box;
} }
} }
} }

View File

@@ -39,6 +39,7 @@
<div class="tag-list short flex flex-justify-center"> <div class="tag-list short flex flex-justify-center">
<div <div
class="tag-item" class="tag-item"
:class="{ active: item === inputValue }"
v-for="item in tagListShort" v-for="item in tagListShort"
:key="item" :key="item"
@click="handleClickTag(item)" @click="handleClickTag(item)"
@@ -50,6 +51,7 @@
<div <div
class="tag-item" class="tag-item"
v-for="item in tagListLong" v-for="item in tagListLong"
:class="{ active: item === inputValue }"
:key="item" :key="item"
@click="handleClickTag(item)" @click="handleClickTag(item)"
> >
@@ -323,6 +325,9 @@ onUnmounted(() => {
text-align: center; text-align: center;
border-radius: 4.6rem; border-radius: 4.6rem;
padding: 0 2.15rem; padding: 0 2.15rem;
&.active{
background-color: #f5f5f5;
}
} }
} }
} }

View File

@@ -4,7 +4,8 @@
<div class="content"> <div class="content">
<!-- 标题 --> <!-- 标题 -->
<div class="header"> <div class="header">
<div class="title">CHOOSE YOUR STYLIST</div> <div class="title">Choose Stylist.</div>
<div class="sub-title">What style are you looking for? </div>
</div> </div>
<div class="carousel-container" v-show="!showVideo"> <div class="carousel-container" v-show="!showVideo">
@@ -35,7 +36,7 @@
<!-- Continue按钮 --> <!-- Continue按钮 -->
<div class="continue-button" @click="handleContinue" v-if="!$route.query?.demo">Continue</div> <div class="continue-button" @click="handleContinue" v-if="!$route.query?.demo">Continue</div>
<van-dialog <!-- <van-dialog
class="video-dialog" class="video-dialog"
:show-confirm-button="false" :show-confirm-button="false"
:show-cancel-button="false" :show-cancel-button="false"
@@ -47,7 +48,7 @@
<van-icon name="cross" class="close-icon" /> <van-icon name="cross" class="close-icon" />
</div> </div>
<Video ref="videoRef" /> <Video ref="videoRef" />
</van-dialog> </van-dialog> -->
</div> </div>
</template> </template>
@@ -57,7 +58,9 @@ import { useRouter } from 'vue-router'
import Video from './components/Video.vue' import Video from './components/Video.vue'
import { useUserInfoStore } from '@/stores' import { useUserInfoStore } from '@/stores'
import male from '@/assets/images/male.png' import male from '@/assets/images/male.png'
import maleThumb from '@/assets/images/male_thumb.png'
import female from '@/assets/images/female.png' import female from '@/assets/images/female.png'
import femaleThumb from '@/assets/images/female_thumb.png'
import HeaderTitle from '@/components/HeaderTitle.vue' import HeaderTitle from '@/components/HeaderTitle.vue'
import FooterNavigation from '@/components/FooterNavigation.vue' import FooterNavigation from '@/components/FooterNavigation.vue'
@@ -70,28 +73,32 @@ const stylists = ref<any[]>([
value: 'crystal', value: 'crystal',
name: 'Vera Lo', name: 'Vera Lo',
description: 'Contemporary, Classic, Simple Silhouettes, Statement Pieces', description: 'Contemporary, Classic, Simple Silhouettes, Statement Pieces',
image: female image: female,
thumb: femaleThumb
}, },
{ {
id: 2, id: 2,
value: 'mini', value: 'mini',
name: 'Sarah Chen', name: 'Sarah Chen',
description: 'Modern, Edgy, Bold Colors, Street Style', description: 'Modern, Edgy, Bold Colors, Street Style',
image: male image: male,
thumb: maleThumb
}, },
{ {
id: 3, id: 3,
value: 'crystal', value: 'crystal',
name: 'Emma Wilson', name: 'Emma Wilson',
description: 'Elegant, Feminine, Vintage Inspired, Soft Tones', description: 'Elegant, Feminine, Vintage Inspired, Soft Tones',
image: female image: female,
thumb: femaleThumb
}, },
{ {
id: 4, id: 4,
value: 'mini', value: 'mini',
name: 'Alex Johnson', name: 'Alex Johnson',
description: 'Minimalist, Professional, Neutral Palette, Clean Lines', description: 'Minimalist, Professional, Neutral Palette, Clean Lines',
image: male image: male,
thumb: maleThumb
} }
]) ])
const currentChoosed = ref(1) const currentChoosed = ref(1)
@@ -119,20 +126,22 @@ const handleClickStylist = (item: any) => {
const handleContinue = () => { const handleContinue = () => {
const generateParams = userInfoStore.getGenerateParams() const generateParams = userInfoStore.getGenerateParams()
generateParams.stylist = const selected = stylists.value.find((item) => item.id === currentChoosed.value)
stylists.value.find((item) => item.id === currentChoosed.value)?.value || '' generateParams.stylist = selected?.value
generateParams.stylistImage = selected?.thumb
userInfoStore.setGenerateParams(generateParams) userInfoStore.setGenerateParams(generateParams)
router.push('/workshop/stylist/sex') router.push('/workshop/stylist/sex')
} }
// 监听showVideo变化关闭时暂停视频 // 监听showVideo变化关闭时暂停视频
watch(showVideo, (newValue) => { // watch(showVideo, (newValue) => {
if (!newValue && videoRef.value) { // if (!newValue && videoRef.value) {
videoRef.value.pause() // videoRef.value.pause()
videoRef.value.reset() // videoRef.value.reset()
} // }
}) // })
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -164,11 +173,15 @@ watch(showVideo, (newValue) => {
text-align: center; text-align: center;
margin-bottom: 4rem; margin-bottom: 4rem;
.title { .title {
font-size: 15rem; font-size: 11rem;
font-weight: 400; font-weight: 400;
color: white; color: white;
font-family: 'boskaRegular';
line-height: 96%; line-height: 96%;
font-family: 'satoshiBold';
}
.sub-title{
font-family: 'satoshiRegular';
font-size: 4rem;
} }
} }
@@ -280,7 +293,7 @@ watch(showVideo, (newValue) => {
} }
.continue-button { .continue-button {
height: 5.9rem; height: 6.7rem;
box-sizing: border-box; box-sizing: border-box;
position: absolute; position: absolute;
bottom: 6.4rem; bottom: 6.4rem;

View File

@@ -55,13 +55,14 @@ const handleSelect = (value: string) => {
background-repeat: no-repeat; background-repeat: no-repeat;
padding: 6rem 12.4rem 0 8.5rem; padding: 6rem 12.4rem 0 8.5rem;
.text { .text {
font-family: 'robotoBold'; font-family: 'satoshiBold';
font-size: 13rem; font-weight: 700;
font-size: 11rem;
line-height: 106%; line-height: 106%;
} }
.desc { .desc {
font-family: 'satoshiRegular'; font-family: 'satoshiRegular';
font-size: 6.4rem; font-size: 4rem;
line-height: 132%; line-height: 132%;
} }
.select-list { .select-list {
@@ -76,7 +77,7 @@ const handleSelect = (value: string) => {
text-align: center; text-align: center;
font-family: 'satoshiRegular'; font-family: 'satoshiRegular';
font-size: 4.8rem; font-size: 4.8rem;
width: 29.7rem; width: 35rem;
height: 8.3rem; height: 8.3rem;
display: flex; display: flex;
align-items: center; align-items: center;