Merge branch 'master' of https://gitee.com/lvYeJu/lane-crawford-3
This commit is contained in:
@@ -130,6 +130,7 @@ export function getTryOnEffectStyleList(styleId: string | number) {
|
||||
}
|
||||
|
||||
|
||||
// 选择顾客
|
||||
interface CustomerInfo {
|
||||
name: string
|
||||
email: string
|
||||
@@ -140,4 +141,12 @@ export const customerCheckin = (data: CustomerInfo) => {
|
||||
method: 'get',
|
||||
params: data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// AI对话
|
||||
interface AIConversation {
|
||||
message: string
|
||||
sessionId: string | number //用户ID
|
||||
gender: 'male' | 'female' //性别
|
||||
}
|
||||
export const streamChatAddress = '/api/llm/streamChat'
|
||||
1
src/assets/icons/mute.svg
Normal file
1
src/assets/icons/mute.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1761623835512" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8645" width="200" height="200"><path d="M426.666667 853.333333a42.666667 42.666667 0 0 1-26.453334-9.386666L198.4 682.666667H42.666667a42.666667 42.666667 0 0 1-42.666667-42.666667V384a42.666667 42.666667 0 0 1 42.666667-42.666667h155.733333l201.813333-161.28a42.666667 42.666667 0 0 1 42.666667-5.12A42.666667 42.666667 0 0 1 469.333333 213.333333v597.333334a42.666667 42.666667 0 0 1-24.32 38.4 38.826667 38.826667 0 0 1-18.346666 4.266666z m-341.333334-256h128a42.666667 42.666667 0 0 1 26.453334 9.386667L384 721.92V302.08l-144.213333 115.2A42.666667 42.666667 0 0 1 213.333333 426.666667H85.333333zM682.666667 682.666667a42.666667 42.666667 0 0 1-30.293334-12.373334 42.666667 42.666667 0 0 1 0-60.586666l256-256a42.666667 42.666667 0 0 1 60.586667 60.586666l-256 256A42.666667 42.666667 0 0 1 682.666667 682.666667z" p-id="8646" fill="#ffffff"></path><path d="M938.666667 682.666667a42.666667 42.666667 0 0 1-30.293334-12.373334l-256-256a42.666667 42.666667 0 0 1 60.586667-60.586666l256 256a42.666667 42.666667 0 0 1 0 60.586666A42.666667 42.666667 0 0 1 938.666667 682.666667z" p-id="8647" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
src/assets/icons/vol.svg
Normal file
1
src/assets/icons/vol.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1761623735176" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7161" width="200" height="200"><path d="M469.333333 853.333333a42.666667 42.666667 0 0 1-26.453333-9.386666L241.066667 682.666667H85.333333a42.666667 42.666667 0 0 1-42.666666-42.666667V384a42.666667 42.666667 0 0 1 42.666666-42.666667h155.733334l201.813333-161.28a42.666667 42.666667 0 0 1 42.666667-5.12A42.666667 42.666667 0 0 1 512 213.333333v597.333334a42.666667 42.666667 0 0 1-24.32 38.4 38.826667 38.826667 0 0 1-18.346667 4.266666z m-341.333333-256h128a42.666667 42.666667 0 0 1 26.88 9.386667l143.786667 115.2V302.08l-143.786667 115.2A42.666667 42.666667 0 0 1 256 426.666667H128zM813.653333 856.32a42.666667 42.666667 0 0 1-30.293333-12.373333 42.666667 42.666667 0 0 1 0-60.586667 384 384 0 0 0 0-542.72 42.666667 42.666667 0 0 1 60.586667-60.586667 469.333333 469.333333 0 0 1 0 663.893334 42.666667 42.666667 0 0 1-30.293334 12.373333z m-150.613333-151.04a42.666667 42.666667 0 0 1-30.293333-12.373333 42.666667 42.666667 0 0 1 0-60.586667 170.666667 170.666667 0 0 0 0-241.066667 42.666667 42.666667 0 0 1 60.586666-60.586666 256 256 0 0 1 0 362.24 42.666667 42.666667 0 0 1-30.293333 12.373333z" p-id="7162" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/asistant.png
Normal file
BIN
src/assets/images/asistant.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
@@ -6,10 +6,9 @@ import { removeLocal, setLocal } from '@/utils/local'
|
||||
export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
const state = ref({
|
||||
userInfo: {},
|
||||
customerId: '',
|
||||
token: '',
|
||||
generateParams: {
|
||||
stylistId: '',
|
||||
stylist: '',
|
||||
sex: ''
|
||||
}
|
||||
})
|
||||
@@ -22,10 +21,6 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
state.value.userInfo = data
|
||||
}
|
||||
|
||||
const setCustomerId = (data: string) => {
|
||||
state.value.customerId = data
|
||||
}
|
||||
|
||||
const setToken = (data: string) => {
|
||||
state.value.token = data
|
||||
setLocal(data, 'token')
|
||||
@@ -40,9 +35,8 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
}
|
||||
|
||||
const resetGenerateParams = () => {
|
||||
state.value.customerId = ''
|
||||
state.value.generateParams = {
|
||||
stylistId: '',
|
||||
stylist: '',
|
||||
sex: ''
|
||||
}
|
||||
}
|
||||
@@ -52,7 +46,6 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
return new Promise((resolve) => {
|
||||
state.value.token = ''
|
||||
state.value.userInfo = {}
|
||||
state.value.customerId = ''
|
||||
removeLocal('token')
|
||||
resetGenerateParams()
|
||||
resolve('')
|
||||
@@ -64,7 +57,6 @@ export const useUserInfoStore = defineStore('userInfo', () => {
|
||||
getUserInfo,
|
||||
setToken,
|
||||
setUserInfo,
|
||||
setCustomerId,
|
||||
setGenerateParams,
|
||||
getGenerateParams,
|
||||
resetGenerateParams,
|
||||
|
||||
@@ -67,6 +67,11 @@ service.interceptors.response.use(
|
||||
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
|
||||
*/
|
||||
(response: any) => {
|
||||
// 如果是llm/streamChat这样的流式接口,不走这样的处理
|
||||
if (response.config.url.includes('llm/streamChat')) {
|
||||
return response
|
||||
}
|
||||
|
||||
// 已完成请求的删除请求中数组
|
||||
removePending(response.config)
|
||||
// 关闭loading
|
||||
@@ -81,8 +86,8 @@ service.interceptors.response.use(
|
||||
message: res.errMsg || res.message,
|
||||
// type: 'fail',
|
||||
duration: 5000,
|
||||
position:'top',
|
||||
icon:'none'
|
||||
position: 'top',
|
||||
icon: 'none'
|
||||
})
|
||||
|
||||
return Promise.reject(new Error('error'))
|
||||
|
||||
@@ -42,6 +42,24 @@ const getMousePosition = (e: any, bor: any) => {
|
||||
}
|
||||
return event
|
||||
}
|
||||
/**
|
||||
* 生成UUID v4
|
||||
* @returns 返回一个标准的UUID v4字符串,格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
||||
*/
|
||||
export function generateUUID(): string {
|
||||
// 优先使用现代浏览器的crypto.randomUUID()方法
|
||||
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
|
||||
// 备用方案:手动生成UUID v4
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
getUniversalZoomLevel,
|
||||
getMousePosition,
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<template>
|
||||
<div
|
||||
class="chat-message"
|
||||
:class="{ 'user-message': isMyself, 'ai-message': !isMyself }"
|
||||
>
|
||||
<div class="chat-message" :class="{ 'user-message': isMyself, 'ai-message': !isMyself }">
|
||||
<!-- AI消息显示头像 -->
|
||||
<div v-if="!isMyself" class="chat-avatar">
|
||||
<img :src="value.thumb" alt="AI Avatar" />
|
||||
<img src="@/assets/images/asistant.png" alt="AI Avatar" />
|
||||
</div>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<div class="message-content">
|
||||
<div class="message-text">
|
||||
{{ value.content }}
|
||||
<div class="message-text" :class="{ streaming: isStreaming }">
|
||||
<div v-html="content"></div>
|
||||
<span v-if="isStreaming" class="streaming-cursor">|</span>
|
||||
</div>
|
||||
<!-- <div v-if="!isMyself" class="message-actions flex">
|
||||
<SvgIcon
|
||||
@@ -28,13 +26,18 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
|
||||
const md = new MarkdownIt()
|
||||
|
||||
// 定义消息类型
|
||||
interface ChatMessage {
|
||||
id: string
|
||||
sessionId: string | number
|
||||
type: string
|
||||
content: string
|
||||
userId: string
|
||||
time: string
|
||||
thumb: string
|
||||
timestamp: string
|
||||
id?: string
|
||||
self?: boolean
|
||||
}
|
||||
|
||||
// 定义操作项类型
|
||||
@@ -46,12 +49,17 @@ interface ActionItem {
|
||||
// 定义组件props类型
|
||||
interface NoticeItemProps {
|
||||
value: ChatMessage
|
||||
isStreaming?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<NoticeItemProps>()
|
||||
|
||||
const isMyself = computed(()=>{
|
||||
return props.value.userId === '1'
|
||||
const isMyself = computed(() => {
|
||||
return props.value.self || false
|
||||
})
|
||||
|
||||
const content = computed(() => {
|
||||
return md.render(props.value.content)
|
||||
})
|
||||
|
||||
const handleClickAction = (value: string): void => {
|
||||
@@ -117,6 +125,7 @@ const actionList: ActionItem[] = [
|
||||
.message-text {
|
||||
color: #000;
|
||||
border-radius: 0 2rem 2rem 2rem;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,12 +136,14 @@ const actionList: ActionItem[] = [
|
||||
height: 7.4rem;
|
||||
border-radius: 50%;
|
||||
margin-right: 1.88rem;
|
||||
border: 2px solid #000;
|
||||
padding: 1.4rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 4.9rem;
|
||||
height: 4.9rem;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
// object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +161,21 @@ const actionList: ActionItem[] = [
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.streaming-cursor {
|
||||
animation: blink 1s infinite;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
51%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div class="chat-list" ref="chatListRef">
|
||||
<div class="chat-message-item" v-for="message in list" :key="message.id">
|
||||
<NoticeItem :value="message" />
|
||||
<NoticeItem
|
||||
:value="message"
|
||||
:is-streaming="isStreaming && streamingMessage?.id === message.id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -21,6 +24,8 @@ interface ChatMessage {
|
||||
}
|
||||
const props = defineProps<{
|
||||
list: ChatMessage[]
|
||||
isStreaming?: boolean
|
||||
streamingMessage?: ChatMessage | null
|
||||
}>()
|
||||
|
||||
const emits = defineEmits(['send-message'])
|
||||
@@ -47,6 +52,17 @@ watch(
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 监听流式消息内容变化,实时滚动
|
||||
watch(
|
||||
() => props.streamingMessage?.content,
|
||||
async () => {
|
||||
if (props.isStreaming) {
|
||||
await nextTick()
|
||||
scrollToBottom()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleSendMessage = (message: string): void => {
|
||||
console.log('list发送消息:', message)
|
||||
emits('send-message', message)
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="content flex-1" v-if="!isLoading">
|
||||
<NoticeList ref="noticeListRef" :list="messageList" />
|
||||
<NoticeList
|
||||
ref="noticeListRef"
|
||||
:list="messageList"
|
||||
:is-streaming="isStreaming"
|
||||
:streaming-message="currentStreamingMessage"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer" v-if="!isLoading">
|
||||
<InputArea @send-message="handleSendMessage" />
|
||||
@@ -26,9 +31,12 @@ import InputArea from './components/InputArea.vue'
|
||||
import GenerateLoading from './components/GenerateLoading.vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { useUserInfoStore } from '@/stores'
|
||||
import { streamChatAddress } from '@/api/workshop'
|
||||
import { generateUUID } from '@/utils/tools'
|
||||
|
||||
const router = useRouter()
|
||||
const userInfoStore = useUserInfoStore()
|
||||
|
||||
defineOptions({
|
||||
name: 'asistant'
|
||||
@@ -40,47 +48,21 @@ interface NoticeListRef {
|
||||
}
|
||||
// 定义消息类型
|
||||
interface ChatMessage {
|
||||
id: string
|
||||
sessionId: string | number
|
||||
type: string
|
||||
content: string
|
||||
userId: string
|
||||
time: string
|
||||
thumb: string
|
||||
timestamp: string
|
||||
id?: string
|
||||
self?: boolean
|
||||
}
|
||||
|
||||
const isLoading = ref<boolean>(false)
|
||||
const noticeListRef = ref<NoticeListRef | null>(null)
|
||||
const messageList = ref<ChatMessage[]>([
|
||||
{
|
||||
id: '1',
|
||||
content: 'I want a chic outfit for dinner.',
|
||||
userId: '1',
|
||||
time: '14:30',
|
||||
thumb: ''
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
content:
|
||||
"Hey there! A chic dinner outfit, I love that! To give you the perfect recommendations, tell me: is this a romantic date, business dinner, or celebration with friends? And what's your go-to style vibe: classic elegance or something with more edge?",
|
||||
userId: '2',
|
||||
time: '14:31',
|
||||
thumb: 'https://files-dev.deercal.com/uploads/platforms/logo_code/669933e1b873e798.jpg'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
content: "It's a romantic date, and I prefer classic elegance.",
|
||||
userId: '1',
|
||||
time: '14:32',
|
||||
thumb: ''
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
content:
|
||||
"Perfect! For a romantic dinner with classic elegance, I'd suggest a little black dress with delicate jewelry and elegant heels. Would you like me to show you some specific options?",
|
||||
userId: '2',
|
||||
time: '14:33',
|
||||
thumb: 'https://files-dev.deercal.com/uploads/platforms/logo_code/669933e1b873e798.jpg'
|
||||
}
|
||||
])
|
||||
const messageList = ref<ChatMessage[]>([])
|
||||
|
||||
// 流式消息相关状态
|
||||
const isStreaming = ref<boolean>(false)
|
||||
const currentStreamingMessage = ref<ChatMessage | null>(null)
|
||||
|
||||
onMounted(() => {})
|
||||
|
||||
@@ -90,30 +72,134 @@ onUnmounted(() => {
|
||||
|
||||
const handleSendMessage = (message: string): void => {
|
||||
console.log('发送:', message)
|
||||
handleFetchMessage()
|
||||
|
||||
// 添加用户消息到列表
|
||||
const userMessage: ChatMessage = {
|
||||
id: generateUUID(),
|
||||
type: 'text',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionId: (userInfoStore.state.userInfo as any).id,
|
||||
self: true
|
||||
}
|
||||
messageList.value.push(userMessage)
|
||||
|
||||
// 开始流式接收AI回复
|
||||
handleFetchMessage(message)
|
||||
}
|
||||
|
||||
const abort = new AbortController()
|
||||
const handleFetchMessage = () => {
|
||||
fetchEventSource('/api/sse', {
|
||||
method: 'POST',
|
||||
openWhenHidden: true,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
signal: abort.signal,
|
||||
onopen: async (res) => {
|
||||
console.log('连接建立', res)
|
||||
},
|
||||
onmessage: (event) => {
|
||||
console.log('收到消息', event)
|
||||
},
|
||||
onerror: (error) => {
|
||||
console.log('错误', error)
|
||||
},
|
||||
onclose: () => {
|
||||
console.log('连接关闭')
|
||||
}
|
||||
const handleFetchMessage = (message: string) => {
|
||||
const params = {
|
||||
message: message,
|
||||
sessionId: (userInfoStore.state.userInfo as any).id,
|
||||
gender: 'male'
|
||||
}
|
||||
|
||||
// 创建AI消息对象
|
||||
const aiMessage: ChatMessage = {
|
||||
id: '',
|
||||
type: 'text',
|
||||
content: '',
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionId: (userInfoStore.state.userInfo as any).id
|
||||
}
|
||||
|
||||
// 添加到消息列表
|
||||
isStreaming.value = true
|
||||
messageList.value.push(aiMessage)
|
||||
currentStreamingMessage.value = aiMessage
|
||||
|
||||
// 直接使用 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()}`
|
||||
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
|
||||
|
||||
let contentBody = ''
|
||||
let buffer = ''
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) throw new Error('无法获取流读取器')
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
try {
|
||||
let flag = true
|
||||
while (flag) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
console.log('传输结束 end---', contentBody)
|
||||
isStreaming.value = false
|
||||
currentStreamingMessage.value = null
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
|
||||
// 优先按空行拆分事件块(SSE标准)
|
||||
let events = buffer.split(/\n\n/)
|
||||
buffer = events.pop() // 保留不完整块
|
||||
|
||||
for (let event of events) {
|
||||
if (!event.trim()) continue
|
||||
|
||||
// 过滤掉 id: 等字段,只取 data:
|
||||
const dataLines = event
|
||||
.split(/\n/)
|
||||
.filter((line) => line.startsWith('data:'))
|
||||
.map((line) => line.replace(/^data:\s*/, '').trim())
|
||||
|
||||
if (dataLines.length === 0) continue
|
||||
const jsonText = dataLines.join('\n')
|
||||
|
||||
try {
|
||||
const jsonData = JSON.parse(jsonText)
|
||||
if (jsonData.content && jsonData.content.length > 0 && jsonData.type !== 'end') {
|
||||
contentBody += jsonData.content
|
||||
currentStreamingMessage.value.content = contentBody
|
||||
}
|
||||
} catch (e) {
|
||||
// JSON 不完整:保留到下一次循环
|
||||
if (!jsonText.trim().endsWith('}')) {
|
||||
buffer = 'data: ' + jsonText // 重新放回缓存
|
||||
continue
|
||||
} else {
|
||||
console.warn('⚠️ JSON 格式错误,跳过:', jsonText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('流式传输错误:', error)
|
||||
isStreaming.value = false
|
||||
currentStreamingMessage.value = null
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('fetch请求失败:', error)
|
||||
isStreaming.value = false
|
||||
currentStreamingMessage.value = null
|
||||
})
|
||||
}
|
||||
|
||||
const handleContinue = () => {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<div class="control-right">
|
||||
<div class="volume-btn" @click="toggleMute">
|
||||
<!-- <van-icon :name="isMuted ? 'volume-mute' : 'volume'" /> -->
|
||||
<SvgIcon :name="isMuted ? 'vol' : 'vol_close'" size="20" />
|
||||
<SvgIcon :name="isMuted ? 'mute' : 'vol'" size="20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,13 +142,15 @@ defineExpose({
|
||||
<style scoped lang="less">
|
||||
.video-container {
|
||||
position: relative;
|
||||
height: 60rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 50rem;
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
object-fit: fill;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
// 视频控制条样式
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
<div class="title">CHOOSE YOUR STYLIST</div>
|
||||
</div>
|
||||
|
||||
<!-- 轮播容器 -->
|
||||
<div class="carousel-container">
|
||||
<!-- 左箭头 -->
|
||||
<div class="carousel-container" v-show="!showVideo">
|
||||
<div class="nav-arrow left" @click="handleChangeSwiper('prev')">
|
||||
<van-icon name="arrow-left" color="#fff" size="40" />
|
||||
</div>
|
||||
@@ -30,15 +28,6 @@
|
||||
|
||||
<div class="nav-arrow right" @click="handleChangeSwiper('next')">
|
||||
<van-icon name="arrow" color="#fff" size="40" />
|
||||
<!-- <svg width="15" height="26" viewBox="0 0 24 24" fill="none">
|
||||
<path
|
||||
d="M9 18L15 12L9 6"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,37 +61,40 @@ const userInfoStore = useUserInfoStore()
|
||||
const stylists = ref<any[]>([
|
||||
{
|
||||
id: 1,
|
||||
value: 'mini',
|
||||
name: 'Vera Lo',
|
||||
description: 'Contemporary, Classic, Simple Silhouettes, Statement Pieces',
|
||||
image: '/src/assets/images/female.png'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
value: 'crystal',
|
||||
name: 'Sarah Chen',
|
||||
description: 'Modern, Edgy, Bold Colors, Street Style',
|
||||
image: '/src/assets/images/male.png'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
value: 'mini',
|
||||
name: 'Emma Wilson',
|
||||
description: 'Elegant, Feminine, Vintage Inspired, Soft Tones',
|
||||
image: '/src/assets/images/female.png'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
value: 'crystal',
|
||||
name: 'Alex Johnson',
|
||||
description: 'Minimalist, Professional, Neutral Palette, Clean Lines',
|
||||
image: '/src/assets/images/male.png'
|
||||
}
|
||||
])
|
||||
const currentChoosed=ref('')
|
||||
const currentChoosed = ref(1)
|
||||
|
||||
const swiperRef = ref<any>(null)
|
||||
const showVideo = ref<boolean>(false)
|
||||
const videoRef = ref<any>(null)
|
||||
|
||||
|
||||
const handleChangeCurrent=(index:number)=>{
|
||||
const handleChangeCurrent = (index: number) => {
|
||||
currentChoosed.value = stylists.value[index].id
|
||||
}
|
||||
|
||||
@@ -121,7 +113,8 @@ const handleClickStylist = (item: any) => {
|
||||
|
||||
const handleContinue = () => {
|
||||
const generateParams = userInfoStore.getGenerateParams()
|
||||
generateParams.stylistId = currentChoosed.value
|
||||
generateParams.stylist =
|
||||
stylists.value.find((item) => item.id === currentChoosed.value)?.value || ''
|
||||
userInfoStore.setGenerateParams(generateParams)
|
||||
|
||||
router.push('/stylist/sex')
|
||||
@@ -286,9 +279,13 @@ watch(showVideo, (newValue) => {
|
||||
font-family: 'satoshiRegular';
|
||||
}
|
||||
|
||||
:deep(.van-overlay){
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.video-dialog) {
|
||||
width: 80%;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
width: calc(100% - 12rem);
|
||||
background: transparent;
|
||||
|
||||
.close-btn {
|
||||
width: 8.6rem;
|
||||
|
||||
@@ -23,8 +23,8 @@ const router = useRouter()
|
||||
const userInfoStore = useUserInfoStore()
|
||||
|
||||
const options = ref<any[]>([
|
||||
{ label: 'Female', value: '1' },
|
||||
{ label: 'Male', value: '0' }
|
||||
{ label: 'Female', value: 'female' },
|
||||
{ label: 'Male', value: 'male' }
|
||||
])
|
||||
|
||||
const handleSelect = (value: string) => {
|
||||
|
||||
Reference in New Issue
Block a user