This commit is contained in:
Your Name
2026-03-20 13:56:40 +08:00
parent 7147c8e148
commit d765517b74
258 changed files with 2481 additions and 364 deletions
+341 -51
View File
@@ -28,7 +28,7 @@
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ formatPatientCount(doctor.patient_count) }}</text>
<text class="stat-value">100{{ formatPatientCount(doctor.patient_count) }}</text>
<text class="stat-label">患者数</text>
</view>
<view class="stat-divider"></view>
@@ -51,6 +51,21 @@
</view>
</section>
<!-- 医生资质 -->
<section class="qualification-section" v-if="qualificationImages.length > 0">
<text class="section-title-plain">医生资质</text>
<view class="qualification-grid">
<image
v-for="(img, idx) in qualificationImages"
:key="idx"
:src="img"
class="qualification-image"
mode="aspectFill"
@click="previewImage(img)"
/>
</view>
</section>
<!-- 专业领域 -->
<section class="expertise-section">
<text class="section-title-plain">专业领域</text>
@@ -108,16 +123,55 @@
<view v-if="doctor">
<!-- 底部操作栏 -->
<view v-if="doctor.enable_image_consult || doctor.enable_video_consult" class="bottom-bar">
<view class="btn-chat" @click="goChat" v-if="doctor.enable_image_consult">
<view class="btn-chat" @click="showCardSelector('chat')" v-if="doctor.enable_image_consult">
<text class="btn-icon">💬</text>
<text class="btn-text">图文问诊</text>
</view>
<view class="btn-video" @click="goVideo" v-if="doctor.enable_video_consult">
<view class="btn-video" @click="showCardSelector('video')" v-if="doctor.enable_video_consult">
<text class="btn-icon">📹</text>
<text class="btn-text">视频问诊</text>
</view>
</view>
</view>
<!-- 就诊人选择弹窗 -->
<view v-if="showCardModal" class="modal-overlay" @click="closeCardModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">选择就诊人</text>
<text class="modal-close" @click="closeCardModal"></text>
</view>
<view class="modal-body">
<!-- 加载中 -->
<view v-if="loadingCards" class="modal-loading">
<text class="loading-text">加载中...</text>
</view>
<!-- 就诊卡列表 -->
<view v-else-if="cardList.length > 0" class="card-list">
<view
v-for="card in cardList"
:key="card.id"
class="card-item"
@click="selectCard(card)"
>
<view class="card-info">
<text class="card-name">{{ card.patient_name }}</text>
<text class="card-detail">{{ card.gender_desc }} / {{ card.age }}</text>
</view>
<text class="card-arrow"></text>
</view>
</view>
<!-- 空状态 -->
<view v-else class="modal-empty">
<text class="empty-text">暂无就诊卡</text>
<view class="add-card-btn" @click="createNewCard">新建就诊卡</view>
</view>
</view>
</view>
</view>
</view>
</template>
@@ -138,6 +192,26 @@ const loading = ref(true)
const error = ref('')
const defaultAvatar = '/static/user/user.png'
// 就诊卡选择相关
const showCardModal = ref(false)
const cardList = ref([])
const loadingCards = ref(false)
const consultType = ref('') // 'chat' 或 'video'
// 医生资质图片
const qualificationImages = computed(() => {
if (!doctor.value?.qualification_images) return []
try {
const images = typeof doctor.value.qualification_images === 'string'
? JSON.parse(doctor.value.qualification_images)
: doctor.value.qualification_images
return Array.isArray(images) ? images : []
} catch (e) {
console.error('解析资质图片失败:', e)
return []
}
})
const formatPatientCount = (count) => {
if (count === undefined || count === null) return '1.2k+'
if (count >= 1000) return (count / 1000).toFixed(1) + 'k+'
@@ -149,6 +223,14 @@ const getExpertiseIcon = (icon) => {
return map[icon] || '📋'
}
// 预览资质图片
const previewImage = (current) => {
uni.previewImage({
current: current,
urls: qualificationImages.value
})
}
const loadDetail = async () => {
if (!doctorId.value) return
loading.value = true
@@ -192,68 +274,136 @@ const viewAllReviews = () => {
uni.showToast({ title: '查看全部评价', icon: 'none' })
}
const goChat = async () => {
// uni.navigateTo({
// url: `/TUIKit/pages/chat/chat?doctorId=${doctorId.value}&doctorName=${encodeURIComponent(doctor.value?.name || '')}`
// })
console.log('dfafafa',uni.getStorageSync('userData'))
if(!uni.getStorageSync('userData').diagnosis){
uni.navigateTo({
url: `/pages/Card/edit_card?add=1`
});
}else{
await loginHandler(uni.getStorageSync('userData').diagnosis.patient_id)
const doctor='doctor_'+doctorId.value
// 跳转到聊天页面
uni.navigateTo({
url: `/TUIKit/pages/chat/chat?userID=${encodeURIComponent(userId)}&userSig=${encodeURIComponent(userSig)}&SDKAppID=${SDKAppID}&targetUserID=${doctor}`
});
// 显示就诊卡选择弹窗
const showCardSelector = async (type) => {
consultType.value = type
showCardModal.value = true
await loadCardList()
}
// 关闭弹窗
const closeCardModal = () => {
showCardModal.value = false
}
// 加载就诊卡列表
const loadCardList = async () => {
loadingCards.value = true
try {
const token = uni.getStorageSync('token')
if (!token) {
uni.showToast({ title: '请先登录', icon: 'none' })
closeCardModal()
return
}
const userData = uni.getStorageSync('userData')
const userId = userData?.id
if (!userId) {
uni.showToast({ title: '用户信息不存在', icon: 'none' })
closeCardModal()
return
}
const res = await proxy.apiUrl({
url: '/api/tcm/getCardList',
method: 'GET',
data: { user_id: userId,patient_id:userId }
}, false)
if (res && res.code === 1) {
cardList.value = res.data || []
} else {
uni.showToast({ title: res?.msg || '获取失败', icon: 'none' })
}
} catch (e) {
console.error('加载就诊卡列表失败:', e)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
loadingCards.value = false
}
}
const goVideo = () => {
if(!uni.getStorageSync('userData').diagnosis){
uni.navigateTo({
url: `/pages/Card/edit_card?add=1`
});
}else{
uni.navigateTo({
url: `/pages/login/login?doctorId=${doctorId.value}&doctorName=${encodeURIComponent(doctor.value?.name || '')}`
// 选择就诊卡
const selectCard = async (card) => {
closeCardModal()
// 更新本地存储的就诊卡信息
const userData = uni.getStorageSync('userData') || {}
userData.diagnosis = {
patient_id: card.patient_id,
id: card.id,
patient_name: card.patient_name
}
uni.setStorageSync('userData', userData)
// 根据类型跳转
if (consultType.value === 'chat') {
await goChat(card)
} else if (consultType.value === 'video') {
await goChat(card)
}
}
// 新建就诊卡
const createNewCard = () => {
closeCardModal()
uni.navigateTo({
url: `/pages/Card/edit_card?add=1&returnUrl=/doctor/pages/doctor/doctor?id=${doctorId.value}`
})
}
const goChat = async (card) => {
console.log('开始图文问诊', uni.getStorageSync('userData'))
const userData = uni.getStorageSync('userData')
if (!userData.diagnosis) {
uni.showToast({ title: '请先选择就诊人', icon: 'none' })
return
}
await loginHandler(userData.diagnosis.patient_id)
const doctor = 'doctor_' + doctorId.value
uni.navigateTo({
url: `/TUIKit/pages/chat/chat?userID=${encodeURIComponent(userId.value)}&userSig=${encodeURIComponent(userSig.value)}&SDKAppID=${SDKAppID.value}&targetUserID=${doctor}&msg=0`
})
}
const loginHandler = async (patient_id) => {
console.log('获取签名开始:',patient_id);
const res = await proxy.apiUrl({
url: '/api/tcm/getPatientSignature',
method: 'GET',
data: { patient_id: patient_id },
}, false)
console.log('获取签名开始:', patient_id)
const res = await proxy.apiUrl({
url: '/api/tcm/getPatientSignature',
method: 'GET',
data: { patient_id: patient_id },
}, false)
const {userId } = res.data;
const { userSig, SDKAppID } = GenerateTestUserSig.genTestUserSig({
userID:userId,
});
console.log('获取签名成功:',userSig);
const { userId: patientUserId } = res.data
const { userSig: patientUserSig, SDKAppID: patientSDKAppID } = GenerateTestUserSig.genTestUserSig({
userID: patientUserId,
})
console.log('获取签名成功:', patientUserSig)
getApp().globalData.userID = userId;
getApp().globalData.userSig = userSig;
getApp().globalData.SDKAppID = SDKAppID;
getApp().globalData.isIMInitialized = false; // 标记IM未初始化
// 更新ref值
userId.value = patientUserId
userSig.value = patientUserSig
SDKAppID.value = patientSDKAppID
getApp().globalData.userID = patientUserId
getApp().globalData.userSig = patientUserSig
getApp().globalData.SDKAppID = patientSDKAppID
getApp().globalData.isIMInitialized = false
// 初始化视频通话
await uni.CallManager.init({
sdkAppID: SDKAppID,
userID: userId,
userSig: userSig,
sdkAppID: patientSDKAppID,
userID: patientUserId,
userSig: patientUserSig,
globalCallPagePath: "TUICallKit/src/Components/TUICallKit",
});
})
console.log('初始化完成,跳转到聊天页面');
};
console.log('初始化完成,跳转到聊天页面')
}
onMounted(() => {
const pages = getCurrentPages()
const current = pages[pages.length - 1]
@@ -453,6 +603,24 @@ $outline-variant: #c1c9be;
line-height: 1.8;
}
.qualification-section {
margin-bottom: 96rpx;
}
.qualification-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24rpx;
}
.qualification-image {
width: 100%;
height: 200rpx;
border-radius: 16rpx;
background: $surface-high;
object-fit: cover;
}
.expertise-section {
margin-bottom: 96rpx;
}
@@ -647,4 +815,126 @@ $outline-variant: #c1c9be;
.btn-icon {
font-size: 40rpx;
}
/* 弹窗样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
width: 640rpx;
max-height: 80vh;
background: $surface-lowest;
border-radius: 32rpx;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 48rpx;
border-bottom: 2rpx solid $outline-variant;
}
.modal-title {
font-size: 40rpx;
font-weight: bold;
color: $on-surface;
}
.modal-close {
font-size: 48rpx;
color: $on-surface-variant;
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
}
.modal-body {
max-height: 60vh;
overflow-y: auto;
}
.modal-loading {
padding: 120rpx 0;
text-align: center;
}
.card-list {
padding: 24rpx;
}
.card-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
margin-bottom: 16rpx;
background: $surface-low;
border-radius: 16rpx;
transition: all 0.3s;
&:active {
background: $surface-high;
transform: scale(0.98);
}
}
.card-info {
flex: 1;
}
.card-name {
display: block;
font-size: 36rpx;
font-weight: bold;
color: $on-surface;
margin-bottom: 8rpx;
}
.card-detail {
display: block;
font-size: 28rpx;
color: $on-surface-variant;
}
.card-arrow {
font-size: 48rpx;
color: $on-surface-variant;
margin-left: 16rpx;
}
.modal-empty {
display: flex;
flex-direction: column;
align-items: center;
padding: 120rpx 48rpx;
}
.empty-text {
font-size: 32rpx;
color: $on-surface-variant;
margin-bottom: 48rpx;
}
.add-card-btn {
padding: 24rpx 64rpx;
background: $primary;
color: $on-primary;
border-radius: 40rpx;
font-size: 32rpx;
font-weight: bold;
}
</style>