From b17637b2bc303aaf1abe6035ce8f9bb5e78348be Mon Sep 17 00:00:00 2001 From: zhangyahui Date: Tue, 23 Dec 2025 14:19:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=8E=B7=E5=8F=96=E9=A1=BE=E5=AE=A2?= =?UTF-8?q?=E5=88=97=E8=A1=A8&=E6=96=B0=E5=BB=BA=E9=A1=BE=E5=AE=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/workshop.ts | 192 +++++++++++++++++++-------------- src/utils/myEvent.js | 1 + src/utils/request.ts | 2 +- src/views/Workshop/profile.vue | 112 ++++++++++++++++--- src/views/login/customer.vue | 57 +++++++--- 5 files changed, 254 insertions(+), 110 deletions(-) diff --git a/src/api/workshop.ts b/src/api/workshop.ts index aa88cb9..7236d3c 100644 --- a/src/api/workshop.ts +++ b/src/api/workshop.ts @@ -8,26 +8,26 @@ import request from '@/utils/request' * @param data.stylist 提示词 * @param data.gender 原始试穿效果id * @param data.num 是否重新生成 0-否,1-是 -*/ + */ export function generateRequestOutfit(data: Object) { - return request({ - url: '/api/style/requestOutfit', - method: 'post', - data, - }) + return request({ + url: '/api/style/requestOutfit', + method: 'post', + data + }) } /** * 获取传达风格 * @param data 获取传达风格数据 * @param data.requestIDs 获取生成结果的taskId -*/ + */ export function getRequestOutfit(data: Object) { - return request({ - url: '/api/style/getOutfitResult', - method: 'get', - params:data - }) + return request({ + url: '/api/style/getOutfitResult', + method: 'get', + params: data + }) } /** @@ -41,13 +41,13 @@ export function getRequestOutfit(data: Object) { * @param data.prompt 提示词 * @param data.originalTryOnId 原始试穿效果id * @param data.isRegenerated 是否重新生成 0-否,1-是 -*/ + */ export function generateTryOnEffect(data: Object) { - return request({ - url: '/api/try-on-effects/generate', - method: 'post', - data, - }) + return request({ + url: '/api/try-on-effects/generate', + method: 'post', + data + }) } /** * 生成试穿效果-演示 @@ -55,13 +55,13 @@ export function generateTryOnEffect(data: Object) { * @param data.customerPhotoId 顾客照片id * @param data.prompt 提示词 * @param data.tryonUrl AI魔改url -*/ + */ export function generateTryOnEffectDemo(data: Object) { - return request({ - url: '/api/try-on-effects/reFace', - method: 'post', - data, - }) + return request({ + url: '/api/try-on-effects/reFace', + method: 'post', + data + }) } /** 上传图片-AI换脸 @@ -71,96 +71,130 @@ export function generateTryOnEffectDemo(data: Object) { * @param data.file 顾客照片文件 */ export function uploadCustomerPhoto(data: FormData) { - return request({ - url: '/api/customer-photos/upload', - method: 'post', - data, - loading: true, - }) + return request({ + url: '/api/customer-photos/upload', + method: 'post', + data, + loading: true + }) } /** * 设置喜欢 * @param tryOnId 试穿效果id -*/ + */ export function setTryOnEffectFavorite(tryOnId: string | number) { - if (!tryOnId) return Promise.reject('试穿效果id不能为空'); - return request({ - url: `/api/try-on-effects/set-favorite/${tryOnId}`, - method: 'post', - }) + if (!tryOnId) return Promise.reject('试穿效果id不能为空') + return request({ + url: `/api/try-on-effects/set-favorite/${tryOnId}`, + method: 'post' + }) } /** * 取消喜欢 * @param tryOnId 试穿效果id -*/ + */ export function cancelTryOnEffectFavorite(tryOnId: string | number) { - if (!tryOnId) return Promise.reject('试穿效果id不能为空'); - return request({ - url: `/api/try-on-effects/cancel-favorite/${tryOnId}`, - method: 'post', - }) + if (!tryOnId) return Promise.reject('试穿效果id不能为空') + return request({ + url: `/api/try-on-effects/cancel-favorite/${tryOnId}`, + method: 'post' + }) } /** 查询进店记录-library * @param customerId 客户id */ export function getCustomerPhotos(customerId: string | number) { - if (!customerId) return Promise.reject('客户id不能为空'); - return request({ - url: `/api/visit-records/customer/${customerId}`, - method: 'get', - }) + if (!customerId) return Promise.reject('客户id不能为空') + return request({ + url: `/api/visit-records/customer/${customerId}`, + method: 'get' + }) } /** 删除进店记录-library * @param visitRecordId 进店记录id */ export function deleteCustomerPhoto(visitRecordId: string | number) { - if (!visitRecordId) return Promise.reject('进店记录id不能为空'); - return request({ - url: `/api/visit-records/${visitRecordId}`, - method: 'delete', - // loading: true, - }) + if (!visitRecordId) return Promise.reject('进店记录id不能为空') + return request({ + url: `/api/visit-records/${visitRecordId}`, + method: 'delete' + // loading: true, + }) } /** 查询收藏列表 * @param visitRecordId 进店记录id */ export function getTryOnEffectFavoriteList(visitRecordId: string | number) { - if (!visitRecordId) return Promise.reject('进店记录id不能为空'); - return request({ - url: `/api/try-on-effects/favorites/${visitRecordId}`, - method: 'get', - }) + if (!visitRecordId) return Promise.reject('进店记录id不能为空') + return request({ + url: `/api/try-on-effects/favorites/${visitRecordId}`, + method: 'get' + }) } /** 查询某套试穿效果列表 * @param styleId 服装id */ export function getTryOnEffectStyleList(styleId: string | number) { - if (!styleId) return Promise.reject('服装id不能为空'); - return request({ - url: `/api/try-on-effects/style/${styleId}`, - method: 'get', - }) + if (!styleId) return Promise.reject('服装id不能为空') + return request({ + url: `/api/try-on-effects/style/${styleId}`, + method: 'get' + }) } +// 获取当前Sales名下的customers列表 +export interface CustomerListParams { + current: number + size: number + desc: boolean + sortField?: 'createTime' + sortOrder?: 'DESC' | 'ASC' + keyword?: string + startTime?: string + endTime?: string + status?: number + offset?: number + limit?: number +} +export const getCustomerList = (data: CustomerListParams) => { + return request({ + url: '/api/customers/getAllCustomer', + method: 'post', + data + }) +} + +// 创建顾客 +export interface CreateCustomerParams { + nickname: string + vipId: string +} +export const createCustomer = (data: CreateCustomerParams) => { + return request({ + url: '/api/customers/createCustomer', + method: 'get', + params: data + }) +} // 选择顾客 interface CustomerInfo { vipId: string } -export const customerCheckin = (data: CustomerInfo) => { - return request({ - url: '/api/customers/checkIn', - method: 'get', - params: data, - }) +export const customerCheckin = (data: CustomerInfo) => { + return request({ + url: '/api/customers/checkIn', + method: 'get', + params: data + }) } // AI对话 interface AIConversation { - message: string - sessionId: string | number //用户ID - gender: 'male' | 'female' //性别 + message: string + sessionId: string | number //用户ID + gender: 'male' | 'female' //性别 } export const streamChatAddress = '/api/llm/streamChat' @@ -171,11 +205,11 @@ export const streamChatAddress = '/api/llm/streamChat' * @param data.visitRecordId 进店记录id * @param data.customerId 顾客id * @param data.suggestion 意见和建议 -*/ + */ export function addTryOnEffectComment(data: Object) { - return request({ - url: '/api/try-on-effects/add-comment', - method: 'post', - data, - }) -} \ No newline at end of file + return request({ + url: '/api/try-on-effects/add-comment', + method: 'post', + data + }) +} diff --git a/src/utils/myEvent.js b/src/utils/myEvent.js index e3c4e1b..78bf9cc 100644 --- a/src/utils/myEvent.js +++ b/src/utils/myEvent.js @@ -7,6 +7,7 @@ class MyEvent { MyEvent.list = MyEvent.list.filter(item => item.name != name && item.call != call) } emit(name, data) { + console.log('Myevent触发--',name) MyEvent.list.forEach(item => { if (item.name == name) item.call(data) }) diff --git a/src/utils/request.ts b/src/utils/request.ts index e047ce9..9a6e98c 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -80,7 +80,7 @@ service.interceptors.response.use( } const res = response.data // 处理异常的情况 - console.log(res) + // console.log(res) if (res.code != 0) { showToast({ message: res.errMsg || res.message, diff --git a/src/views/Workshop/profile.vue b/src/views/Workshop/profile.vue index e3dc126..c221087 100644 --- a/src/views/Workshop/profile.vue +++ b/src/views/Workshop/profile.vue @@ -4,6 +4,9 @@ import router from '@/router' import { showConfirmDialog } from 'vant' import { useUserInfoStore, useOverallStore } from '@/stores' import { LogOut } from '@/api/login' +import { getCustomerList, type CustomerListParams } from '@/api/workshop' +import MyEvent from '@/utils/myEvent' + const userInfoStore = useUserInfoStore() const overallStore = useOverallStore() const emit = defineEmits(['view-type', 'selected-customer']) @@ -60,22 +63,84 @@ const logout = () => { } const showSwitchCustomerPopup = ref(false) +const custmerParams = ref({ + current: 1, + size: 5, + desc: true +}) +const customerList = ref([]) +const pager = reactive({ + loading: false, + noMore: false, + total: null as number | null +}) +const customerListEl = ref(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 = () => { @@ -86,10 +151,19 @@ const handleSelectCustomer = () => { 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 }) @@ -185,16 +259,16 @@ defineExpose({ open, close, openSwitchCustomerPopup })
Saved Customer ID
-
+
-
{{ item.nickname }}
-
{{ item.id }}
+
{{ item.name }}
+
{{ item.vipId }}
@@ -204,6 +278,10 @@ defineExpose({ open, close, openSwitchCustomerPopup })
+
Confirm
@@ -375,6 +453,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; diff --git a/src/views/login/customer.vue b/src/views/login/customer.vue index ada4ffd..0e49d5d 100644 --- a/src/views/login/customer.vue +++ b/src/views/login/customer.vue @@ -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(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 = {