diff --git a/src/api/index.js b/src/api/index.js index 6e867f5..949b346 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -52,6 +52,13 @@ export const registerBoss = (data) => post('/user/register-boss', data) // 注册员工 export const registerStaff = (data) => post('/user/register-staff', data) +// 宠物档案(客户:ownerUserId;门店:storeId) +export const getPetList = (params) => get('/pet/list', params) +export const createPet = (data) => post('/pet/create', data) +export const updatePet = (data) => put('/pet/update', data) +export const deletePet = (id, operatorUserId, role) => + del(`/pet/delete?id=${id}&operatorUserId=${operatorUserId}&role=${encodeURIComponent(role)}`) + // 预约列表 export const getAppointmentList = (userId, storeId) => get('/appointment/list', { userId, storeId }) @@ -77,8 +84,12 @@ export const getReportByToken = (token) => get('/report/get', { token }) // 报告列表 export const getReportList = (params) => get('/report/list', params) -// 服务类型列表 -export const getServiceTypeList = (storeId) => get('/service-type/list', { storeId }) +// 服务类型列表(不传 storeId 时仅返回系统默认,供未写入门店会话的 C 端展示) +export const getServiceTypeList = (storeId) => { + const params = {} + if (storeId != null && storeId !== '') params.storeId = storeId + return get('/service-type/list', params) +} // 创建服务类型 export const createServiceType = (storeId, name) => post('/service-type/create', { storeId, name }) @@ -98,6 +109,9 @@ export const deleteStaff = (staffId) => del(`/user/staff?staffId=${staffId}`) // 更新用户信息 export const updateUser = (data) => put('/user/update', data) +// 门店列表(客户预约选店) +export const getStoreList = () => get('/store/list', {}) + // 更新店铺 export const updateStore = (data) => put('/store/update', data) diff --git a/src/manifest.json b/src/manifest.json index 29d1f2a..1869b4f 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -11,14 +11,15 @@ "lazyCodeLoading": "requiredComponents", "permission": { "scope.userLocation": { - "desc": "用于在地图上选择门店位置" + "desc": "用于推荐距离您最近的门店,也可在预约页手动切换" }, "scope.writePhotosAlbum": { "desc": "用于将服务报告海报保存到手机相册" } }, "requiredPrivateInfos": [ - "chooseLocation" + "chooseLocation", + "getLocation" ], "setting": { "urlCheck": false, diff --git a/src/pages.json b/src/pages.json index 19f89df..0a435f4 100644 --- a/src/pages.json +++ b/src/pages.json @@ -11,6 +11,7 @@ {"path": "pages/mine/Store"}, {"path": "pages/mine/MyReports"}, {"path": "pages/mine/MyOrders"}, + {"path": "pages/mine/MyPets"}, {"path": "pages/mine/Profile"}, {"path": "pages/report-view/reportView"} ], diff --git a/src/pages/appointment/CustAppointmentCreate.vue b/src/pages/appointment/CustAppointmentCreate.vue index 5f1ce8e..4a2e4dc 100644 --- a/src/pages/appointment/CustAppointmentCreate.vue +++ b/src/pages/appointment/CustAppointmentCreate.vue @@ -12,6 +12,32 @@ + + 预约门店 + + + {{ storePickerDisplay }} + + + + {{ storeHintLine }} + + + 使用档案(可选) + + + {{ petPickerLabel }} + + + + 宠物名字 @@ -60,13 +86,20 @@ diff --git a/src/pages/home/Home.vue b/src/pages/home/Home.vue index bf9ffdb..ef7feb0 100644 --- a/src/pages/home/Home.vue +++ b/src/pages/home/Home.vue @@ -19,6 +19,14 @@ @click="currentStatus = tab.name" > {{ tab.title }} + {{ newApptCount > 99 ? '99+' : newApptCount }} + {{ doingApptCount > 99 ? '99+' : doingApptCount }} @@ -64,7 +72,14 @@ @click="currentStatus = tab.name" > {{ tab.title }} - + {{ newApptCount > 99 ? '99+' : newApptCount }} + {{ doingApptCount > 99 ? '99+' : doingApptCount }} @@ -191,6 +206,7 @@ const statusTabs = [ const orders = ref([]) const newApptCount = computed(() => orders.value.filter(o => o.status === 'new').length) +const doingApptCount = computed(() => orders.value.filter(o => o.status === 'doing').length) const serviceTypes = ref([]) const showNewAppt = ref(false) const creatingAppt = ref(false) @@ -338,11 +354,10 @@ const confirmNewAppt = async () => { } onMounted(() => { - fetchAppointments() if (userInfo.role !== 'customer') loadServiceTypes() }) onShow(() => { - if (userInfo.role === 'customer') fetchAppointments() + fetchAppointments() }) @@ -498,14 +513,24 @@ onShow(() => { color: #999993; } .tab-active .tab-text { color: #fff; } -.tab-dot { +.tab-badge { position: absolute; - top: 6px; - right: 16px; - width: 7px; - height: 7px; + top: 4px; + right: 8px; + min-width: 16px; + height: 16px; + padding: 0 4px; + line-height: 16px; + text-align: center; + font-size: 10px; + font-weight: 700; + color: #fff; background: #e5534b; - border-radius: 50%; + border-radius: 8px; + box-sizing: border-box; +} +.tab-badge--doing { + background: #2db96d; } /* B端列表(底部留白,避免最后一条被悬浮钮挡住) */ diff --git a/src/pages/mine/Mine.vue b/src/pages/mine/Mine.vue index 1b24b43..f7d94c1 100644 --- a/src/pages/mine/Mine.vue +++ b/src/pages/mine/Mine.vue @@ -46,6 +46,11 @@ 个人信息 + + + 我的宠物 + + 我的订单 @@ -75,6 +80,7 @@ const goStaffPage = () => uni.navigateTo({ url: '/pages/mine/Staff' }) const goServiceTypePage = () => uni.navigateTo({ url: '/pages/mine/ServiceType' }) const goStorePage = () => uni.navigateTo({ url: '/pages/mine/Store' }) const openProfile = () => uni.navigateTo({ url: '/pages/mine/Profile' }) +const goMyPetsPage = () => uni.navigateTo({ url: '/pages/mine/MyPets' }) const goMyOrdersPage = () => uni.navigateTo({ url: '/pages/mine/MyOrders' }) const userCardSafeStyle = (() => { diff --git a/src/pages/mine/MyPets.vue b/src/pages/mine/MyPets.vue new file mode 100644 index 0000000..194a544 --- /dev/null +++ b/src/pages/mine/MyPets.vue @@ -0,0 +1,553 @@ + + + + + diff --git a/src/utils/geo.js b/src/utils/geo.js new file mode 100644 index 0000000..9c42f09 --- /dev/null +++ b/src/utils/geo.js @@ -0,0 +1,27 @@ +/** + * 球面距离(千米)。坐标需同一套系(小程序 getLocation 建议 gcj02,与门店地图选点一致)。 + */ +export function haversineKm(lat1, lon1, lat2, lon2) { + if ( + lat1 == null || + lon1 == null || + lat2 == null || + lon2 == null || + Number.isNaN(lat1) || + Number.isNaN(lon1) || + Number.isNaN(lat2) || + Number.isNaN(lon2) + ) { + return null + } + const R = 6371 + const r1 = (lat1 * Math.PI) / 180 + const r2 = (lat2 * Math.PI) / 180 + const dLat = ((lat2 - lat1) * Math.PI) / 180 + const dLon = ((lon2 - lon1) * Math.PI) / 180 + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(r1) * Math.cos(r2) * Math.sin(dLon / 2) * Math.sin(dLon / 2) + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) + return R * c +} diff --git a/src/utils/globalState.js b/src/utils/globalState.js index 7041e1e..2b8e612 100644 --- a/src/utils/globalState.js +++ b/src/utils/globalState.js @@ -12,7 +12,8 @@ const SUB_ROUTE_MAP = { store: '/pages/mine/Store', profile: '/pages/mine/Profile', myReports: '/pages/mine/MyReports', - myOrders: '/pages/mine/MyOrders' + myOrders: '/pages/mine/MyOrders', + myPets: '/pages/mine/MyPets' } /**