feat: collect customer identity for assisted bookings
This commit is contained in:
parent
f192d89534
commit
ec4015aaba
@ -135,6 +135,10 @@ npm run dev:h5
|
||||
|
||||
当前实现:**提交时登录 + guest 草稿恢复**。未登录用户可填写预约表单,提交时跳转登录页(带 `redirect` 回预约页并保留 `storeId`),登录成功后回到预约页并恢复 guest 草稿(草稿 key 不依赖 `userInfo.id`,避免 `userId: undefined`)。登录态用户继续按 `userId` 维度保存草稿。草稿有效期 7 天。
|
||||
|
||||
### 门店代客预约身份
|
||||
|
||||
老板/员工在首页新建预约时必须填写宠主手机号,可选填宠主称呼。后端按手机号关联或创建 customer,并分别记录 `customerUserId` 与 `createdByUserId`;前端不得再把当前员工 `userId` 当作宠主提交。预约详情优先读取 `assignedStaffId`,兼容旧字段 `assignedUserId`。
|
||||
|
||||
## 前端 RC 发布门禁
|
||||
|
||||
发布前必须依次执行并全部通过:
|
||||
|
||||
@ -83,7 +83,7 @@ export const getAppointmentList = (/* userId, storeId, */ options = {}) =>
|
||||
// 预约详情
|
||||
export const getAppointmentDetail = (id) => get('/appointment/detail', { id })
|
||||
|
||||
// 创建预约(userId/storeId 由后端从上下文派生;前端仍传 storeId 供 customer 选店场景,后端会按 role 决定是否采纳)
|
||||
// 创建预约:登录人身份始终由后端派生;customer 传选中门店,门店代客预约传 customerUserId 或 customerPhone
|
||||
export const createAppointment = (data) => post('/appointment/create', data)
|
||||
|
||||
/** 门店某日可预约半小时档(可约 / 已满 / 已过);公开接口 */
|
||||
|
||||
@ -42,9 +42,9 @@
|
||||
<text class="label">备注</text>
|
||||
<text class="value remark">{{ detail.remark }}</text>
|
||||
</view>
|
||||
<view v-if="detail.assignedUserId" class="detail-row">
|
||||
<view v-if="detail.assignedStaffId || detail.assignedUserId" class="detail-row">
|
||||
<text class="label">服务技师</text>
|
||||
<text class="value">#{{ detail.assignedUserId }}</text>
|
||||
<text class="value">#{{ detail.assignedStaffId || detail.assignedUserId }}</text>
|
||||
</view>
|
||||
<view class="detail-meta">
|
||||
<text v-if="detail.id">预约编号 {{ detail.id }}</text>
|
||||
|
||||
@ -593,7 +593,7 @@ const confirmNewAppt = async () => {
|
||||
uni.showToast({ title: '宠物档案保存失败,请重试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const payload = { ...a, storeId, userId: userInfo.id }
|
||||
const payload = { ...a, storeId }
|
||||
payload.petId = ensuredPet.id
|
||||
const res = await createAppointment(payload)
|
||||
creatingAppt.value = false
|
||||
|
||||
@ -199,6 +199,20 @@
|
||||
<text class="popup-close" @click="showNewAppt = false">✕</text>
|
||||
</view>
|
||||
<view class="popup-body">
|
||||
<view class="c-field">
|
||||
<text class="app-form-label">宠主手机号</text>
|
||||
<input
|
||||
v-model="newAppt.customerPhone"
|
||||
class="app-form-input"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
placeholder="用于关联宠主与服务报告"
|
||||
/>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="app-form-label">宠主称呼(可选)</text>
|
||||
<input v-model="newAppt.customerName" class="app-form-input" maxlength="64" placeholder="例如:小白妈妈" />
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="app-form-label">宠物名字</text>
|
||||
<input v-model="newAppt.petName" class="app-form-input" placeholder="请输入" />
|
||||
@ -387,7 +401,16 @@ const petTypes = [
|
||||
{ label: '其他', value: '其他' }
|
||||
]
|
||||
|
||||
const newAppt = ref({ petName: '', petType: '', serviceType: '', appointmentTime: '', remark: '' })
|
||||
const emptyStaffAppointment = () => ({
|
||||
customerPhone: '',
|
||||
customerName: '',
|
||||
petName: '',
|
||||
petType: '',
|
||||
serviceType: '',
|
||||
appointmentTime: '',
|
||||
remark: ''
|
||||
})
|
||||
const newAppt = ref(emptyStaffAppointment())
|
||||
const appointmentTime = computed(() => {
|
||||
const raw = newAppt.value.appointmentTime || ''
|
||||
if (!raw.includes('T')) return ''
|
||||
@ -671,6 +694,10 @@ const goReport = (item) => {
|
||||
|
||||
const confirmNewAppt = async () => {
|
||||
const a = newAppt.value
|
||||
if (!/^1[3-9]\d{9}$/.test(a.customerPhone || '')) {
|
||||
uni.showToast({ title: '请输入正确的宠主手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!a.petName) { uni.showToast({ title: '请输入宠物名字', icon: 'none' }); return }
|
||||
if (!a.petType) { uni.showToast({ title: '请选择宠物类型', icon: 'none' }); return }
|
||||
if (!a.serviceType) { uni.showToast({ title: '请选择服务类型', icon: 'none' }); return }
|
||||
@ -681,11 +708,11 @@ const confirmNewAppt = async () => {
|
||||
return
|
||||
}
|
||||
creatingAppt.value = true
|
||||
const res = await createAppointment({ ...a, storeId: storeInfo.id, userId: userInfo.id })
|
||||
const res = await createAppointment({ ...a, storeId: storeInfo.id })
|
||||
creatingAppt.value = false
|
||||
if (res.code === 200) {
|
||||
uni.showToast({ title: '预约成功', icon: 'success' })
|
||||
newAppt.value = { petName: '', petType: '', serviceType: '', appointmentTime: '', remark: '' }
|
||||
newAppt.value = emptyStaffAppointment()
|
||||
showNewAppt.value = false
|
||||
fetchAppointments()
|
||||
} else {
|
||||
|
||||
@ -700,7 +700,6 @@ const submitReport = async () => {
|
||||
|
||||
const payload = {
|
||||
appointmentId: currentAppointmentId.value || null,
|
||||
userId: userInfo.id,
|
||||
petName: report.value.petName,
|
||||
serviceType: report.value.serviceType,
|
||||
appointmentTime: report.value.appointmentTime || null,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user