feat: 获取顾客列表&新建顾客

This commit is contained in:
2025-12-23 14:19:00 +08:00
parent b7abd2dbf9
commit b17637b2bc
5 changed files with 254 additions and 110 deletions

View File

@@ -8,26 +8,26 @@ import request from '@/utils/request'
* @param data.stylist 提示词 * @param data.stylist 提示词
* @param data.gender 原始试穿效果id * @param data.gender 原始试穿效果id
* @param data.num 是否重新生成 0-否1-是 * @param data.num 是否重新生成 0-否1-是
*/ */
export function generateRequestOutfit(data: Object) { export function generateRequestOutfit(data: Object) {
return request({ return request({
url: '/api/style/requestOutfit', url: '/api/style/requestOutfit',
method: 'post', method: 'post',
data, data
}) })
} }
/** /**
* 获取传达风格 * 获取传达风格
* @param data 获取传达风格数据 * @param data 获取传达风格数据
* @param data.requestIDs 获取生成结果的taskId * @param data.requestIDs 获取生成结果的taskId
*/ */
export function getRequestOutfit(data: Object) { export function getRequestOutfit(data: Object) {
return request({ return request({
url: '/api/style/getOutfitResult', url: '/api/style/getOutfitResult',
method: 'get', method: 'get',
params:data params: data
}) })
} }
/** /**
@@ -41,13 +41,13 @@ export function getRequestOutfit(data: Object) {
* @param data.prompt 提示词 * @param data.prompt 提示词
* @param data.originalTryOnId 原始试穿效果id * @param data.originalTryOnId 原始试穿效果id
* @param data.isRegenerated 是否重新生成 0-否1-是 * @param data.isRegenerated 是否重新生成 0-否1-是
*/ */
export function generateTryOnEffect(data: Object) { export function generateTryOnEffect(data: Object) {
return request({ return request({
url: '/api/try-on-effects/generate', url: '/api/try-on-effects/generate',
method: 'post', method: 'post',
data, data
}) })
} }
/** /**
* 生成试穿效果-演示 * 生成试穿效果-演示
@@ -55,13 +55,13 @@ export function generateTryOnEffect(data: Object) {
* @param data.customerPhotoId 顾客照片id * @param data.customerPhotoId 顾客照片id
* @param data.prompt 提示词 * @param data.prompt 提示词
* @param data.tryonUrl AI魔改url * @param data.tryonUrl AI魔改url
*/ */
export function generateTryOnEffectDemo(data: Object) { export function generateTryOnEffectDemo(data: Object) {
return request({ return request({
url: '/api/try-on-effects/reFace', url: '/api/try-on-effects/reFace',
method: 'post', method: 'post',
data, data
}) })
} }
/** 上传图片-AI换脸 /** 上传图片-AI换脸
@@ -71,96 +71,130 @@ export function generateTryOnEffectDemo(data: Object) {
* @param data.file 顾客照片文件 * @param data.file 顾客照片文件
*/ */
export function uploadCustomerPhoto(data: FormData) { export function uploadCustomerPhoto(data: FormData) {
return request({ return request({
url: '/api/customer-photos/upload', url: '/api/customer-photos/upload',
method: 'post', method: 'post',
data, data,
loading: true, loading: true
}) })
} }
/** /**
* 设置喜欢 * 设置喜欢
* @param tryOnId 试穿效果id * @param tryOnId 试穿效果id
*/ */
export function setTryOnEffectFavorite(tryOnId: string | number) { export function setTryOnEffectFavorite(tryOnId: string | number) {
if (!tryOnId) return Promise.reject('试穿效果id不能为空'); if (!tryOnId) return Promise.reject('试穿效果id不能为空')
return request({ return request({
url: `/api/try-on-effects/set-favorite/${tryOnId}`, url: `/api/try-on-effects/set-favorite/${tryOnId}`,
method: 'post', method: 'post'
}) })
} }
/** /**
* 取消喜欢 * 取消喜欢
* @param tryOnId 试穿效果id * @param tryOnId 试穿效果id
*/ */
export function cancelTryOnEffectFavorite(tryOnId: string | number) { export function cancelTryOnEffectFavorite(tryOnId: string | number) {
if (!tryOnId) return Promise.reject('试穿效果id不能为空'); if (!tryOnId) return Promise.reject('试穿效果id不能为空')
return request({ return request({
url: `/api/try-on-effects/cancel-favorite/${tryOnId}`, url: `/api/try-on-effects/cancel-favorite/${tryOnId}`,
method: 'post', method: 'post'
}) })
} }
/** 查询进店记录-library /** 查询进店记录-library
* @param customerId 客户id * @param customerId 客户id
*/ */
export function getCustomerPhotos(customerId: string | number) { export function getCustomerPhotos(customerId: string | number) {
if (!customerId) return Promise.reject('客户id不能为空'); if (!customerId) return Promise.reject('客户id不能为空')
return request({ return request({
url: `/api/visit-records/customer/${customerId}`, url: `/api/visit-records/customer/${customerId}`,
method: 'get', method: 'get'
}) })
} }
/** 删除进店记录-library /** 删除进店记录-library
* @param visitRecordId 进店记录id * @param visitRecordId 进店记录id
*/ */
export function deleteCustomerPhoto(visitRecordId: string | number) { export function deleteCustomerPhoto(visitRecordId: string | number) {
if (!visitRecordId) return Promise.reject('进店记录id不能为空'); if (!visitRecordId) return Promise.reject('进店记录id不能为空')
return request({ return request({
url: `/api/visit-records/${visitRecordId}`, url: `/api/visit-records/${visitRecordId}`,
method: 'delete', method: 'delete'
// loading: true, // loading: true,
}) })
} }
/** 查询收藏列表 /** 查询收藏列表
* @param visitRecordId 进店记录id * @param visitRecordId 进店记录id
*/ */
export function getTryOnEffectFavoriteList(visitRecordId: string | number) { export function getTryOnEffectFavoriteList(visitRecordId: string | number) {
if (!visitRecordId) return Promise.reject('进店记录id不能为空'); if (!visitRecordId) return Promise.reject('进店记录id不能为空')
return request({ return request({
url: `/api/try-on-effects/favorites/${visitRecordId}`, url: `/api/try-on-effects/favorites/${visitRecordId}`,
method: 'get', method: 'get'
}) })
} }
/** 查询某套试穿效果列表 /** 查询某套试穿效果列表
* @param styleId 服装id * @param styleId 服装id
*/ */
export function getTryOnEffectStyleList(styleId: string | number) { export function getTryOnEffectStyleList(styleId: string | number) {
if (!styleId) return Promise.reject('服装id不能为空'); if (!styleId) return Promise.reject('服装id不能为空')
return request({ return request({
url: `/api/try-on-effects/style/${styleId}`, url: `/api/try-on-effects/style/${styleId}`,
method: 'get', 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 { interface CustomerInfo {
vipId: string vipId: string
} }
export const customerCheckin = (data: CustomerInfo) => { export const customerCheckin = (data: CustomerInfo) => {
return request({ return request({
url: '/api/customers/checkIn', url: '/api/customers/checkIn',
method: 'get', method: 'get',
params: data, params: data
}) })
} }
// AI对话 // AI对话
interface AIConversation { interface AIConversation {
message: string message: string
sessionId: string | number //用户ID sessionId: string | number //用户ID
gender: 'male' | 'female' //性别 gender: 'male' | 'female' //性别
} }
export const streamChatAddress = '/api/llm/streamChat' export const streamChatAddress = '/api/llm/streamChat'
@@ -171,11 +205,11 @@ export const streamChatAddress = '/api/llm/streamChat'
* @param data.visitRecordId 进店记录id * @param data.visitRecordId 进店记录id
* @param data.customerId 顾客id * @param data.customerId 顾客id
* @param data.suggestion 意见和建议 * @param data.suggestion 意见和建议
*/ */
export function addTryOnEffectComment(data: Object) { export function addTryOnEffectComment(data: Object) {
return request({ return request({
url: '/api/try-on-effects/add-comment', url: '/api/try-on-effects/add-comment',
method: 'post', method: 'post',
data, data
}) })
} }

View File

@@ -7,6 +7,7 @@ class MyEvent {
MyEvent.list = MyEvent.list.filter(item => item.name != name && item.call != call) MyEvent.list = MyEvent.list.filter(item => item.name != name && item.call != call)
} }
emit(name, data) { emit(name, data) {
console.log('Myevent触发--',name)
MyEvent.list.forEach(item => { MyEvent.list.forEach(item => {
if (item.name == name) item.call(data) if (item.name == name) item.call(data)
}) })

View File

@@ -80,7 +80,7 @@ service.interceptors.response.use(
} }
const res = response.data const res = response.data
// 处理异常的情况 // 处理异常的情况
console.log(res) // console.log(res)
if (res.code != 0) { if (res.code != 0) {
showToast({ showToast({
message: res.errMsg || res.message, message: res.errMsg || res.message,

View File

@@ -4,6 +4,9 @@ import router from '@/router'
import { showConfirmDialog } from 'vant' import { showConfirmDialog } from 'vant'
import { useUserInfoStore, useOverallStore } from '@/stores' import { useUserInfoStore, useOverallStore } from '@/stores'
import { LogOut } from '@/api/login' import { LogOut } from '@/api/login'
import { getCustomerList, type CustomerListParams } from '@/api/workshop'
import MyEvent from '@/utils/myEvent'
const userInfoStore = useUserInfoStore() const userInfoStore = useUserInfoStore()
const overallStore = useOverallStore() const overallStore = useOverallStore()
const emit = defineEmits(['view-type', 'selected-customer']) const emit = defineEmits(['view-type', 'selected-customer'])
@@ -60,22 +63,84 @@ const logout = () => {
} }
const showSwitchCustomerPopup = ref(false) 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([ const loadCustomers = async (reset = false) => {
{ id: 'CUST001', nickname: 'Customer One', checked: true }, if (pager.loading) return
{ id: 'CUST002', nickname: 'Customer Two', checked: false }, if (reset) {
{ id: 'CUST003', nickname: 'Customer Three', checked: false }, custmerParams.value.current = 1
{ id: 'CUST004', nickname: 'Customer Four', checked: false }, pager.noMore = false
{ id: 'CUST005', nickname: 'Customer Five', checked: 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弹窗 // 打开customer选择时关闭profile弹窗 如果不是点击confirem关闭则重新打开profile弹窗
const handleShowPopup = (flag: boolean) => { const handleShowPopup = (flag: boolean) => {
showSwitchCustomerPopup.value = flag showSwitchCustomerPopup.value = flag
show.value = !flag show.value = !flag
if (flag) {
loadCustomers(true)
}
} }
const handleChangeChecked = (item) => { const handleChangeChecked = (item) => {
customerList.value.forEach((customer) => { customerList.value.forEach((customer) => {
customer.checked = customer.id === item.id customer.checked = customer.vipId === item.vipId
}) })
} }
const handleSelectCustomer = () => { const handleSelectCustomer = () => {
@@ -86,10 +151,19 @@ const handleSelectCustomer = () => {
showSwitchCustomerPopup.value = false showSwitchCustomerPopup.value = false
} }
const handleFetchCustomerList = () => {
loadCustomers(true)
}
MyEvent.add('update-customer-list', handleFetchCustomerList)
const openSwitchCustomerPopup = (flag = true) => { const openSwitchCustomerPopup = (flag = true) => {
showSwitchCustomerPopup.value = flag showSwitchCustomerPopup.value = flag
} }
onMounted(() => {
handleFetchCustomerList()
})
defineExpose({ open, close, openSwitchCustomerPopup }) defineExpose({ open, close, openSwitchCustomerPopup })
</script> </script>
@@ -185,16 +259,16 @@ defineExpose({ open, close, openSwitchCustomerPopup })
<div class="title-txt">Saved Customer ID</div> <div class="title-txt">Saved Customer ID</div>
<SvgIcon name="close_nocolor" color="#a1a1a1" size="40" @click="handleShowPopup(false)" /> <SvgIcon name="close_nocolor" color="#a1a1a1" size="40" @click="handleShowPopup(false)" />
</div> </div>
<div class="cusomter-list"> <div ref="customerListEl" class="cusomter-list" @scroll="onScroll">
<div <div
class="customer-item flex flex-align-center flex-between" class="customer-item flex flex-align-center flex-between"
v-for="item in customerList" v-for="(item, index) in customerList"
:key="item.id" :key="index + 'customer'"
@click="handleChangeChecked(item)" @click="handleChangeChecked(item)"
> >
<div class="info"> <div class="info">
<div class="name">{{ item.nickname }}</div> <div class="name">{{ item.name }}</div>
<div class="id">{{ item.id }}</div> <div class="id">{{ item.vipId }}</div>
</div> </div>
<div class="select-box"> <div class="select-box">
<div class="check-box flex flex-center" v-show="!item.checked"> <div class="check-box flex flex-center" v-show="!item.checked">
@@ -204,6 +278,10 @@ defineExpose({ open, close, openSwitchCustomerPopup })
</div> </div>
</div> </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> <div class="confirm-btn" @click="handleSelectCustomer">Confirm</div>
</van-popup> </van-popup>
</template> </template>
@@ -375,6 +453,12 @@ defineExpose({ open, close, openSwitchCustomerPopup })
} }
} }
} }
.list-footer {
text-align: center;
padding: 2rem 0;
font-size: 3rem;
color: #9b9b9b;
}
.confirm-btn { .confirm-btn {
margin: 4.9rem 12.7rem 0 13.3rem; margin: 4.9rem 12.7rem 0 13.3rem;
border: 0.25rem solid #3b3b3b; border: 0.25rem solid #3b3b3b;

View File

@@ -66,8 +66,10 @@ import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useGenerateStore, useUserInfoStore } from '@/stores' import { useGenerateStore, useUserInfoStore } from '@/stores'
import { showToast } from 'vant' import { showToast } from 'vant'
import { customerCheckin } from '@/api/workshop' import { customerCheckin, createCustomer, type CreateCustomerParams } from '@/api/workshop'
import Profile from '../Workshop/profile.vue' import Profile from '../Workshop/profile.vue'
import MyEvent from '@/utils/myEvent'
const profileRef = ref<typeof Profile>(null) const profileRef = ref<typeof Profile>(null)
const handleOpenProfile = () => { const handleOpenProfile = () => {
@@ -94,21 +96,43 @@ const customerData = ref({
}) })
const handleConfirm = async () => { const handleConfirm = async () => {
if (customerData.value.vipId === '') {
showToast({
message: 'please input name and email',
position: 'top'
})
return
}
if (pageMode.value === 'form') { 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() useUserInfoStore().resetGenerateParams()
generateStore.setCustomerInfo(res) generateStore.setCustomerInfo(res)
router.push('/workshop/home') router.push('/workshop/home')
}) })
} else { } 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) => { const handleSelectCustomer = (value) => {
console.log(value) if (value) {
// if (selected) { customerData.value.nickname = value.name
// customerData.value.nickname = selected.nickname }
// }
} }
const handleBack = (e?: Event) => { const handleBack = (e?: Event) => {
e.stopPropagation() if (e) {
e.preventDefault() e.stopPropagation()
e.preventDefault()
}
if (pageMode.value !== 'entry') { if (pageMode.value !== 'entry') {
pageMode.value = 'entry' pageMode.value = 'entry'
customerData.value = { customerData.value = {