feat: 我的预约,专家预约界面编写以及接口对接

master
xjs 2025-03-25 12:00:33 +08:00
parent 0816b72919
commit 23f18c9dd3
8 changed files with 290 additions and 86 deletions

View File

@ -2,14 +2,6 @@
六维志愿小程序 六维志愿小程序
## 平台兼容性
| H5 | IOS | 安卓 | 微信小程序 | 字节小程序 | 快手小程序 | 支付宝小程序 | 钉钉小程序 | 百度小程序 |
| --- | --- | ---- | ---------- | ---------- | ---------- | ------------ | ---------- | ---------- |
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
注意每种 `UI框架` 支持的平台有所不同,详情请看各 `UI框架` 的官网,也可以看 `unibest` 文档。
## ⚙️ 环境 ## ⚙️ 环境
- node>=18 - node>=18

View File

@ -330,55 +330,102 @@ const getPosition = (index: number, list = cloneList.value): [number, number] =>
} }
// //
const initList = (list: ItemType[] = [], changeheight: boolean = false) => { const initList = (list: ItemType[] = [], changeheight: boolean = false): Promise<void> => {
const newList = deepCopy(list) return new Promise((resolve) => {
showList.value = newList.map((item, index) => { const newList = deepCopy(list)
const [x, y] = getPosition(index) // itemxykey
let data = { showList.value = newList.map((item, index) => {
...item, const [x, y] = getPosition(index)
x, let data = {
y, ...item,
dropId: index + 1, x,
} y,
let key = 'slot' + Math.random() + index dropId: index + 1,
// xykey
if (x === item?.x && y === item?.y) {
if (activeIndex.value !== index) {
// key
key = item.key
} }
} let key = 'slot' + Math.random() + index
data.key = key // xykey
return data if (x === item?.x && y === item?.y) {
}) if (activeIndex.value !== index) {
cloneList.value = deepCopy(showList.value) // key
nextTick(() => { key = item.key
showArea.value = true
})
if (changeheight && props.itemHeight === 'auto') {
const query = uni.createSelectorQuery().in(instance.proxy)
query
.selectAll('.slotContent')
.boundingClientRect((data) => {
let domList = JSON.parse(JSON.stringify(data))
let max = 0
let _viewMaxHeight = 0
for (let i = 0; i < domList.length; i++) {
let height = domList[i].height
if (isList.value) {
cloneList.value[i].height = height
}
_viewMaxHeight += height
if (height > max) {
max = height
}
} }
viewMaxHeight.value = _viewMaxHeight // }
itemMaxHeight.value = max + 'px' //
initList(cloneList.value) data.key = key
}) return data
.exec() })
} cloneList.value = deepCopy(showList.value)
nextTick(() => {
showArea.value = true
})
if (changeheight && props.itemHeight === 'auto') {
// item
setTimeout(async () => {
// #ifdef APP-NVUE
showArea.value = false
const calculateHeights = async () => {
let max = 0
let viewMaxHeightVal = 0
const promises = slotContent.value.map((content, index) => {
return new Promise<void>((resolve) => {
dom.getComponentRect(content, (res: any) => {
let size = res.size
if (isList.value) {
cloneList.value[index].height = size.height
}
viewMaxHeightVal += size.height
if (size.height > max) {
max = size.height
}
resolve()
})
})
})
await Promise.all(promises)
viewMaxHeight.value = viewMaxHeightVal
itemMaxHeight.value = max + 'px'
nextTick(() => {
initList(cloneList.value).then(resolve)
})
}
await calculateHeights()
// #endif
// #ifndef APP-NVUE
const query = uni.createSelectorQuery().in(instance.proxy)
query
.selectAll('.slotContent')
.boundingClientRect((data) => {
let domList = JSON.parse(JSON.stringify(data))
let max = 0
let viewMaxHeightVal = 0
for (let i = 0; i < domList.length; i++) {
let height = domList[i].height
if (isList.value) {
cloneList.value[i].height = height
}
viewMaxHeightVal += height
if (height > max) {
max = height
}
}
viewMaxHeight.value = viewMaxHeightVal //
itemMaxHeight.value = max + 'px'
initList(cloneList.value).then(resolve)
})
.exec()
// #endif
}, 0)
} else {
resolve()
}
})
} }
// //

View File

@ -100,21 +100,23 @@ const handleMove = (index) => {
myDrop.value.handleLongpress(index) myDrop.value.handleLongpress(index)
} }
const handleDelete = (index) => { const handleDelete = async (index) => {
// //
wishList.value.splice(index, 1) wishList.value.splice(index, 1)
myDrop.value.initList(wishList.value) await myDrop.value.initList(wishList.value)
} }
const handleUpdateHeight = () => { const handleUpdateHeight = async () => {
// DragSort // DragSort
myDrop.value?.initList(wishList.value, true) await myDrop.value?.initList(wishList.value, true)
} }
const handleDeleteMajor = async (index, majorIndex) => { const handleDeleteMajor = async (index, majorIndex) => {
wishList.value[index].vItems.splice(majorIndex, 1) wishList.value[index].vItems.splice(majorIndex, 1)
userStore.sortWishMajorList({ list: wishList.value[index].vItems, uIndex: index }) userStore.sortWishMajorList({ list: wishList.value[index].vItems, uIndex: index })
myDrop.value?.initList(wishList.value, true) myDrop.value?.initList(wishList.value, true).then(() => {
myDrop.value?.initList(wishList.value, true)
})
} }
const handleSave = () => { const handleSave = () => {

File diff suppressed because one or more lines are too long

View File

@ -7,12 +7,12 @@
</route> </route>
<template> <template>
<scroll-view scroll-y class="pb-safe bg-[#f8f8f8]"> <scroll-view scroll-y class="pb-safe bg-[#f8f8f8] h-screen">
<view class="item-wrapper" v-for="(item, index) in wishList" :key="index"> <view class="item-wrapper" v-for="(item, index) in list" :key="index">
<view class="flex gap-[24rpx] wish-border justify-between p-[32rpx] rounded-[8rpx]"> <view class="flex gap-[24rpx] wish-border justify-between p-[32rpx] rounded-[8rpx]">
<view class="flex flex-col gap-[14rpx]"> <view class="flex flex-col gap-[14rpx]">
<text class="text-[#303030] text-[32rpx] font-semibold"> <text class="text-[#303030] text-[32rpx] font-semibold">
{{ item.tableName }} {{ item.title }}
</text> </text>
<text class="text-[22rpx] text-[#303030]">{{ item.createTime || '时间消失了' }}</text> <text class="text-[22rpx] text-[#303030]">{{ item.createTime || '时间消失了' }}</text>
</view> </view>
@ -25,17 +25,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { deleteWishList, getWishList } from '@/service/index/api' import { deleteWishList, getMyBusReports } from '@/service/index/api'
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
const userStore = useUserStore() const userStore = useUserStore()
const wishList = ref([]) const list = ref([])
onLoad(() => { onLoad(() => {
getWishList().then((res) => { getMyBusReports({ CustomId: userStore.userInfo?.estimatedAchievement.wxId }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
wishList.value = res.result as any[] list.value = (res.result as { customReports: any[] }).customReports
} }
}) })
}) })
@ -43,7 +43,7 @@ onLoad(() => {
const handleDelete = (item, index) => { const handleDelete = (item, index) => {
deleteWishList({ id: item.vId }).then((res) => { deleteWishList({ id: item.vId }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
wishList.value.splice(index, 1) list.value.splice(index, 1)
uni.showToast({ title: '删除成功' }) uni.showToast({ title: '删除成功' })
} }
}) })

View File

@ -69,7 +69,7 @@
"path": "pages/expert/index/index", "path": "pages/expert/index/index",
"type": "page", "type": "page",
"style": { "style": {
"navigationBarTitleText": "测评师" "navigationBarTitleText": "专家"
}, },
"needLogin": true "needLogin": true
}, },

