|
|
@@ -15,13 +15,46 @@
|
|
|
<text class="doctor-phone" v-else>联系电话: 未提供</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <view class="action-buttons">
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <!-- 复诊记录 -->
|
|
|
+ <view class="followup-section" v-show="followUps.length > 0">
|
|
|
+ <view class="section-title">我的复诊记录</view>
|
|
|
+ <view class="followup-list">
|
|
|
+ <view class="followup-card" v-for="followUp in followUps" :key="followUp.id">
|
|
|
+ <view class="card-header">
|
|
|
+ <view class="status-badge" :class="followUp.status">
|
|
|
+ {{ getFollowUpStatusText(followUp.status) }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-content">
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">预约时间:</text>
|
|
|
+ <text class="info-value">{{ formatDate(followUp.appointmentTime) }}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="info-row" v-if="followUp.reason">
|
|
|
+ <text class="info-label">复诊原因:</text>
|
|
|
+ <text class="info-value reason-text">{{ followUp.reason }}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 操作按钮:仅对PENDING和CONFIRMED状态显示 -->
|
|
|
+ <view class="action-row" v-if="followUp.status === 'PENDING' || followUp.status === 'CONFIRMED'">
|
|
|
+ <button class="action-btn secondary" @click="editFollowUp(followUp)">编辑</button>
|
|
|
+ <button class="action-btn cancel" @click="cancelFollowUp(followUp.id)">取消</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="action-buttons" v-if="!hasPendingOrConfirmedFollowUp">
|
|
|
<button class="action-btn primary" @click="makeAppointment">预约复诊</button>
|
|
|
- <button class="action-btn secondary" @click="contactDoctor">联系医生</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
+
|
|
|
<view class="empty-state" v-else>
|
|
|
<image class="empty-icon" src="/static/icons/remixicon/account-circle-line.svg" />
|
|
|
<text class="empty-text">暂无绑定的医生</text>
|
|
|
@@ -31,11 +64,14 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue'
|
|
|
-import { onLoad } from '@dcloudio/uni-app'
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
+import { onLoad, onShow } from '@dcloudio/uni-app'
|
|
|
import CustomNav from '@/components/custom-nav.vue'
|
|
|
import { listUserBindingsByPatient, type UserBindingResponse, type UserBindingPageResponse } from '@/api/userBinding'
|
|
|
import { downloadAvatar } from '@/api/user'
|
|
|
+import { getFollowUpList, updateFollowUp, deleteFollowUp } from '@/api/followUp'
|
|
|
+import type { FollowUp } from '@/api/followUp'
|
|
|
+import { formatDate } from '@/utils/date'
|
|
|
|
|
|
// 简化医生信息接口,只包含API实际返回的字段
|
|
|
interface LocalDoctorInfo {
|
|
|
@@ -54,6 +90,19 @@ const pageData = ref({
|
|
|
pages: 0
|
|
|
})
|
|
|
|
|
|
+// 复诊记录相关
|
|
|
+const followUps = ref<FollowUp[]>([])
|
|
|
+
|
|
|
+// 计算是否有待处理或已确认的复诊请求
|
|
|
+const hasPendingOrConfirmedFollowUp = computed(() => {
|
|
|
+ return followUps.value.some(followUp =>
|
|
|
+ followUp.status === 'PENDING' || followUp.status === 'CONFIRMED'
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+// 调试信息
|
|
|
+const debugInfo = ref('')
|
|
|
+
|
|
|
const defaultAvatar = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
|
|
|
|
|
|
// 下载后本地临时保存的图片路径(uni.downloadFile 会产出 tempFilePath)
|
|
|
@@ -73,7 +122,7 @@ const doctorAvatar = computed(() => {
|
|
|
// 获取医生信息
|
|
|
const fetchDoctorInfo = async () => {
|
|
|
uni.showLoading({ title: '加载中...' })
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
const token = uni.getStorageSync('token')
|
|
|
if (!token) {
|
|
|
@@ -84,11 +133,11 @@ const fetchDoctorInfo = async () => {
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 获取当前用户ID
|
|
|
const userInfo = uni.getStorageSync('user_info')
|
|
|
const patientUserId = userInfo?.id
|
|
|
-
|
|
|
+
|
|
|
if (!patientUserId) {
|
|
|
uni.hideLoading()
|
|
|
uni.showToast({
|
|
|
@@ -97,38 +146,38 @@ const fetchDoctorInfo = async () => {
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 查询患者绑定的医生列表
|
|
|
- const response = await listUserBindingsByPatient(
|
|
|
- patientUserId,
|
|
|
- 'DOCTOR',
|
|
|
+ const doctorResponse = await listUserBindingsByPatient(
|
|
|
+ patientUserId,
|
|
|
+ 'DOCTOR',
|
|
|
{
|
|
|
pageNum: pageData.value.pageNum,
|
|
|
pageSize: pageData.value.pageSize
|
|
|
}
|
|
|
)
|
|
|
-
|
|
|
+
|
|
|
uni.hideLoading()
|
|
|
-
|
|
|
- const resp = response.data as any
|
|
|
-
|
|
|
+
|
|
|
+ const resp = doctorResponse.data as any
|
|
|
+
|
|
|
if (resp && resp.code === 200 && resp.data) {
|
|
|
const pageResult = resp.data as UserBindingPageResponse
|
|
|
userBindings.value = pageResult.records
|
|
|
pageData.value.total = pageResult.total
|
|
|
pageData.value.pages = pageResult.pages
|
|
|
-
|
|
|
+
|
|
|
// 如果有绑定的医生,获取第一个医生的详细信息
|
|
|
if (pageResult.records && pageResult.records.length > 0) {
|
|
|
const boundDoctor = pageResult.records[0]
|
|
|
-
|
|
|
+
|
|
|
// 直接使用绑定接口返回的信息,不再调用额外的用户详情接口
|
|
|
doctorInfo.value = {
|
|
|
id: boundDoctor.id,
|
|
|
name: boundDoctor.boundUserNickname || '未知医生',
|
|
|
phone: boundDoctor.boundUserPhone || '未提供', // 当电话为null时显示"未提供"
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 尝试下载头像(绑定接口返回的数据中可能没有 avatar)
|
|
|
try {
|
|
|
if (boundDoctor.boundUserId) {
|
|
|
@@ -159,30 +208,149 @@ const fetchDoctorInfo = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 获取复诊记录
|
|
|
+const fetchFollowUpRecords = async () => {
|
|
|
+ try {
|
|
|
+ const followUpResponse: any = await getFollowUpList({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10
|
|
|
+ })
|
|
|
+
|
|
|
+ // 检查响应结构
|
|
|
+ if (!followUpResponse) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!followUpResponse.data) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 注意:这里需要访问 followUpResponse.data.data 才是真正的数据
|
|
|
+ const apiResponse = followUpResponse.data
|
|
|
+
|
|
|
+ if (apiResponse.code !== 200) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查实际数据字段
|
|
|
+ const data = apiResponse.data
|
|
|
+
|
|
|
+ if (!data) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!data.records) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!Array.isArray(data.records)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 正常处理records
|
|
|
+ followUps.value = data.records || []
|
|
|
+
|
|
|
+ // 隐藏加载提示(如果有)
|
|
|
+ uni.hideLoading()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取复诊记录失败:', error)
|
|
|
+ uni.showToast({
|
|
|
+ title: '获取复诊记录失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+
|
|
|
+ // 隐藏加载提示(如果有)
|
|
|
+ uni.hideLoading()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const makeAppointment = () => {
|
|
|
+ // 跳转到复诊申请页面,传递医生信息
|
|
|
+ if (doctorInfo.value) {
|
|
|
+ uni.navigateTo({
|
|
|
+ url: `/pages/patient/profile/infos/followup-request?doctorId=${doctorInfo.value.id}&doctorName=${encodeURIComponent(doctorInfo.value.name)}&boundUserId=${userBindings.value[0]?.boundUserId || ''}`
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: '未获取到医生信息',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const bindDoctor = () => {
|
|
|
uni.showToast({
|
|
|
- title: '预约功能开发中',
|
|
|
+ title: '绑定医生功能开发中',
|
|
|
icon: 'none'
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const contactDoctor = () => {
|
|
|
- uni.showToast({
|
|
|
- title: '联系医生功能开发中',
|
|
|
- icon: 'none'
|
|
|
+// 编辑复诊记录
|
|
|
+const editFollowUp = (followUp: FollowUp) => {
|
|
|
+ // 跳转到复诊编辑页面,传递复诊记录信息
|
|
|
+ uni.navigateTo({
|
|
|
+ url: `/pages/patient/profile/infos/followup-edit?id=${followUp.id}&doctorId=${doctorInfo.value?.id}&doctorName=${encodeURIComponent(doctorInfo.value?.name || '')}&appointmentTime=${encodeURIComponent(followUp.appointmentTime)}&reason=${encodeURIComponent(followUp.reason || '')}&boundUserId=${userBindings.value[0]?.boundUserId || ''}`
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const bindDoctor = () => {
|
|
|
- uni.showToast({
|
|
|
- title: '绑定医生功能开发中',
|
|
|
- icon: 'none'
|
|
|
+// 取消复诊记录
|
|
|
+const cancelFollowUp = (id: string) => {
|
|
|
+ uni.showModal({
|
|
|
+ title: '确认取消',
|
|
|
+ content: '确定要取消这个复诊预约吗?',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ // 调用接口取消复诊记录
|
|
|
+ updateFollowUp(id, { status: 'CANCELLED' })
|
|
|
+ .then((res: any) => {
|
|
|
+ if (res && res.data && res.data.code === 200) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '取消成功',
|
|
|
+ icon: 'success'
|
|
|
+ })
|
|
|
+ // 重新获取复诊记录列表
|
|
|
+ fetchFollowUpRecords()
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: '取消失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ console.error('取消复诊记录失败:', error)
|
|
|
+ uni.showToast({
|
|
|
+ title: '取消失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
|
|
|
onLoad(() => {
|
|
|
fetchDoctorInfo()
|
|
|
+ fetchFollowUpRecords()
|
|
|
})
|
|
|
+
|
|
|
+onShow(() => {
|
|
|
+ // 页面每次显示时都刷新复诊记录列表
|
|
|
+ // 这样从创建或编辑页面返回时能获取最新数据
|
|
|
+ fetchFollowUpRecords()
|
|
|
+})
|
|
|
+
|
|
|
+// 获取复诊状态文本
|
|
|
+const getFollowUpStatusText = (status: string) => {
|
|
|
+ switch (status) {
|
|
|
+ case 'PENDING': return '待处理'
|
|
|
+ case 'CONFIRMED': return '已确认'
|
|
|
+ case 'CANCELLED': return '已取消'
|
|
|
+ case 'COMPLETED': return '已完成'
|
|
|
+ default: return status
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -193,6 +361,7 @@ onLoad(() => {
|
|
|
padding-bottom: 40rpx;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
.doctor-card {
|
|
|
background-color: #fff;
|
|
|
margin: 20rpx;
|
|
|
@@ -296,4 +465,148 @@ onLoad(() => {
|
|
|
line-height: 80rpx;
|
|
|
width: 80%;
|
|
|
}
|
|
|
+
|
|
|
+.followup-section {
|
|
|
+ margin: 0 20rpx 20rpx;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ padding: 24rpx 30rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.followup-list {
|
|
|
+ padding: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.followup-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.followup-card:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx 24rpx;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge {
|
|
|
+ font-size: 24rpx;
|
|
|
+ padding: 6rpx 20rpx;
|
|
|
+ border-radius: 30rpx;
|
|
|
+ color: #fff;
|
|
|
+ font-weight: normal;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.PENDING {
|
|
|
+ background-color: #ff9500;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.CONFIRMED {
|
|
|
+ background-color: #007aff;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.COMPLETED {
|
|
|
+ background-color: #34c759;
|
|
|
+}
|
|
|
+
|
|
|
+.status-badge.CANCELLED {
|
|
|
+ background-color: #8e8e93;
|
|
|
+}
|
|
|
+
|
|
|
+.appointment-time {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.card-content {
|
|
|
+ padding: 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+ align-items: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.info-label {
|
|
|
+ color: #888;
|
|
|
+ font-size: 26rpx;
|
|
|
+ width: 140rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value {
|
|
|
+ flex: 1;
|
|
|
+ color: #333;
|
|
|
+ font-size: 26rpx;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+
|
|
|
+.reason-text {
|
|
|
+ color: #555;
|
|
|
+}
|
|
|
+
|
|
|
+.action-row {
|
|
|
+ display: flex;
|
|
|
+ gap: 20rpx;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ padding-top: 20rpx;
|
|
|
+ border-top: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.action-btn {
|
|
|
+ flex: 1;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ line-height: 70rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.primary {
|
|
|
+ background-color: #3742fa;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.secondary {
|
|
|
+ background-color: #f0f0f0;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.cancel {
|
|
|
+ background-color: #ff4757;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.debug-info {
|
|
|
+ padding: 20rpx;
|
|
|
+ background-color: #ffeb3b;
|
|
|
+ color: #333;
|
|
|
+ font-size: 28rpx;
|
|
|
+ margin: 20rpx;
|
|
|
+ border-radius: 10rpx;
|
|
|
+ white-space: pre-wrap;
|
|
|
+}
|
|
|
</style>
|