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.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,
})
}
return request({
url: '/api/try-on-effects/add-comment',
method: 'post',
data
})
}

View File

@@ -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)
})

View File

@@ -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,

View File

@@ -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<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 = () => {
@@ -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 })
</script>
@@ -185,16 +259,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 +278,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 +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;

View File

@@ -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 = {