View File

@ -1,18 +1,138 @@
<route lang="json5" type="page"> <route lang="json5" type="page">
{ {
style: { style: {
navigationBarTitleText: '测评师', navigationBarTitleText: '专家',
}, },
needLogin: true, needLogin: true,
} }
</route> </route>
<template> <template>
<view class=""> <view class="h-screen flex flex-col bg-[#F9F9F9]">
<text>庄家</text> <view class="flex-1 m-[32rpx] rounded-t-[32rpx] gap-[30rpx] flex flex-col">
<view
class="flex items-start gap-[24rpx] pl-[24rpx] pr-[38rpx] pt-[24rpx] pb-[12rpx] bg-[#fff]"
v-for="(item, index) in list"
:key="item.id"
>
<view class="flex flex-col gap-[24rpx]">
<image :src="item.pic" mode="aspectFit" class="w-[112rpx] h-[112rpx] rounded-full" />
<button
:class="`appointment-btn ${item.isAppointment ? 'bg-[#1580ff]!' : ''}`"
@click="handleAppointment(item, index)"
>
<text v-if="!item.isAppointment"></text>
<view class="i-carbon-checkmark text-[#fff] font-semibold" v-else></view>
</button>
</view>
<view class="flex flex-col">
<text class="text-[36rpx] font-semibold text-[#333]">{{ item.name }}</text>
<view class="flex gap-[8rpx] mt-[14rpx]">
<view
v-for="(major, majorIndex) in item.majorTags.slice(0, 3)"
:key="majorIndex"
class="bg-[#f8f8f8] rounded-[4rpx] px-[16rpx] py-[4rpx] text-[#999] text-[20rpx]"
>
{{ major }}
</view>
</view>
<view class="text-[22rpx] text-[#999] mt-[16rpx]">{{ item.summary }}</view>
</view>
</view>
</view>
<TabBar :current-page="1"></TabBar> <TabBar :current-page="1"></TabBar>
<FabButton :initial-x="0" :initial-y="100" />
</view> </view>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import TabBar from '@/components/bar/TabBar.vue' import TabBar from '@/components/bar/TabBar.vue'
import FabButton from '@/components/fab/FabButton.vue'
import {
addSpecial,
deleteMyAppointment,
getBusSpecialListGroup,
getMySpecialList,
} from '@/service/index/api'
import { useUserStore } from '@/store/user'
const list = ref<any[]>([])
const userStore = useUserStore()
const show = ref(true)
onLoad(() => {
getMySpecialList({
openId: userStore.userInfo?.estimatedAchievement.wxId.toString(),
}).then((_res) => {
if (_res.code === 200) {
let _mySpecialList = _res.result as any[]
getBusSpecialListGroup().then((res) => {
if (res.code === 200) {
list.value = res.result as any[]
list.value = list.value.map((item) => {
let _mySpecial = _mySpecialList.find((_item) => _item.sId === item.id)
return {
...item,
isAppointment: !!_mySpecial,
appointId: _mySpecial?.id,
}
})
}
})
}
})
})
const handleAppointment = (
item: { id: number; isAppointment: boolean; appointId: number },
index: number,
) => {
if (item.isAppointment) {
deleteMyAppointment({ id: item.appointId }).then((res) => {
if (res.code === 200) {
list.value[index].isAppointment = !item.isAppointment
}
})
} else {
addSpecial({
sId: item.id,
openId: userStore.userInfo?.estimatedAchievement.wxId.toString(),
appointmentTime: '2025-07-31',
isDelete: item.isAppointment,
}).then((res) => {
if (res.code === 200) {
list.value[index].isAppointment = !item.isAppointment
uni.showToast({
title: item.isAppointment ? '预约成功' : '取消预约成功',
})
} else {
uni.showToast({
title: res.message,
icon: 'none',
})
}
})
}
}
</script> </script>
<style lang="scss" scoped>
.appointment-btn {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 70rpx;
border: 2rpx solid #1580ff;
line-height: 1;
font-size: 24rpx;
color: #1580ff;
width: 96rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
margin: 0;
}
</style>

View File

@ -384,3 +384,28 @@ export const saveBusScaleAnswer = (params: {
}) => { }) => {
return http.post('/api/busScale/CustomBusScale/v2', params) return http.post('/api/busScale/CustomBusScale/v2', params)
} }
export const getBusSpecialListGroup = (params?: { Code: number }) => {
return http.get('/api/busSpecialistGroup/list', params)
}
export const addSpecial = (params: {
openId: string
sId: number
appointmentTime: string
isDelete?: boolean
}) => {
return http.post('/api/specialistReservation/add', params)
}
export const getMySpecialList = (params: { openId: string }) => {
return http.get('/api/specialistReservation/reservation', params)
}
export const getMyBusReports = (params: { CustomId: number }) => {
return http.get('/api/busScale/GetBusCustomReports', params)
}
export const deleteMyAppointment = (params: { id: number }) => {
return http.post('/api/specialistReservation/delete', params)
}