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
All checks were successful
git提交控制 AiDA WEB-Node.js main 分支构建部署 / build (20.19.0) (push) Has been skipped
This commit is contained in:
@@ -8,7 +8,7 @@ import { FlowType } from '@/types/enum'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
const emit = defineEmits([
|
||||
'view-type'
|
||||
'viewType'
|
||||
])
|
||||
|
||||
// const data = reactive({
|
||||
@@ -23,7 +23,7 @@ const historicalReview = ()=>{
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
emit('view-type', 1)
|
||||
emit('viewType', 1)
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@ onMounted(()=>{
|
||||
flowTypeList: [FlowType.H_TRYON,FlowType.H_AI],
|
||||
},
|
||||
{
|
||||
path: 'recommended',
|
||||
path: 'product',
|
||||
imgPath: new URL('@/assets/images/nav2.png',import.meta.url).href,
|
||||
flowTypeList: [FlowType.H_OUTFIT],
|
||||
},
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, reactive, ref, toRefs } from "vue";
|
||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||
import FooterNavigation from '@/components/FooterNavigation.vue'
|
||||
import { onMounted, onUnmounted, reactive, ref, toRefs, computed } from "vue";
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
||||
import { useGenerateStore } from '@/stores'
|
||||
import { useGenerateStore, useHGenerateStore } from '@/stores'
|
||||
import { generateTryOnEffect, setTryOnEffectFavorite, cancelTryOnEffectFavorite, addTryOnEffectComment } from '@/api/workshop'
|
||||
import { FlowType } from '@/types/enum'
|
||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||
|
||||
const router = useRouter()
|
||||
//const props = defineProps({
|
||||
@@ -19,8 +17,11 @@ let data = reactive({
|
||||
],
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const query = computed(() => router.currentRoute.value.query)
|
||||
const generateStore = useGenerateStore()
|
||||
const hGenerateStore = useHGenerateStore()
|
||||
|
||||
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
||||
|
||||
const vanDialogShow = ref(false)
|
||||
const feedbackForm = ref({
|
||||
@@ -34,11 +35,10 @@ const feedbackForm = ref({
|
||||
|
||||
|
||||
const onContinue = ()=>{
|
||||
const query = router.currentRoute.value.query
|
||||
if(query?.flowType == FlowType.MAIN || !query?.flowType){
|
||||
router.push({ path: 'uploadFace', query: {...query} })
|
||||
if(!isHistoryFlow.value){
|
||||
router.push({ path: 'uploadFace', query: {...query.value} })
|
||||
}else{
|
||||
router.push({ path: 'creation', query: {...query} })
|
||||
router.push({ path: 'creation', query: {...query.value} })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ const startGenerate = ()=>{
|
||||
let value = {
|
||||
customerId:generateStore.customerId,
|
||||
visitRecordId:generateStore.visitRecordId,
|
||||
styleId:generateStore.styleId,
|
||||
styleId:isHistoryFlow.value ? hGenerateStore.styleId : generateStore.styleId,
|
||||
// customerPhotoId:null,
|
||||
// modelPhotoId:null,
|
||||
// prompt:null,
|
||||
@@ -63,7 +63,7 @@ const startGenerate = ()=>{
|
||||
generateStore.originalTryOn.isLike = false
|
||||
generateStore.originalTryOn.id = res.id
|
||||
generateStore.originalTryOn.tryOnUrl = res.tryOnUrl
|
||||
generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式
|
||||
// generateStore.useStyleGenerate()//生成后需要对选择衣服页面设置不可选中样式
|
||||
generateStore.setIsGenerate(false)
|
||||
|
||||
generateStore.clearCustomizeInfo()
|
||||
|
||||
@@ -4,6 +4,13 @@ import router from '@/router'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import { useUserInfoStore, useOverallStore } from '@/stores'
|
||||
import { LogOut } from '@/api/login'
|
||||
import { getCustomerList, type CustomerListParams,customerCheckin } from '@/api/workshop'
|
||||
import MyEvent from '@/utils/myEvent'
|
||||
|
||||
const props = defineProps<{
|
||||
isCustomer?: boolean
|
||||
}>()
|
||||
|
||||
const userInfoStore = useUserInfoStore()
|
||||
const overallStore = useOverallStore()
|
||||
const emit = defineEmits(['view-type', 'selected-customer'])
|
||||
@@ -60,22 +67,84 @@ const logout = () => {
|
||||
}
|
||||
|
||||
const showSwitchCustomerPopup = ref(false)
|
||||
const custmerParams = ref<CustomerListParams>({
|
||||
current: 1,
|
||||
size: 5,
|
||||
desc: true
|
||||
})
|
||||
const customerList = ref<any[]>([])
|
||||
const pager = reactive({
|
||||
loading: false,
|
||||
noMore: false,
|
||||
total: null as number | null
|
||||
})
|
||||
const customerListEl = ref<HTMLElement | null>(null)
|
||||
|
||||
const customerList = ref([
|
||||
{ id: 'CUST001', nickname: 'Customer One', checked: true },
|
||||
{ id: 'CUST002', nickname: 'Customer Two', checked: false },
|
||||
{ id: 'CUST003', nickname: 'Customer Three', checked: false },
|
||||
{ id: 'CUST004', nickname: 'Customer Four', checked: false },
|
||||
{ id: 'CUST005', nickname: 'Customer Five', checked: false }
|
||||
])
|
||||
const loadCustomers = async (reset = false) => {
|
||||
if (pager.loading) return
|
||||
if (reset) {
|
||||
custmerParams.value.current = 1
|
||||
pager.noMore = false
|
||||
customerList.value = []
|
||||
}
|
||||
if (pager.noMore) return
|
||||
pager.loading = true
|
||||
try {
|
||||
const res: any = await getCustomerList(custmerParams.value)
|
||||
let list: any[] = []
|
||||
const pages = res?.pages ?? res?.data?.pages ?? null
|
||||
if (res && Array.isArray(res.records)) {
|
||||
list = res.records
|
||||
pager.total = res.total ?? pager.total
|
||||
} else if (res && Array.isArray(res.data)) {
|
||||
list = res.data
|
||||
} else if (res && res.data && Array.isArray(res.data.list)) {
|
||||
list = res.data.list
|
||||
pager.total = res.data.total ?? pager.total
|
||||
}
|
||||
|
||||
if (reset) customerList.value = list
|
||||
else customerList.value = customerList.value.concat(list)
|
||||
|
||||
if (pages != null) {
|
||||
if (custmerParams.value.current >= pages) {
|
||||
pager.noMore = true
|
||||
} else {
|
||||
custmerParams.value.current += 1
|
||||
}
|
||||
} else {
|
||||
if (list.length === 0 || list.length < custmerParams.value.size) {
|
||||
pager.noMore = true
|
||||
} else {
|
||||
custmerParams.value.current += 1
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore error for now
|
||||
} finally {
|
||||
pager.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = (e: Event) => {
|
||||
const el = e.target as HTMLElement
|
||||
if (!el) return
|
||||
if (pager.loading || pager.noMore) return
|
||||
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 50) {
|
||||
loadCustomers(false)
|
||||
}
|
||||
}
|
||||
// 打开customer选择时关闭profile弹窗 如果不是点击confirem关闭则重新打开profile弹窗
|
||||
const handleShowPopup = (flag: boolean) => {
|
||||
showSwitchCustomerPopup.value = flag
|
||||
show.value = !flag
|
||||
show.value = !flag
|
||||
if (flag) {
|
||||
loadCustomers(true)
|
||||
}
|
||||
}
|
||||
const handleChangeChecked = (item) => {
|
||||
customerList.value.forEach((customer) => {
|
||||
customer.checked = customer.id === item.id
|
||||
customer.checked = customer.vipId === item.vipId
|
||||
})
|
||||
}
|
||||
const handleSelectCustomer = () => {
|
||||
@@ -83,13 +152,30 @@ const handleSelectCustomer = () => {
|
||||
if (selectedCustomer) {
|
||||
emit('selected-customer', selectedCustomer)
|
||||
}
|
||||
if(!props.isCustomer){
|
||||
// show.value = true
|
||||
customerCheckin({ nickname: selectedCustomer.name }).then((res) => {
|
||||
useUserInfoStore().resetGenerateParams()
|
||||
MyEvent.emit('clear-generate-state')
|
||||
useUserInfoStore().setCustomerInfo(res)
|
||||
})
|
||||
}
|
||||
showSwitchCustomerPopup.value = false
|
||||
}
|
||||
|
||||
const handleFetchCustomerList = () => {
|
||||
loadCustomers(true)
|
||||
}
|
||||
MyEvent.add('update-customer-list', handleFetchCustomerList)
|
||||
|
||||
const openSwitchCustomerPopup = (flag = true) => {
|
||||
showSwitchCustomerPopup.value = flag
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleFetchCustomerList()
|
||||
})
|
||||
|
||||
defineExpose({ open, close, openSwitchCustomerPopup })
|
||||
</script>
|
||||
|
||||
@@ -185,16 +271,16 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
||||
<div class="title-txt">Saved Customer ID</div>
|
||||
<SvgIcon name="close_nocolor" color="#a1a1a1" size="40" @click="handleShowPopup(false)" />
|
||||
</div>
|
||||
<div class="cusomter-list">
|
||||
<div ref="customerListEl" class="cusomter-list" @scroll="onScroll">
|
||||
<div
|
||||
class="customer-item flex flex-align-center flex-between"
|
||||
v-for="item in customerList"
|
||||
:key="item.id"
|
||||
v-for="(item, index) in customerList"
|
||||
:key="index + 'customer'"
|
||||
@click="handleChangeChecked(item)"
|
||||
>
|
||||
<div class="info">
|
||||
<div class="name">{{ item.nickname }}</div>
|
||||
<div class="id">{{ item.id }}</div>
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="id">{{ item.vipId }}</div>
|
||||
</div>
|
||||
<div class="select-box">
|
||||
<div class="check-box flex flex-center" v-show="!item.checked">
|
||||
@@ -204,6 +290,10 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-footer">
|
||||
<div v-if="pager.loading">Loading...</div>
|
||||
<div v-else-if="pager.noMore">No more</div>
|
||||
</div>
|
||||
<div class="confirm-btn" @click="handleSelectCustomer">Confirm</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
@@ -375,6 +465,12 @@ defineExpose({ open, close, openSwitchCustomerPopup })
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-footer {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
font-size: 3rem;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
.confirm-btn {
|
||||
margin: 4.9rem 12.7rem 0 13.3rem;
|
||||
border: 0.25rem solid #3b3b3b;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, reactive, toRefs, computed, onActivated } from "vue";
|
||||
import { onMounted, onUnmounted, reactive, toRefs, computed, ref } from "vue";
|
||||
import SelectItem from "@/components/selectStyle/selectItem.vue";
|
||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||
import FooterNavigation from '@/components/FooterNavigation.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useGenerateStore, useUserInfoStore } from '@/stores'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useGenerateStore, useUserInfoStore, useHGenerateStore } from '@/stores'
|
||||
import { showToast } from 'vant';
|
||||
import { generateRequestOutfit, getRequestOutfit } from '@/api/workshop'
|
||||
import { FlowType } from '@/types/enum'
|
||||
import { shareImageToWhatsapp } from '@/utils/tools'
|
||||
import { generateRequestOutfit, getRequestOutfit, setStyleFavorite, cancelStyleFavorite, retrieveAndRegenerate } from '@/api/workshop'
|
||||
import { FlowType, IsHistoryFlow } from '@/types/enum'
|
||||
import GenerateLoading from '@/views/asistant/components/GenerateLoading.vue'
|
||||
import gradientButton from '@/components/gradientButton.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const route = useRoute()
|
||||
//const props = defineProps({
|
||||
//})
|
||||
const emit = defineEmits([
|
||||
@@ -18,125 +19,190 @@ const emit = defineEmits([
|
||||
])
|
||||
const generateStore = useGenerateStore()
|
||||
const userInfoStore = useUserInfoStore()
|
||||
const hGenerateStore = useHGenerateStore()
|
||||
|
||||
const query = computed(() => route.query)
|
||||
const isHistoryFlow = computed(() => IsHistoryFlow(query.value.flowType))
|
||||
const isLoading = ref(false)
|
||||
const loadingTitle= ref('Analyzing the Outfit...')
|
||||
// const loadingTitle = computed(()=>{
|
||||
// let str = ''
|
||||
// if(!select.value.status)str = 'Analyzing the Outfit...'
|
||||
// if(select.value.status == 'RUNNING')str = 'Generating Results...'
|
||||
// if(select.value.status == 'PENDING')str = 'Almost there...'
|
||||
// return str
|
||||
// })
|
||||
|
||||
let data = reactive({
|
||||
select:computed(()=>generateStore.style),
|
||||
styleList:computed(()=>generateStore.styleList),
|
||||
// styleList:computed(()=>generateStore.styleList),
|
||||
})
|
||||
|
||||
let getGenerateTime = null as any
|
||||
|
||||
const selectItem = (item)=>{
|
||||
if(!item.id || item.status != 'SUCCEEDED'){
|
||||
return
|
||||
const updateStyle = ()=>{
|
||||
// generateStore.updateStyle(item)
|
||||
// data.styleList[index] = {}
|
||||
requestOutfit({num:1})
|
||||
}
|
||||
const setLikeStyle = (likeStyle)=>{
|
||||
if(!select.value.id)return
|
||||
if(likeStyle){
|
||||
cancelStyleFavorite(select.value.id).then(()=>{
|
||||
select.value.isLike = false
|
||||
})
|
||||
}else{
|
||||
setStyleFavorite(select.value.id).then(()=>{
|
||||
select.value.isLike = true
|
||||
})
|
||||
}
|
||||
generateStore.selectStyle(item)
|
||||
}
|
||||
|
||||
const updateStyle = ({item,index})=>{
|
||||
generateStore.updateStyle(item)
|
||||
data.styleList[index] = {}
|
||||
requestOutfit({num:1,index})
|
||||
const setDownload = ()=>{
|
||||
if(select.value.path)shareImageToWhatsapp(select.value.path)
|
||||
}
|
||||
|
||||
const toProduct = ()=>{
|
||||
if(!generateStore.style.id){
|
||||
showToast({ message: 'Please select a style.' });
|
||||
return
|
||||
}
|
||||
if(generateStore.style.id != generateStore.style.oldId){
|
||||
generateStore.setIsGenerate(true)
|
||||
}
|
||||
const query = router.currentRoute.value.query
|
||||
if(query?.flowType == FlowType.MAIN || !query?.flowType){
|
||||
router.push({ path: 'product', query: {...query} })
|
||||
// if(generateStore.style.id){
|
||||
// generateStore.setIsGenerate(true)
|
||||
// }
|
||||
if(!isHistoryFlow.value){
|
||||
router.push({ path: 'product', query: {...query.value} })
|
||||
}else{
|
||||
router.push({ path: 'creation', query: {...query} })
|
||||
router.push({ path: 'creation', query: {...query.value, active: FlowType.H_OUTFIT} })
|
||||
}
|
||||
}
|
||||
|
||||
const requestOutfit = ({num,index})=>{
|
||||
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)=>{
|
||||
let rvIndex = 0
|
||||
data.styleList.forEach((item,styleIndex)=>{
|
||||
if(styleIndex < index)return
|
||||
item.taskId = rv[rvIndex]
|
||||
rvIndex++
|
||||
})
|
||||
getRequestOutfitList(rv)
|
||||
const requestOutfit = async ({num})=>{
|
||||
let rv = await new Promise<void>((resolve, reject) => {
|
||||
if(isHistoryFlow.value){
|
||||
retrieveAndRegenerate({tryOnEffectsId:hGenerateStore.originalTryOn.id}).then((rv:any)=>{
|
||||
resolve(rv)
|
||||
})
|
||||
}else{
|
||||
let value = {
|
||||
"customerId": generateStore.customerId,
|
||||
"checkInId": generateStore.visitRecordId,
|
||||
"stylist": userInfoStore.state.generateParams.stylist,
|
||||
"gender": userInfoStore.state.generateParams.sex,
|
||||
"sessionId": generateStore.sessionId,
|
||||
num,
|
||||
}
|
||||
generateRequestOutfit(value).then((rv:any)=>{
|
||||
resolve(rv)
|
||||
})
|
||||
}
|
||||
})
|
||||
isLoading.value = true
|
||||
generateStore.clearStyle()
|
||||
data.select.taskId = rv[0]
|
||||
getRequestOutfitList(rv)
|
||||
}
|
||||
|
||||
const getRequestOutfitList = (generateList)=>{
|
||||
let value = {requestIDs:generateList.join(',')}
|
||||
getRequestOutfit(value).then((rv:any)=>{
|
||||
let pendingList = []
|
||||
rv.forEach((item)=>{
|
||||
if(['RUNNING','PENDING'].includes(item.status))pendingList.push(item.requestId)
|
||||
let index = data.styleList.findIndex((styleItem)=>styleItem.taskId == item.requestId)
|
||||
if(index != -1){
|
||||
data.styleList[index].id = item.id
|
||||
data.styleList[index].path = item.path
|
||||
data.styleList[index].status = item.status
|
||||
}
|
||||
data.select.id = item.id
|
||||
data.select.path = item.path
|
||||
data.select.status = item.status
|
||||
})
|
||||
|
||||
if(pendingList.length > 0){
|
||||
if(['RUNNING','PENDING'].includes(data.select.status)){
|
||||
getGenerateTime = setTimeout(()=>{
|
||||
getRequestOutfitList(pendingList)
|
||||
getRequestOutfitList([data.select.taskId])
|
||||
},3000)
|
||||
}else{
|
||||
isLoading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
// generateStore.clearProductData()
|
||||
|
||||
emit('view-type', 1)
|
||||
// if(!data.styleList[0]?.id)getRequestOutfitList(0)
|
||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
||||
if(!data.styleList[0]?.taskId){
|
||||
requestOutfit({num:4,index:0})
|
||||
}else if(data.styleList.filter((item)=>item?.status != 'SUCCEEDED').length > 0){
|
||||
let generateList = data.styleList.map((item)=>item.taskId)
|
||||
getRequestOutfitList(generateList)
|
||||
if(data.select.status == 'SUCCEEDED'){
|
||||
return
|
||||
}else if(!data.select?.taskId){
|
||||
requestOutfit({num:1})
|
||||
}else if(data.select.status != 'SUCCEEDED'){
|
||||
isLoading.value = true
|
||||
// let generateList = [data.styleList[0].taskId]
|
||||
getRequestOutfitList([data.select.taskId])
|
||||
}
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
if(getGenerateTime)clearTimeout(getGenerateTime)
|
||||
})
|
||||
defineExpose({})
|
||||
const { styleList, select } = toRefs(data);
|
||||
const { select } = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="selectStyle">
|
||||
<div class="text">
|
||||
<div class="title">
|
||||
What’s your Style?
|
||||
Outfit Result
|
||||
</div>
|
||||
<div class="info">
|
||||
Select the outfit that matches you the most.
|
||||
Refine your Look
|
||||
</div>
|
||||
</div>
|
||||
<div class="selectContent flex-1">
|
||||
<SelectItem :selectList="styleList" v-model:select="select" @selectItem="selectItem" @updateStyle="updateStyle" />
|
||||
<div class="selectContent">
|
||||
<!-- {{ select }} -->
|
||||
<div class="imgBox">
|
||||
<img :src="select.path" alt="">
|
||||
</div>
|
||||
<div class="btn">
|
||||
<div class="like" @click.stop="setLikeStyle(select.isLike)">
|
||||
<SvgIcon :name="`love_${select.isLike?1:0}`" size="35" />
|
||||
</div>
|
||||
<div class="down" @click.stop="setDownload()">
|
||||
<SvgIcon name="download" size="35" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button class="flex flex-center" @click.stop="toProduct">Continue</button>
|
||||
<div class="btn">
|
||||
<div class="btnItem style1" @click.stop="updateStyle()">
|
||||
<gradientButton>
|
||||
<template #content>
|
||||
<div class="text">
|
||||
<span class="icon">
|
||||
<SvgIcon name="reTry" size="40" />
|
||||
</span>
|
||||
Re-try
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</gradientButton>
|
||||
</div>
|
||||
<div class="btnItem style2" @click.stop="toProduct">Continue</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="footer placeholder"></div> -->
|
||||
<div class="loading-container" v-if="isLoading">
|
||||
<GenerateLoading :title="loadingTitle"/>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.header-title {
|
||||
// --header-title-background: #f6f6f6;
|
||||
}
|
||||
.loading-container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 2;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.selectStyle{
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
@@ -149,12 +215,12 @@ const { styleList, select } = toRefs(data);
|
||||
> .text{
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 4.9rem;
|
||||
margin-top: 8.5rem;
|
||||
margin-bottom: 8.5rem;
|
||||
> .title{
|
||||
font-family: satoshiBold;
|
||||
font-weight: 700;
|
||||
font-size: 9.6rem;
|
||||
font-size: 8.6rem;
|
||||
line-height: 124%;
|
||||
color: #000;
|
||||
}
|
||||
@@ -162,46 +228,74 @@ const { styleList, select } = toRefs(data);
|
||||
font-size: 4rem;
|
||||
font-weight: 400;
|
||||
line-height: 124%;
|
||||
margin-top: 1.3rem;
|
||||
margin-top: 3.2rem;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
}
|
||||
.selectContent{
|
||||
padding: 0 4rem;
|
||||
// padding: 0 4rem;
|
||||
margin: 0 auto;
|
||||
overflow: auto;
|
||||
width: 73.7rem;
|
||||
margin-bottom: 19.4rem;
|
||||
> .imgBox{
|
||||
height: 73.7rem;
|
||||
width: 100%;
|
||||
margin-bottom: 4.4rem;
|
||||
> img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
> .btn{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 2rem;
|
||||
> div{
|
||||
color: #000;
|
||||
border-radius: 50%;
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
padding: 1rem;
|
||||
background-color: #fff;
|
||||
&:hover{
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
// position: fixed;
|
||||
// margin-top: 4.4rem;
|
||||
// padding: 4rem 4rem 0 0;
|
||||
margin-top: auto;
|
||||
margin-bottom: 5.6rem;
|
||||
margin-right: 4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
// position: absolute;
|
||||
// right: 4rem;
|
||||
// bottom: 4rem;
|
||||
height: 6.7rem;
|
||||
// background-color: #f6f6f6;
|
||||
&.placeholder{
|
||||
position: relative;
|
||||
}
|
||||
> button {
|
||||
// margin-right: 5rem;
|
||||
border-radius: .7rem;
|
||||
border: 3px solid #000;
|
||||
background-color: #000;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-family: satoshiMedium;
|
||||
font-size: 3.6rem;
|
||||
width: 24.6rem;
|
||||
height: 6.7rem;
|
||||
box-sizing: border-box;
|
||||
line-height: 6.7rem;
|
||||
> .btn{
|
||||
display: flex;
|
||||
gap: 6.6rem;
|
||||
justify-content: center;
|
||||
> div {
|
||||
border-radius: .96rem;
|
||||
width: 33.7rem;
|
||||
font-size: 4.8rem;
|
||||
font-family: satoshiMedium;
|
||||
line-height: 9.2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
&.style1{
|
||||
--borderRadius: .96rem;
|
||||
--borderWidth: 2px;
|
||||
.text{
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
> .icon{
|
||||
left: 4rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.style2{
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,12 +4,19 @@
|
||||
<img src="@/assets/images/chat_loading.png" alt="Loading" class="loading-image" />
|
||||
<!-- 阴影效果 -->
|
||||
<div class="loading-shadow"></div>
|
||||
<div class="loading-text">{{ title }}</div>
|
||||
<div class="loading-text" ref="textBox">
|
||||
<span>{{ text }}</span>
|
||||
<div class="loading-dot" ref="dotBox"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, watch, reactive, toRefs, nextTick, ref } from "vue";
|
||||
import { gsap, TweenMax } from "gsap";
|
||||
|
||||
|
||||
// 这个组件只负责显示loading动画
|
||||
|
||||
// 定义组件props类型
|
||||
@@ -20,6 +27,76 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const dotBox = ref(null)
|
||||
const textBox = ref(null)
|
||||
let tl1 = null;
|
||||
const text = ref('')
|
||||
|
||||
watch(() => props.title, (newVal, oldVal) => {
|
||||
if (newVal !== oldVal) {
|
||||
destroyAnimation()
|
||||
fadeOut(newVal)
|
||||
}
|
||||
})
|
||||
|
||||
function fadeOut(newVal) {
|
||||
gsap.to(textBox.value,.5, {
|
||||
opacity: 0,
|
||||
duration: 1,
|
||||
ease: "power2.in",
|
||||
onComplete: () => {
|
||||
// 消失后更换文字
|
||||
text.value = newVal
|
||||
// 然后出现
|
||||
fadeIn();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 出现动画
|
||||
function fadeIn() {
|
||||
gsap.to(textBox.value,.5, {
|
||||
opacity: 1,
|
||||
duration: 1,
|
||||
onComplete:()=>{
|
||||
setTl1();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const setTl1 = ()=>{
|
||||
nextTick(()=>{
|
||||
let el = dotBox.value
|
||||
let width = el.offsetWidth + el.parentElement.offsetWidth
|
||||
let time = el.parentElement.offsetWidth / 35
|
||||
console.log(time,width)
|
||||
tl1 = gsap.timeline();
|
||||
tl1.to(el,time,
|
||||
{
|
||||
ease: "power1.in",
|
||||
left:width,
|
||||
onComplete:()=>{
|
||||
setTimeout(() => {
|
||||
tl1.restart()
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
)
|
||||
tl1.progress(0);
|
||||
})
|
||||
}
|
||||
|
||||
function destroyAnimation() {
|
||||
if (tl1) {
|
||||
tl1.progress(0);
|
||||
tl1.kill();
|
||||
tl1 = null;
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
text.value = props.title
|
||||
setTl1()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -54,6 +131,18 @@ const props = defineProps({
|
||||
font-size: 4.8rem;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 124%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-dot{
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translate(-100%, -50%);
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
|
||||
@@ -3,32 +3,26 @@
|
||||
<div class="header">
|
||||
<HeaderTitle hasSetting styleType="3" />
|
||||
</div>
|
||||
<div class="loading-container" v-if="isLoading">
|
||||
<GenerateLoading />
|
||||
<div class="content flex-1">
|
||||
<NoticeList
|
||||
ref="noticeListRef"
|
||||
:list="messageList"
|
||||
:is-streaming="isStreaming"
|
||||
:streaming-message="currentStreamingMessage"
|
||||
/>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="content flex-1" v-if="!isLoading">
|
||||
<NoticeList
|
||||
ref="noticeListRef"
|
||||
:list="messageList"
|
||||
:is-streaming="isStreaming"
|
||||
:streaming-message="currentStreamingMessage"
|
||||
/>
|
||||
<div class="footer">
|
||||
<InputArea @send-message="handleSendMessage" />
|
||||
<div class="continue flex">
|
||||
<div class="btn flex flex-center" @click="handleContinue">Continue</div>
|
||||
</div>
|
||||
<div class="footer" v-if="!isLoading">
|
||||
<InputArea @send-message="handleSendMessage" />
|
||||
<div class="continue flex">
|
||||
<div class="btn flex flex-center" @click="handleContinue">Continue</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import HeaderTitle from '@/components/HeaderTitle.vue'
|
||||
import NoticeList from './components/NoticeList.vue'
|
||||
import InputArea from './components/InputArea.vue'
|
||||
import GenerateLoading from './components/GenerateLoading.vue'
|
||||
import { ref, onMounted, onUnmounted, onActivated } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useUserInfoStore, useGenerateStore } from '@/stores'
|
||||
@@ -60,7 +54,6 @@ interface ChatMessage {
|
||||
self?: boolean
|
||||
}
|
||||
|
||||
const isLoading = ref<boolean>(false)
|
||||
const noticeListRef = ref<NoticeListRef | null>(null)
|
||||
const messageList = ref<ChatMessage[]>([])
|
||||
|
||||
@@ -273,12 +266,8 @@ const handleFetchMessage = (message: string) => {
|
||||
const handleContinue = () => {
|
||||
// router.push('/workshop/selectStyle')
|
||||
// 模拟接口之后再跳转
|
||||
isLoading.value = true
|
||||
generateStore.clearProductData()
|
||||
setTimeout(() => {
|
||||
router.push('/workshop/selectStyle')
|
||||
isLoading.value = false
|
||||
}, 1000)
|
||||
router.push('/workshop/selectStyle')
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@@ -315,12 +304,4 @@ const handleContinue = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Profile ref="profileRef" @selected-customer="handleSelectCustomer" />
|
||||
<Profile ref="profileRef" @selected-customer="handleSelectCustomer" is-customer />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -66,8 +66,10 @@ import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useGenerateStore, useUserInfoStore } from '@/stores'
|
||||
import { showToast } from 'vant'
|
||||
import { customerCheckin } from '@/api/workshop'
|
||||
import { customerCheckin, createCustomer, type CreateCustomerParams } from '@/api/workshop'
|
||||
import Profile from '../Workshop/profile.vue'
|
||||
import MyEvent from '@/utils/myEvent'
|
||||
|
||||
|
||||
const profileRef = ref<typeof Profile>(null)
|
||||
const handleOpenProfile = () => {
|
||||
@@ -94,21 +96,43 @@ const customerData = ref({
|
||||
})
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (customerData.value.vipId === '') {
|
||||
showToast({
|
||||
message: 'please input name and email',
|
||||
position: 'top'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (pageMode.value === 'form') {
|
||||
await customerCheckin({ nickname: customerData.value.nickname }).then((res) => {
|
||||
if (customerData.value.nickname === '') {
|
||||
showToast({
|
||||
message: 'please input the nickname'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
customerCheckin({ nickname: customerData.value.nickname }).then((res) => {
|
||||
useUserInfoStore().resetGenerateParams()
|
||||
generateStore.setCustomerInfo(res)
|
||||
router.push('/workshop/home')
|
||||
})
|
||||
} else {
|
||||
// 创建接口
|
||||
if (customerData.value.vipId === '') {
|
||||
showToast({
|
||||
message: 'please input the VIP ID'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (customerData.value.nickname === '') {
|
||||
showToast({
|
||||
message: 'please input the nickname'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
createCustomer({
|
||||
nickname: customerData.value.nickname,
|
||||
vipId: customerData.value.vipId
|
||||
} as CreateCustomerParams).then((res) => {
|
||||
showToast({
|
||||
message: 'Customer created successfully'
|
||||
})
|
||||
handleBack()
|
||||
MyEvent.emit('update-customer-list')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,15 +142,16 @@ const handleShowPopup = (flag: Boolean) => {
|
||||
}
|
||||
|
||||
const handleSelectCustomer = (value) => {
|
||||
console.log(value)
|
||||
// if (selected) {
|
||||
// customerData.value.nickname = selected.nickname
|
||||
// }
|
||||
if (value) {
|
||||
customerData.value.nickname = value.name
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = (e?: Event) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
if (pageMode.value !== 'entry') {
|
||||
pageMode.value = 'entry'
|
||||
customerData.value = {
|
||||
|
||||
Reference in New Issue
Block a user