|
@@ -0,0 +1,378 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <CustomNav title="我的医生" leftType="back" />
|
|
|
|
|
+ <view class="page-container">
|
|
|
|
|
+ <view class="doctor-card" v-if="doctorInfo">
|
|
|
|
|
+ <view class="doctor-header">
|
|
|
|
|
+ <view class="avatar-section">
|
|
|
|
|
+ <view class="avatar-frame">
|
|
|
|
|
+ <image class="avatar-img" :src="doctorAvatar" mode="aspectFill" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="doctor-info">
|
|
|
|
|
+ <text class="doctor-name">{{ doctorInfo.name }}</text>
|
|
|
|
|
+ <text class="doctor-title" v-if="doctorInfo.title">{{ doctorInfo.title }}</text>
|
|
|
|
|
+ <text class="doctor-hospital" v-if="doctorInfo.hospital">{{ doctorInfo.hospital }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="doctor-details">
|
|
|
|
|
+ <view class="detail-item">
|
|
|
|
|
+ <text class="label">科室:</text>
|
|
|
|
|
+ <text class="value">{{ doctorInfo.department || '未填写' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="detail-item">
|
|
|
|
|
+ <text class="label">联系电话:</text>
|
|
|
|
|
+ <text class="value">{{ doctorInfo.phone || '未填写' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="detail-item">
|
|
|
|
|
+ <text class="label">擅长领域:</text>
|
|
|
|
|
+ <text class="value">{{ doctorInfo.specialty || '未填写' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="detail-item">
|
|
|
|
|
+ <text class="label">简介:</text>
|
|
|
|
|
+ <text class="value intro">{{ doctorInfo.introduction || '暂无介绍' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="action-buttons">
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <button class="bind-btn" @click="bindDoctor">绑定医生</button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { ref, computed } from 'vue'
|
|
|
|
|
+import { onLoad } from '@dcloudio/uni-app'
|
|
|
|
|
+import CustomNav from '@/components/custom-nav.vue'
|
|
|
|
|
+import { listUserBindingsByPatient, type UserBindingResponse, type UserBindingPageResponse } from '@/api/userBinding'
|
|
|
|
|
+import { getUserInfo } from '@/api/user'
|
|
|
|
|
+
|
|
|
|
|
+// 为避免与导入的类型冲突,重命名本地接口
|
|
|
|
|
+interface LocalDoctorInfo {
|
|
|
|
|
+ id: string
|
|
|
|
|
+ name: string
|
|
|
|
|
+ title?: string
|
|
|
|
|
+ hospital?: string
|
|
|
|
|
+ department?: string
|
|
|
|
|
+ phone?: string
|
|
|
|
|
+ specialty?: string
|
|
|
|
|
+ introduction?: string
|
|
|
|
|
+ avatar?: string
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const doctorInfo = ref<LocalDoctorInfo | null>(null)
|
|
|
|
|
+const userBindings = ref<UserBindingResponse[]>([])
|
|
|
|
|
+const pageData = ref({
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ pages: 0
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const defaultAvatar = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
|
|
|
|
|
+
|
|
|
|
|
+const doctorAvatar = computed(() => {
|
|
|
|
|
+ if (doctorInfo.value?.avatar) {
|
|
|
|
|
+ return doctorInfo.value.avatar
|
|
|
|
|
+ }
|
|
|
|
|
+ return defaultAvatar
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 获取医生信息
|
|
|
|
|
+const fetchDoctorInfo = async () => {
|
|
|
|
|
+ uni.showLoading({ title: '加载中...' })
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const token = uni.getStorageSync('token')
|
|
|
|
|
+ if (!token) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '未登录',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前用户ID
|
|
|
|
|
+ const userInfo = uni.getStorageSync('user_info')
|
|
|
|
|
+ const patientUserId = userInfo?.id
|
|
|
|
|
+
|
|
|
|
|
+ if (!patientUserId) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '获取用户信息失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询患者绑定的医生列表
|
|
|
|
|
+ const response = await listUserBindingsByPatient(
|
|
|
|
|
+ patientUserId,
|
|
|
|
|
+ 'DOCTOR',
|
|
|
|
|
+ {
|
|
|
|
|
+ pageNum: pageData.value.pageNum,
|
|
|
|
|
+ pageSize: pageData.value.pageSize
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+
|
|
|
|
|
+ const resp = response.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]
|
|
|
|
|
+
|
|
|
|
|
+ // 通过用户接口获取医生详细信息
|
|
|
|
|
+ try {
|
|
|
|
|
+ const userInfoResponse = await getUserInfo(boundDoctor.boundUserId)
|
|
|
|
|
+ const userInfo = userInfoResponse.data?.data
|
|
|
|
|
+
|
|
|
|
|
+ if (userInfo) {
|
|
|
|
|
+ doctorInfo.value = {
|
|
|
|
|
+ id: boundDoctor.id,
|
|
|
|
|
+ name: userInfo.nickname || boundDoctor.boundUserNickname || '未知医生',
|
|
|
|
|
+ title: userInfo.title || '医生',
|
|
|
|
|
+ hospital: userInfo.hospital || '未知医院',
|
|
|
|
|
+ department: userInfo.department || '未知科室',
|
|
|
|
|
+ phone: boundDoctor.boundUserPhone || userInfo.phone || '未提供',
|
|
|
|
|
+ specialty: userInfo.specialty || '未知',
|
|
|
|
|
+ introduction: userInfo.introduction || '暂无介绍',
|
|
|
|
|
+ avatar: userInfo.avatar
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果获取详细信息失败,使用绑定接口返回的基础信息
|
|
|
|
|
+ doctorInfo.value = {
|
|
|
|
|
+ id: boundDoctor.id,
|
|
|
|
|
+ name: boundDoctor.boundUserNickname || '未知医生',
|
|
|
|
|
+ title: '医生',
|
|
|
|
|
+ hospital: '未知医院',
|
|
|
|
|
+ department: '未知科室',
|
|
|
|
|
+ phone: boundDoctor.boundUserPhone || '未提供',
|
|
|
|
|
+ specialty: '未知',
|
|
|
|
|
+ introduction: '暂无介绍'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取医生详细信息失败:', error)
|
|
|
|
|
+ // 使用绑定接口返回的基础信息
|
|
|
|
|
+ doctorInfo.value = {
|
|
|
|
|
+ id: boundDoctor.id,
|
|
|
|
|
+ name: boundDoctor.boundUserNickname || '未知医生',
|
|
|
|
|
+ title: '医生',
|
|
|
|
|
+ hospital: '未知医院',
|
|
|
|
|
+ department: '未知科室',
|
|
|
|
|
+ phone: boundDoctor.boundUserPhone || '未提供',
|
|
|
|
|
+ specialty: '未知',
|
|
|
|
|
+ introduction: '暂无介绍'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doctorInfo.value = null
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '获取医生信息失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ console.error('获取医生信息失败:', error)
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '获取医生信息失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const makeAppointment = () => {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '预约功能开发中',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const contactDoctor = () => {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '联系医生功能开发中',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const bindDoctor = () => {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '绑定医生功能开发中',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onLoad(() => {
|
|
|
|
|
+ fetchDoctorInfo()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.page-container {
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
|
+ padding-top: calc(var(--status-bar-height) + 44px);
|
|
|
|
|
+ padding-bottom: 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-card {
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ margin: 20rpx;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 40rpx;
|
|
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.avatar-section {
|
|
|
|
|
+ margin-right: 30rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.avatar-frame {
|
|
|
|
|
+ width: 120rpx;
|
|
|
|
|
+ height: 120rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ border: 1px solid rgba(128, 128, 128, 0.5);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.avatar-img {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ object-fit: cover;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-info {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-name {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ margin-bottom: 10rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-title {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-bottom: 10rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-hospital {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.doctor-details {
|
|
|
|
|
+ padding: 30rpx 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.detail-item:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.label {
|
|
|
|
|
+ width: 160rpx;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.value {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.value.intro {
|
|
|
|
|
+ white-space: pre-wrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.action-buttons {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 30rpx 40rpx;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.action-btn {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ border-radius: 10rpx;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ line-height: 80rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.primary {
|
|
|
|
|
+ background-color: #3742fa;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.secondary {
|
|
|
|
|
+ background-color: #f0f0f0;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-state {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 100rpx 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-icon {
|
|
|
|
|
+ width: 120rpx;
|
|
|
|
|
+ height: 120rpx;
|
|
|
|
|
+ margin-bottom: 30rpx;
|
|
|
|
|
+ opacity: 0.5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-text {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-bottom: 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.bind-btn {
|
|
|
|
|
+ background-color: #3742fa;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border-radius: 10rpx;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ line-height: 80rpx;
|
|
|
|
|
+ width: 80%;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|