更新
@@ -67,10 +67,11 @@ const userinfo = async (code) => {
|
||||
|
||||
wxcode(code)
|
||||
}
|
||||
if(res.code==1){
|
||||
uni.setStorageSync('userData', res.data)
|
||||
if(res.code==1 && res.data.diagnosis){
|
||||
video(res.data)
|
||||
}
|
||||
uni.setStorageSync('userData', res.data)
|
||||
|
||||
} catch (err) {
|
||||
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
const SDKAppID = app.globalData.SDKAppID || options.SDKAppID;
|
||||
targetUserID = options.targetUserID;
|
||||
currentUserID = userID; // 保存当前用户ID
|
||||
|
||||
console.log('-------',userID,userSig,SDKAppID)
|
||||
// 获取用户头像
|
||||
const userData = uni.getStorageSync('userData');
|
||||
if (userData && userData.avatar) {
|
||||
@@ -180,7 +180,7 @@
|
||||
}
|
||||
|
||||
conversationID = `C2C${targetUserID}`;
|
||||
console.log('当前用户:', currentUserID, '对方用户:', targetUserID);
|
||||
console.log('当前用户:', currentUserID, '对方用户:', targetUserID,'conversationID'+conversationID);
|
||||
|
||||
if (!app.globalData.imChat) {
|
||||
chat = TencentCloudChat.create({
|
||||
@@ -212,7 +212,7 @@
|
||||
}
|
||||
|
||||
await loadHistoryMessages();
|
||||
uni.showToast({ title: '聊天已就绪', icon: 'success' });
|
||||
//uni.showToast({ title: '聊天已就绪', icon: 'success' });
|
||||
|
||||
// 监听键盘高度变化
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
|
||||
@@ -0,0 +1,649 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 加载中 -->
|
||||
<view v-if="loading" class="loading-wrap">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 医生详情 -->
|
||||
<view v-else-if="doctor" class="main">
|
||||
<!-- 医生头部 -->
|
||||
<section class="hero-section">
|
||||
<view class="hero-content">
|
||||
<view class="avatar-wrap">
|
||||
<image :src="doctor.avatar || defaultAvatar" class="avatar" mode="aspectFill"></image>
|
||||
<view class="badge">{{ doctor.title || '主任医师' }}</view>
|
||||
</view>
|
||||
<view class="hero-info">
|
||||
<text class="doctor-name">{{ doctor.name }}</text>
|
||||
<text class="hospital">{{ doctor.hospital || '甄养堂医院' }}</text>
|
||||
<view class="experience-row">
|
||||
<text class="verified">✓</text>
|
||||
<text class="experience">{{ doctor.experience || '24年临床经验' }}</text>
|
||||
</view>
|
||||
<view class="stats-row">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ doctor.rating || '4.9' }}</text>
|
||||
<text class="stat-label">评分</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ formatPatientCount(doctor.patient_count) }}</text>
|
||||
<text class="stat-label">患者数</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item" v-if="doctor.license_no">
|
||||
<text class="stat-value">执业证书编号</text>
|
||||
<text class="stat-label">{{doctor.license_no}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</section>
|
||||
|
||||
<!-- 擅长领域 -->
|
||||
<section class="about-section">
|
||||
<view class="section-title-bar">
|
||||
<text class="section-title">擅长领域</text>
|
||||
</view>
|
||||
<view class="about-content">
|
||||
<text class="about-text">{{ doctor.about || '擅长运用传统中医理论与现代诊断相结合...' }}</text>
|
||||
</view>
|
||||
</section>
|
||||
|
||||
<!-- 专业领域 -->
|
||||
<section class="expertise-section">
|
||||
<text class="section-title-plain">专业领域</text>
|
||||
<view class="expertise-grid">
|
||||
<view
|
||||
v-for="(item, idx) in (doctor.expertise || [])"
|
||||
:key="idx"
|
||||
class="expertise-card"
|
||||
:class="{ 'expertise-wide': item.wide }"
|
||||
>
|
||||
<view class="expertise-icon" :class="'icon-' + (item.icon || 'default')">
|
||||
<text class="icon-emoji">{{ getExpertiseIcon(item.icon) }}</text>
|
||||
</view>
|
||||
<view class="expertise-text">
|
||||
<text class="expertise-title">{{ item.title }}</text>
|
||||
<text class="expertise-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</section>
|
||||
|
||||
<!-- 患者评价 -->
|
||||
<section class="reviews-section">
|
||||
<view class="reviews-header">
|
||||
<text class="section-title-plain">患者评价</text>
|
||||
<!-- <text class="view-all" @click="viewAllReviews">查看全部</text> -->
|
||||
</view>
|
||||
<view class="reviews-list">
|
||||
<view
|
||||
v-for="review in reviews"
|
||||
:key="review.id"
|
||||
class="review-card"
|
||||
>
|
||||
<view class="review-header">
|
||||
<view class="review-avatar">{{ review.patient_initials }}</view>
|
||||
<view class="review-meta">
|
||||
<text class="review-name">{{ review.patient_name }}</text>
|
||||
<view class="review-stars">
|
||||
<text v-for="i in 5" :key="i" class="star">★</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="review-time">{{ review.create_time }}</text>
|
||||
</view>
|
||||
<text class="review-content">"{{ review.content }}"</text>
|
||||
</view>
|
||||
</view>
|
||||
</section>
|
||||
</view>
|
||||
|
||||
<!-- 错误 -->
|
||||
<view v-else-if="!loading && error" class="error-wrap">
|
||||
<text class="error-text">{{ error }}</text>
|
||||
<view class="retry-btn" @click="loadDetail">重试</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<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">
|
||||
<text class="btn-icon">💬</text>
|
||||
<text class="btn-text">图文问诊</text>
|
||||
</view>
|
||||
<view class="btn-video" @click="goVideo" v-if="doctor.enable_video_consult">
|
||||
<text class="btn-icon">📹</text>
|
||||
<text class="btn-text">视频问诊</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import * as GenerateTestUserSig from "@/debug/GenerateTestUserSig-es.js";
|
||||
import { CallManager } from "@/TUICallKit/src/TUICallService/serve/callManager";
|
||||
const { proxy } = getCurrentInstance()
|
||||
let userId = ref("");
|
||||
let userSig= ref("");
|
||||
let SDKAppID=ref("");
|
||||
uni.CallManager = new CallManager();
|
||||
const doctorId = ref('')
|
||||
const doctor = ref(null)
|
||||
const reviews = ref([])
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const defaultAvatar = '/static/user/user.png'
|
||||
|
||||
const formatPatientCount = (count) => {
|
||||
if (count === undefined || count === null) return '1.2k+'
|
||||
if (count >= 1000) return (count / 1000).toFixed(1) + 'k+'
|
||||
return count + '+'
|
||||
}
|
||||
|
||||
const getExpertiseIcon = (icon) => {
|
||||
const map = { psychiatry: '🌿', eco: '🌱', vital_signs: '💓' }
|
||||
return map[icon] || '📋'
|
||||
}
|
||||
|
||||
const loadDetail = async () => {
|
||||
if (!doctorId.value) return
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
const res = await proxy.apiUrl({
|
||||
url: '/api/doctor/detail',
|
||||
method: 'GET',
|
||||
data: { id: doctorId.value }
|
||||
}, false)
|
||||
if (res && res.code === 1) {
|
||||
doctor.value = res.data
|
||||
} else {
|
||||
error.value = res?.msg || '加载失败'
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = '网络异常,请重试'
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadReviews = async () => {
|
||||
if (!doctorId.value) return
|
||||
try {
|
||||
const res = await proxy.apiUrl({
|
||||
url: '/api/doctor/reviews',
|
||||
method: 'GET',
|
||||
data: { doctor_id: doctorId.value, page_no: 1, page_size: 10 }
|
||||
}, false)
|
||||
if (res && res.code === 1) {
|
||||
reviews.value = res.data?.lists || []
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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)
|
||||
|
||||
const {userId } = res.data;
|
||||
const { userSig, SDKAppID } = GenerateTestUserSig.genTestUserSig({
|
||||
userID:userId,
|
||||
});
|
||||
console.log('获取签名成功:',userSig);
|
||||
|
||||
getApp().globalData.userID = userId;
|
||||
getApp().globalData.userSig = userSig;
|
||||
getApp().globalData.SDKAppID = SDKAppID;
|
||||
getApp().globalData.isIMInitialized = false; // 标记IM未初始化
|
||||
|
||||
// 初始化视频通话
|
||||
await uni.CallManager.init({
|
||||
sdkAppID: SDKAppID,
|
||||
userID: userId,
|
||||
userSig: userSig,
|
||||
globalCallPagePath: "TUICallKit/src/Components/TUICallKit",
|
||||
});
|
||||
|
||||
console.log('初始化完成,跳转到聊天页面');
|
||||
|
||||
|
||||
};
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages()
|
||||
const current = pages[pages.length - 1]
|
||||
const options = current.options || current.$page?.options || {}
|
||||
doctorId.value = options.id || options.doctorId || ''
|
||||
if (doctorId.value) {
|
||||
loadDetail().then(() => loadReviews())
|
||||
} else {
|
||||
error.value = '缺少医生ID'
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$bg: #faf9f5;
|
||||
$primary: #204e2b;
|
||||
$primary-container: #386641;
|
||||
$on-primary: #ffffff;
|
||||
$secondary-container: #fdc39a;
|
||||
$on-secondary-container: #794e2e;
|
||||
$surface-lowest: #ffffff;
|
||||
$surface-low: #f4f4f0;
|
||||
$surface-high: #e9e8e4;
|
||||
$surface-container: #efeeea;
|
||||
$on-surface: #1b1c1a;
|
||||
$on-surface-variant: #414941;
|
||||
$tertiary-container: #4b6500;
|
||||
$outline-variant: #c1c9be;
|
||||
|
||||
.page {
|
||||
background: $bg;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 180rpx;
|
||||
}
|
||||
|
||||
.loading-wrap, .error-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
.loading-text, .error-text {
|
||||
font-size: 28rpx;
|
||||
color: $on-surface-variant;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 24rpx;
|
||||
padding: 16rpx 48rpx;
|
||||
background: $primary;
|
||||
color: $on-primary;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 32rpx 48rpx 40rpx;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
margin-bottom: 80rpx;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
width: 320rpx;
|
||||
height: 320rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 24rpx;
|
||||
background: $surface-high;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
bottom: -16rpx;
|
||||
right: -16rpx;
|
||||
background: $tertiary-container;
|
||||
color: $on-primary;
|
||||
font-size: 22rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 9999rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hero-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.doctor-name {
|
||||
display: block;
|
||||
font-size: 64rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
margin-bottom: 16rpx;
|
||||
font-family: 'Noto Serif SC', 'STSong', 'SimSun', serif;
|
||||
}
|
||||
|
||||
.hospital {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
color: $primary;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.experience-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.verified {
|
||||
font-size: 32rpx;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.experience {
|
||||
font-size: 28rpx;
|
||||
color: $on-surface-variant;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 20rpx;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: $on-surface-variant;
|
||||
font-weight: bold;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 2rpx;
|
||||
height: 64rpx;
|
||||
background: $outline-variant;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.about-section {
|
||||
margin-bottom: 96rpx;
|
||||
}
|
||||
|
||||
.section-title-bar {
|
||||
padding-left: 32rpx;
|
||||
border-left: 8rpx solid $secondary-container;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
|
||||
.about-content {
|
||||
background: $surface-low;
|
||||
padding: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.about-text {
|
||||
font-size: 28rpx;
|
||||
color: $on-surface-variant;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.expertise-section {
|
||||
margin-bottom: 96rpx;
|
||||
}
|
||||
|
||||
.section-title-plain {
|
||||
display: block;
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
margin-bottom: 48rpx;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
|
||||
.expertise-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.expertise-card {
|
||||
background: $surface-lowest;
|
||||
padding: 40rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.expertise-card.expertise-wide {
|
||||
grid-column: span 2;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 48rpx;
|
||||
}
|
||||
|
||||
.expertise-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #ffdcc5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.expertise-icon.icon-eco {
|
||||
background: #ccf078;
|
||||
}
|
||||
|
||||
.expertise-icon.icon-vital_signs {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
background: #bcefc0;
|
||||
}
|
||||
|
||||
.icon-emoji {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.expertise-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: $on-surface;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.expertise-desc {
|
||||
font-size: 24rpx;
|
||||
color: $on-surface-variant;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.reviews-section {
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.reviews-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.view-all {
|
||||
font-size: 28rpx;
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reviews-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.review-card {
|
||||
background: $surface-lowest;
|
||||
padding: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.review-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: $surface-container;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface-variant;
|
||||
}
|
||||
|
||||
.review-meta {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.review-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.review-stars {
|
||||
color: #805533;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.review-time {
|
||||
font-size: 24rpx;
|
||||
color: $on-surface-variant;
|
||||
}
|
||||
|
||||
.review-content {
|
||||
font-size: 28rpx;
|
||||
color: $on-surface-variant;
|
||||
line-height: 1.6;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 48rpx;
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
background: rgba(250, 249, 245, 0.95);
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.btn-chat {
|
||||
flex: 1;
|
||||
height: 112rpx;
|
||||
background: $secondary-container;
|
||||
color: $on-secondary-container;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
font-weight: bold;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.btn-video {
|
||||
flex: 1;
|
||||
height: 112rpx;
|
||||
background: linear-gradient(135deg, $primary 0%, $primary-container 100%);
|
||||
color: $on-primary;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24rpx;
|
||||
font-weight: bold;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -116,12 +116,47 @@ function apiUrl(promise, Loading = true) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析页面参数(支持普通参数和扫码 scene 参数)
|
||||
* @param {Object} options - 页面 options,如 getCurrentPages()[].options
|
||||
* @returns {Object} 解析后的参数对象
|
||||
*/
|
||||
function parsePageParams(options) {
|
||||
const params = {}
|
||||
if (!options) return params
|
||||
|
||||
// 如果有 scene 参数(扫码进入),解析 scene
|
||||
if (options.scene) {
|
||||
try {
|
||||
const decodedScene = decodeURIComponent(options.scene)
|
||||
decodedScene.split('&').forEach(item => {
|
||||
const [key, value] = item.split('=')
|
||||
if (key && value) {
|
||||
params[key] = value
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('解析scene参数失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 合并普通参数(普通跳转进入)
|
||||
Object.keys(options).forEach(key => {
|
||||
if (key !== 'scene' && options[key]) {
|
||||
params[key] = options[key]
|
||||
}
|
||||
})
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
|
||||
// Vue3挂载全局属性的方式
|
||||
app.config.globalProperties.$url = baseUrl
|
||||
app.config.globalProperties.apiUrl = apiUrl
|
||||
app.config.globalProperties.$parsePageParams = parsePageParams
|
||||
|
||||
return {
|
||||
app
|
||||
|
||||
@@ -80,11 +80,22 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "doctor",
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/doctor/doctor",
|
||||
"style": {
|
||||
"navigationBarTitleText": "医生信息"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#FFA500",
|
||||
"color": "#8a8a8a",
|
||||
"selectedColor": "#204e2b",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [{
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<view class="card-container">
|
||||
<!-- 顶部标题栏 -->
|
||||
<view class="header">
|
||||
<text class="header-title">我的就诊卡</text>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 就诊卡列表 -->
|
||||
<view class="card-list">
|
||||
@@ -61,6 +59,7 @@
|
||||
<view v-if="!loading && cardList.length === 0" class="empty-state">
|
||||
<uni-icons type="wallet" size="60" color="#999"></uni-icons>
|
||||
<text class="empty-text">暂无就诊卡</text>
|
||||
<view class="add-card-btn" @click="createCard">新建就诊卡</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
@@ -73,7 +72,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, getCurrentInstance } from 'vue'
|
||||
|
||||
import { onLaunch, onShow, onHide, onError ,onShareAppMessage} from '@dcloudio/uni-app'
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
// 数据
|
||||
@@ -84,7 +83,9 @@ const loading = ref(false)
|
||||
onMounted(() => {
|
||||
loadCardList()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
loadCardList()
|
||||
})
|
||||
// 加载就诊卡列表
|
||||
const loadCardList = async () => {
|
||||
loading.value = true
|
||||
@@ -93,9 +94,9 @@ const loadCardList = async () => {
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: '/pages/login/login' })
|
||||
}, 1500)
|
||||
// setTimeout(() => {
|
||||
// uni.redirectTo({ url: '/pages/login/login' })
|
||||
// }, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ const loadCardList = async () => {
|
||||
const patientId = userData?.diagnosis?.patient_id
|
||||
|
||||
if (!patientId) {
|
||||
uni.showToast({ title: '患者信息不存在', icon: 'none' })
|
||||
//uni.showToast({ title: '患者信息不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,6 +138,18 @@ const viewCardDetail = (card) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 新建就诊卡(patient_id 创建后自动生成,仅需登录)
|
||||
const createCard = () => {
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/Card/edit_card?add=1'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
return status === 1 ? '启用' : '禁用'
|
||||
@@ -257,8 +270,8 @@ const formatDate = (timestamp) => {
|
||||
|
||||
&.status-confirmed {
|
||||
background: #f6ffed;
|
||||
color: #52c41a;
|
||||
border: 2rpx solid #b7eb8f;
|
||||
color: #204e2b;
|
||||
border: 2rpx solid #204e2b;
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
@@ -343,9 +356,19 @@ const formatDate = (timestamp) => {
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.add-card-btn {
|
||||
padding: 24rpx 64rpx;
|
||||
background: #1890ff;
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 48rpx;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -821,6 +821,7 @@ const agreedToTerms = ref(true) // 默认选中"是"
|
||||
const showDatePicker = ref(false)
|
||||
const diagnosisId = ref(null)
|
||||
const patientId = ref(null)
|
||||
const isAddMode = ref(false) // 新建模式
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
@@ -1063,23 +1064,37 @@ const loadCardDetail = async () => {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
diagnosisId.value = currentPage.options.id
|
||||
|
||||
if (!diagnosisId.value) {
|
||||
uni.showToast({ title: '诊单ID不存在', icon: 'none' })
|
||||
setTimeout(() => uni.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
const options = currentPage.options || {}
|
||||
diagnosisId.value = options.id
|
||||
isAddMode.value = options.add === '1' || options.add === 1
|
||||
|
||||
const userData = uni.getStorageSync('userData')
|
||||
patientId.value = userData?.diagnosis?.patient_id
|
||||
|
||||
// 新建模式:patient_id 可选(创建后自动生成),仅需登录
|
||||
if (isAddMode.value) {
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => uni.redirectTo({ url: '/pages/login/login' }), 1500)
|
||||
return
|
||||
}
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (!patientId.value) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => uni.redirectTo({ url: '/pages/login/login' }), 1500)
|
||||
return
|
||||
}
|
||||
|
||||
if (!diagnosisId.value) {
|
||||
uni.showToast({ title: '诊单ID不存在', icon: 'none' })
|
||||
setTimeout(() => uni.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await proxy.apiUrl({
|
||||
url: '/api/tcm/diagnosisDetail',
|
||||
method: 'GET',
|
||||
@@ -1302,18 +1317,26 @@ const submitForm = async () => {
|
||||
submitData.diagnosis_date = submitData.local_hospital_visit_date
|
||||
}
|
||||
|
||||
const apiUrl = isAddMode.value ? '/api/tcm/addCard' : '/api/tcm/updateCard'
|
||||
const apiData = isAddMode.value
|
||||
? { ...submitData, ...(patientId.value ? { patient_id: patientId.value } : {}) }
|
||||
: { id: diagnosisId.value, patient_id: patientId.value, ...submitData }
|
||||
|
||||
const res = await proxy.apiUrl({
|
||||
url: '/api/tcm/updateCard',
|
||||
url: apiUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
id: diagnosisId.value,
|
||||
patient_id: patientId.value,
|
||||
...submitData
|
||||
}
|
||||
data: apiData
|
||||
})
|
||||
|
||||
if (res.code === 1) {
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
// 新建成功:若有返回 patient_id,写入 userData 供后续列表查询
|
||||
if (isAddMode.value && res.data?.patient_id) {
|
||||
const userData = uni.getStorageSync('userData') || {}
|
||||
if (!userData.diagnosis) userData.diagnosis = {}
|
||||
userData.diagnosis.patient_id = res.data.patient_id
|
||||
uni.setStorageSync('userData', userData)
|
||||
}
|
||||
uni.showToast({ title: isAddMode.value ? '创建成功' : '保存成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
|
||||
@@ -1,39 +1,123 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-section">
|
||||
<view class="search-bar">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input class="search-input" placeholder="搜索药材、医生或症状..." type="text" />
|
||||
<text class="search-filter">⚙</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 科室分类 -->
|
||||
<view class="department-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">科室分类</text>
|
||||
<text class="more-link" @click="navigateToDepartments">查看全部</text>
|
||||
</view>
|
||||
<view class="department-grid">
|
||||
<view class="dept-col-left">
|
||||
<view
|
||||
class="department-card large"
|
||||
@click="navigateToDepartment(departments[0])"
|
||||
>
|
||||
<view class="dept-card-inner dept-green">
|
||||
<view class="dept-icon"><text class="icon-text">✦</text></view>
|
||||
<text class="dept-name text-white">针灸</text>
|
||||
<text class="dept-desc text-white-sub">调理气血</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="dept-col-right">
|
||||
<view
|
||||
class="department-card small"
|
||||
@click="navigateToDepartment(departments[1])"
|
||||
>
|
||||
<view class="dept-card-inner dept-beige">
|
||||
<view class="dept-icon-wrap">
|
||||
<view class="dept-icon"><text class="icon-text">●</text></view>
|
||||
</view>
|
||||
<view>
|
||||
<text class="dept-name">中药</text>
|
||||
<text class="dept-desc">古法方剂</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="department-card small"
|
||||
@click="navigateToDepartment(departments[2])"
|
||||
>
|
||||
<view class="dept-card-inner dept-grey">
|
||||
<view class="dept-icon-wrap">
|
||||
<view class="dept-icon"><text class="icon-text">☯</text></view>
|
||||
</view>
|
||||
<view>
|
||||
<text class="dept-name">推拿</text>
|
||||
<text class="dept-desc">中医按摩</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 问诊服务 -->
|
||||
|
||||
|
||||
<!-- 特色专科医师 -->
|
||||
<!-- 名医推荐 -->
|
||||
<view class="doctors-section">
|
||||
<view class="section-header">
|
||||
<view class="section-title-wrapper">
|
||||
|
||||
<view class="section-title">特色专科医师</view>
|
||||
</view>
|
||||
<view class="more-link" @click="navigateToAllDoctors">更多</view>
|
||||
<text class="section-title">名医推荐</text>
|
||||
<text class="more-link" @click="navigateToAllDoctors">查看全部</text>
|
||||
</view>
|
||||
|
||||
<view class="doctors-grid">
|
||||
<view class="doctors-list">
|
||||
<view
|
||||
v-for="doctor in doctors"
|
||||
:key="doctor.id"
|
||||
class="doctor-card"
|
||||
@click="selectDoctor(doctor)"
|
||||
>
|
||||
<view class="doctor-avatar-wrapper">
|
||||
<image :src="doctor.avatar" class="doctor-avatar" mode="aspectFill"></image>
|
||||
<view class="avatar-frame"></view>
|
||||
<image :src="doctor.avatar || defaultAvatar" class="doctor-photo" mode="aspectFill"></image>
|
||||
<view class="doctor-info">
|
||||
<view class="doctor-top">
|
||||
<text class="doctor-name">{{ doctor.name }}</text>
|
||||
<text class="doctor-rating">★ {{ doctor.rating || '4.9' }}</text>
|
||||
</view>
|
||||
<text class="doctor-specialty">{{ doctor.specialty || doctor.title || '中医' }}</text>
|
||||
<view class="doctor-price-row">
|
||||
<text class="doctor-price">{{ doctor.price || '' }}</text>
|
||||
<text class="doctor-unit">/次</text>
|
||||
</view>
|
||||
<view class="doctor-actions">
|
||||
<view class="appointment-btn" @click.stop="selectDoctor(doctor)">预约</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="doctor-name">{{ doctor.name }}</view>
|
||||
<view class="doctor-title">{{ doctor.title }}</view>
|
||||
<view class="doctor-dept">{{ doctor.department || '内科' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 健康百科 -->
|
||||
<view class="encyclopedia-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">健康百科</text>
|
||||
<text class="more-link" @click="navigateToEncyclopedia">查看全部</text>
|
||||
</view>
|
||||
<scroll-view class="encyclopedia-scroll no-scrollbar" scroll-x :show-scrollbar="false">
|
||||
<view
|
||||
v-for="article in articles"
|
||||
:key="article.id"
|
||||
class="article-card"
|
||||
:class="{ 'article-card-secondary': article.id === 2 }"
|
||||
@click="navigateToArticle(article)"
|
||||
>
|
||||
<view class="article-image-wrap">
|
||||
<image :src="article.cover" class="article-image" mode="aspectFill"></image>
|
||||
<view class="article-gradient" :class="{ 'gradient-secondary': article.id === 2 }"></view>
|
||||
<view class="article-tag" :class="{ 'tag-secondary': article.id === 2 }">{{ article.tag }}</view>
|
||||
</view>
|
||||
<text class="article-title" :class="{ 'title-secondary': article.id === 2 }">{{ article.title }}</text>
|
||||
<text class="article-desc">{{ article.desc }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -45,51 +129,46 @@ const { proxy } = getCurrentInstance()
|
||||
|
||||
const doctors = ref([])
|
||||
const loading = ref(false)
|
||||
const defaultAvatar = '/static/user/user.png'
|
||||
|
||||
// 科室分类
|
||||
const departments = ref([
|
||||
{ id: 1, name: '针灸', desc: '调理气血', icon: '✦', iconClass: 'icon-acupuncture', bgColor: 'linear-gradient(135deg, #2d5a3d 0%, #3d7a4d 100%)', dark: true, size: 'large', pattern: true },
|
||||
{ id: 2, name: '中药', desc: '古法方剂', icon: '◉', iconClass: 'icon-herb', bgColor: '#F5E6D3', dark: false, size: 'small' },
|
||||
{ id: 3, name: '推拿', desc: '中医按摩', icon: '☯', iconClass: 'icon-massage', bgColor: '#E8E4DF', dark: false, size: 'small' }
|
||||
])
|
||||
|
||||
// 健康百科
|
||||
const articles = ref([
|
||||
{ id: 1, title: '晨间生姜:脾胃健康的催化剂', desc: '了解每天早晨一片生姜如何改善您的新陈代谢...', tag: '膳食营养', cover: 'https://picsum.photos/seed/herb/300/180' },
|
||||
{ id: 2, title: '四时呼吸:顺应节气养生', desc: '顺应节气,调养身心...', tag: '正念冥想', cover: 'https://picsum.photos/seed/nature/300/180' },
|
||||
{ id: 3, title: '中医养生之道', desc: '传统智慧,现代健康...', tag: '养生保健', cover: 'https://picsum.photos/seed/health/300/180' }
|
||||
])
|
||||
|
||||
const selectDoctor = (doctor) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/index?doctorId=${doctor.id}&doctorName=${doctor.name}`
|
||||
url: `/doctor/pages/doctor/doctor?id=${doctor.id}`
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToExperts = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/experts/index'
|
||||
})
|
||||
const navigateToDepartments = () => {
|
||||
uni.showToast({ title: '科室列表', icon: 'none' })
|
||||
}
|
||||
|
||||
const navigateToConsultation = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/consultation/index'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToQuickConsult = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/quick-consult/index'
|
||||
})
|
||||
const navigateToDepartment = (item) => {
|
||||
uni.showToast({ title: item.name, icon: 'none' })
|
||||
}
|
||||
|
||||
const navigateToAllDoctors = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/doctors/index'
|
||||
})
|
||||
uni.showToast({ title: '更多医生', icon: 'none' })
|
||||
}
|
||||
|
||||
const navigateToHome = () => {
|
||||
// 已在首页
|
||||
const navigateToEncyclopedia = () => {
|
||||
uni.showToast({ title: '健康百科', icon: 'none' })
|
||||
}
|
||||
|
||||
const navigateToMessages = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/messages/index'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToProfile = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/profile/index'
|
||||
})
|
||||
const navigateToArticle = (article) => {
|
||||
uni.showToast({ title: article.title, icon: 'none' })
|
||||
}
|
||||
|
||||
const fetchDoctors = async () => {
|
||||
@@ -124,377 +203,454 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* TCM Care 设计系统 - 源自 code.html */
|
||||
$background: #faf9f5;
|
||||
$primary: #204e2b;
|
||||
$primary-container: #386641;
|
||||
$on-primary: #ffffff;
|
||||
$on-primary-container: #afe2b3;
|
||||
$secondary-container: #fdc39a;
|
||||
$on-secondary-container: #794e2e;
|
||||
$surface-container-lowest: #ffffff;
|
||||
$surface-container-low: #f4f4f0;
|
||||
$surface-container-high: #e9e8e4;
|
||||
$surface-container-highest: #e3e2df;
|
||||
$on-surface: #1b1c1a;
|
||||
$on-surface-variant: #414941;
|
||||
$tertiary-container: #4b6500;
|
||||
$tertiary: #384c00;
|
||||
$outline: #727970;
|
||||
|
||||
.container {
|
||||
background: #f5f3f0;
|
||||
background: $background;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 120rpx;
|
||||
padding: 24rpx 32rpx 120rpx;
|
||||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
// 古典卷轴横幅
|
||||
.scroll-banner {
|
||||
margin: 24rpx 16rpx;
|
||||
.font-headline {
|
||||
font-family: 'Noto Serif', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', serif;
|
||||
}
|
||||
|
||||
// 搜索栏 - surface-container-low
|
||||
.search-section {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background: $surface-container-low;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
}
|
||||
|
||||
.scroll-rod {
|
||||
width: 32rpx;
|
||||
height: 160rpx;
|
||||
background: linear-gradient(180deg, #8B7355 0%, #6B5644 50%, #8B7355 100%);
|
||||
border-radius: 16rpx;
|
||||
position: relative;
|
||||
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.2);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: radial-gradient(circle, #D4AF37 0%, #B8941E 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: radial-gradient(circle, #D4AF37 0%, #B8941E 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.search-icon {
|
||||
font-size: 36rpx;
|
||||
color: $outline;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.scroll-rod-left {
|
||||
margin-right: -8rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.scroll-rod-right {
|
||||
margin-left: -8rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.scroll-content {
|
||||
.search-input {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #F5E6D3 0%, #E8D7C3 100%);
|
||||
border: 3rpx solid #8B7355;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: inset 0 2rpx 8rpx rgba(139, 115, 85, 0.2);
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
left: 12rpx;
|
||||
right: 12rpx;
|
||||
bottom: 12rpx;
|
||||
border: 1rpx solid rgba(139, 115, 85, 0.3);
|
||||
border-radius: 8rpx;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.scroll-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.scroll-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #3E2723;
|
||||
margin-bottom: 8rpx;
|
||||
font-family: 'STKaiti', 'KaiTi', serif;
|
||||
}
|
||||
|
||||
.scroll-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #5D4037;
|
||||
font-family: 'STKaiti', 'KaiTi', serif;
|
||||
}
|
||||
|
||||
.scroll-image {
|
||||
width: 200rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 2rpx solid #8B7355;
|
||||
}
|
||||
|
||||
// 问诊服务
|
||||
.services-section {
|
||||
padding: 32rpx 16rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #3E2723;
|
||||
margin-bottom: 24rpx;
|
||||
font-family: 'STKaiti', 'KaiTi', serif;
|
||||
position: relative;
|
||||
padding-left: 16rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
background: linear-gradient(180deg, #D4AF37 0%, #B8941E 100%);
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.services-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 320rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.card-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.15;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.bg-decoration {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-expert {
|
||||
background: linear-gradient(135deg, #FFF8DC 0%, #F5E6D3 100%);
|
||||
border: 2rpx solid #E8D7C3;
|
||||
}
|
||||
|
||||
.service-health {
|
||||
background: linear-gradient(135deg, #E8F5E9 0%, #C8E6C9 100%);
|
||||
border: 2rpx solid #A5D6A7;
|
||||
}
|
||||
|
||||
.service-quick {
|
||||
background: linear-gradient(135deg, #E0F2F1 0%, #B2DFDB 100%);
|
||||
border: 2rpx solid #80CBC4;
|
||||
}
|
||||
|
||||
.service-title {
|
||||
background: transparent;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #3E2723;
|
||||
margin-bottom: 12rpx;
|
||||
font-family: 'STKaiti', 'KaiTi', serif;
|
||||
color: $on-surface;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 22rpx;
|
||||
color: #5D4037;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16rpx;
|
||||
flex: 1;
|
||||
.search-input::placeholder {
|
||||
color: #414941;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
font-size: 56rpx;
|
||||
text-align: center;
|
||||
margin: 16rpx 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.service-btn {
|
||||
color: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.15);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.btn-orange {
|
||||
background: linear-gradient(135deg, #D2691E 0%, #CD853F 100%);
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
background: linear-gradient(135deg, #2E7D32 0%, #388E3C 100%);
|
||||
}
|
||||
|
||||
.btn-purple {
|
||||
background: linear-gradient(135deg, #4A148C 0%, #6A1B9A 100%);
|
||||
}
|
||||
|
||||
// 特色专科医师
|
||||
.doctors-section {
|
||||
padding: 32rpx 16rpx;
|
||||
.search-filter {
|
||||
font-size: 36rpx;
|
||||
color: $outline;
|
||||
}
|
||||
|
||||
// 通用区块
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.section-title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 28rpx;
|
||||
.section-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: $on-surface;
|
||||
font-family: 'Noto Serif', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', serif;
|
||||
}
|
||||
|
||||
.more-link {
|
||||
font-size: 24rpx;
|
||||
color: #D4AF37;
|
||||
font-size: 28rpx;
|
||||
color: $primary;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.doctors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16rpx;
|
||||
// 科室分类 - grid 布局
|
||||
.department-section {
|
||||
margin-bottom: 64rpx;
|
||||
}
|
||||
|
||||
.department-grid {
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.dept-col-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dept-col-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.department-card {
|
||||
&.large {
|
||||
height: 100%;
|
||||
min-height: 320rpx;
|
||||
}
|
||||
|
||||
&.small {
|
||||
flex: 1;
|
||||
min-height: 160rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dept-card-inner {
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx;
|
||||
height: 100%;
|
||||
min-height: 160rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 针灸 - primary-container */
|
||||
.dept-green {
|
||||
background: $primary-container;
|
||||
color: $on-primary;
|
||||
}
|
||||
|
||||
.dept-green .dept-icon .icon-text,
|
||||
.dept-green .dept-name {
|
||||
color: $on-primary;
|
||||
}
|
||||
|
||||
.dept-green .dept-desc {
|
||||
color: $on-primary-container;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 中药 - secondary-container */
|
||||
.dept-beige {
|
||||
background: $secondary-container;
|
||||
color: $on-secondary-container;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.dept-beige .dept-icon-wrap {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
padding: 16rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.dept-beige .dept-icon .icon-text {
|
||||
font-size: 40rpx;
|
||||
color: $on-secondary-container;
|
||||
}
|
||||
|
||||
.dept-beige .dept-name {
|
||||
color: $on-secondary-container;
|
||||
margin-bottom: 4rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dept-beige .dept-desc {
|
||||
color: $on-secondary-container;
|
||||
opacity: 0.8;
|
||||
font-size: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 推拿 - surface-container-high */
|
||||
.dept-grey {
|
||||
background: $surface-container-high;
|
||||
color: $on-surface;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.dept-grey .dept-icon-wrap {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
padding: 16rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.dept-grey .dept-icon .icon-text {
|
||||
font-size: 40rpx;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.dept-grey .dept-name {
|
||||
color: $on-surface;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dept-grey .dept-desc {
|
||||
color: $on-surface-variant;
|
||||
font-size: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dept-icon {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.dept-beige .dept-icon,
|
||||
.dept-grey .dept-icon {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.dept-icon .icon-text {
|
||||
font-size: 56rpx;
|
||||
color: $on-primary;
|
||||
}
|
||||
|
||||
.dept-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: $on-surface;
|
||||
margin-bottom: 8rpx;
|
||||
font-family: 'Noto Serif', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', serif;
|
||||
|
||||
&.text-white {
|
||||
color: $on-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.dept-desc {
|
||||
font-size: 26rpx;
|
||||
color: $on-surface-variant;
|
||||
|
||||
&.text-white-sub {
|
||||
color: $on-primary-container;
|
||||
}
|
||||
}
|
||||
|
||||
// 名医推荐
|
||||
.doctors-section {
|
||||
margin-bottom: 64rpx;
|
||||
}
|
||||
|
||||
.doctors-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.doctor-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.doctor-avatar-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 16rpx;
|
||||
background: $surface-container-lowest;
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.doctor-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 3rpx solid #E8E8E8;
|
||||
.doctor-photo {
|
||||
width: 160rpx;
|
||||
height: 230rpx;
|
||||
border-radius: 16rpx;
|
||||
flex-shrink: 0;
|
||||
background: $surface-container-highest;
|
||||
}
|
||||
|
||||
.avatar-frame {
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border: 2rpx solid rgba(212, 175, 55, 0.3);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
.doctor-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 192rpx;
|
||||
}
|
||||
|
||||
.doctor-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.doctor-name {
|
||||
font-size: 26rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #3E2723;
|
||||
margin-bottom: 6rpx;
|
||||
color: $on-surface;
|
||||
font-family: 'Noto Serif', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', serif;
|
||||
}
|
||||
|
||||
.doctor-title {
|
||||
font-size: 22rpx;
|
||||
color: #757575;
|
||||
margin-bottom: 4rpx;
|
||||
.doctor-rating {
|
||||
font-size: 24rpx;
|
||||
color: #805533;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.doctor-dept {
|
||||
font-size: 20rpx;
|
||||
color: #9E9E9E;
|
||||
.doctor-specialty {
|
||||
font-size: 28rpx;
|
||||
color: $on-surface-variant;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
// 底部导航
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
.doctor-price-row {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.doctor-price {
|
||||
font-size: 28rpx;
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.doctor-unit {
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
color: $on-surface-variant;
|
||||
}
|
||||
|
||||
.doctor-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.appointment-btn {
|
||||
background: $primary;
|
||||
color: $on-primary;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 9999rpx;
|
||||
}
|
||||
|
||||
// 健康百科 - 横向滚动
|
||||
.encyclopedia-section {
|
||||
margin-bottom: 64rpx;
|
||||
}
|
||||
|
||||
.encyclopedia-scroll {
|
||||
white-space: nowrap;
|
||||
margin: 0 -32rpx;
|
||||
padding: 0 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.article-card {
|
||||
display: inline-block;
|
||||
width: 560rpx;
|
||||
margin-right: 40rpx;
|
||||
background: rgba(75, 101, 0, 0.06);
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.article-card-secondary {
|
||||
background: rgba(253, 195, 154, 0.1);
|
||||
}
|
||||
|
||||
.article-card:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.article-image-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 256rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.article-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $tertiary-container;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.article-gradient {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #E8E8E8;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 16rpx 0 24rpx 0;
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
z-index: 100;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
background: linear-gradient(to top, $tertiary-container, transparent);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
flex: 1;
|
||||
.article-gradient.gradient-secondary {
|
||||
background: linear-gradient(to top, $secondary-container, transparent);
|
||||
}
|
||||
|
||||
&.active {
|
||||
.nav-icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
color: #D4AF37;
|
||||
.article-tag {
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
left: 24rpx;
|
||||
background: $tertiary;
|
||||
color: $on-primary;
|
||||
font-size: 20rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 9999rpx;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
letter-spacing: 0.05em;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 36rpx;
|
||||
transition: all 0.3s ease;
|
||||
.article-tag.tag-secondary {
|
||||
background: #805533;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
font-size: 22rpx;
|
||||
color: #757575;
|
||||
transition: all 0.3s ease;
|
||||
.article-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #394d00;
|
||||
padding: 32rpx 32rpx 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
font-family: 'Noto Serif', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', serif;
|
||||
}
|
||||
|
||||
.article-title.title-secondary {
|
||||
color: $on-secondary-container;
|
||||
}
|
||||
|
||||
.article-desc {
|
||||
font-size: 24rpx;
|
||||
color: $on-surface-variant;
|
||||
padding: 0 32rpx 32rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -64,15 +64,17 @@ import { ref,onMounted,getCurrentInstance} from "vue";
|
||||
const { proxy } = getCurrentInstance()
|
||||
import * as GenerateTestUserSig from "@/debug/GenerateTestUserSig-es.js";
|
||||
import { CallManager } from "@/TUICallKit/src/TUICallService/serve/callManager";
|
||||
import { onLaunch, onShow, onHide, onError ,onShareAppMessage} from '@dcloudio/uni-app'
|
||||
let userID = ref("4");
|
||||
let avatarUrl = ref(""); // 声明为响应式变量
|
||||
uni.CallManager = new CallManager();
|
||||
|
||||
onMounted(() => {
|
||||
onLaunch(() => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: function (loginRes) {
|
||||
|
||||
|
||||
wxcode(loginRes.code)
|
||||
// 获取用户信息
|
||||
|
||||
@@ -106,6 +108,7 @@ onShareAppMessage((res) =>{
|
||||
|
||||
const wxcode = async (code) => {
|
||||
|
||||
return
|
||||
try {
|
||||
// 通过 proxy 调用全局的 apiUrl
|
||||
const res = await proxy.apiUrl({
|
||||
|
||||
@@ -50,16 +50,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from "vue";
|
||||
import { ref, getCurrentInstance,onMounted } from "vue";
|
||||
import { onShow, onShareAppMessage } from '@dcloudio/uni-app'
|
||||
const { proxy } = getCurrentInstance()
|
||||
import * as GenerateTestUserSig from "@/debug/GenerateTestUserSig-es.js";
|
||||
import { CallManager } from "@/TUICallKit/src/TUICallService/serve/callManager";
|
||||
const { proxy } = getCurrentInstance()
|
||||
let userId = ref("");
|
||||
let userSig= ref("");
|
||||
let SDKAppID=ref("");
|
||||
uni.CallManager = new CallManager();
|
||||
|
||||
const doctorId = ref('')
|
||||
const doctorName = ref('')
|
||||
onShow(() => {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
@@ -69,7 +70,15 @@ onShow(() => {
|
||||
}
|
||||
});
|
||||
})
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
// const options = current.options || current.$page?.options || {}
|
||||
const options = proxy.$parsePageParams(currentPage.options|| currentPage.$page?.options || {});
|
||||
doctorId.value = options.id || options.doctorId || ''
|
||||
console.log('---------进入onMounted',options,doctorId.value)
|
||||
|
||||
})
|
||||
onShareAppMessage((res) =>{
|
||||
return {
|
||||
title: '视频问诊通话邀请',
|
||||
@@ -103,7 +112,7 @@ const wxcode = async (code) => {
|
||||
}
|
||||
}
|
||||
const url=()=>{
|
||||
const doctor='doctor_'+uni.getStorageSync('userData').diagnosis.diagnosis_id
|
||||
const doctor=doctorId.value?'doctor_'+doctorId.value:'doctor_'+uni.getStorageSync('userData').diagnosis.diagnosis_id
|
||||
// 跳转到聊天页面
|
||||
uni.navigateTo({
|
||||
url: `/TUIKit/pages/chat/chat?userID=${encodeURIComponent(userId)}&userSig=${encodeURIComponent(userSig)}&SDKAppID=${SDKAppID}&targetUserID=${doctor}`
|
||||
|
||||
@@ -519,51 +519,15 @@ const getDictData = async (type) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 解析页面参数(支持普通参数和scene参数)
|
||||
const parsePageParams = (options) => {
|
||||
const params = {};
|
||||
|
||||
// 如果有scene参数(扫码进入),解析scene
|
||||
if (options.scene) {
|
||||
try {
|
||||
// URL解码scene参数
|
||||
const decodedScene = decodeURIComponent(options.scene);
|
||||
// 解析键值对:id=4&share_user=1
|
||||
decodedScene.split('&').forEach(item => {
|
||||
const [key, value] = item.split('=');
|
||||
if (key && value) {
|
||||
params[key] = value;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('解析scene参数失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 合并普通参数(普通跳转进入)
|
||||
Object.keys(options).forEach(key => {
|
||||
if (key !== 'scene' && options[key]) {
|
||||
params[key] = options[key];
|
||||
}
|
||||
});
|
||||
|
||||
// 获取当前登录用户ID(从本地存储或全局状态获取)
|
||||
// TODO: 根据你的项目实际情况获取用户ID
|
||||
// 例如: const userInfo = uni.getStorageSync('userInfo');
|
||||
// params.user_id = userInfo?.id || 0;
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
// 加载诊单详情
|
||||
const loadDiagnosisDetail = async () => {
|
||||
// 从页面参数获取诊单ID
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const params = parsePageParams(currentPage.options);
|
||||
const params = proxy.$parsePageParams(currentPage.options);
|
||||
dingdan_ok.value=uni.getStorageSync('dingdan_ok');
|
||||
const diagnosisId = params.id;
|
||||
console.log('页面参数:', params, '诊单ID:', diagnosisId);
|
||||
console.log('进入onMounted:', params, '诊单ID:', diagnosisId);
|
||||
|
||||
if (!diagnosisId) {
|
||||
uni.showToast({ title: '缺少诊单ID', icon: 'none' });
|
||||
@@ -724,7 +688,7 @@ const handleConfirm = () => {
|
||||
const confirmDiagnosis = async () => {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const params = parsePageParams(currentPage.options);
|
||||
const params = proxy.$parsePageParams(currentPage.options);
|
||||
|
||||
console.log('确认诊单参数:', params);
|
||||
|
||||
|
||||
@@ -100,36 +100,13 @@ onMounted(() => {
|
||||
loadOrderDetail()
|
||||
})
|
||||
|
||||
const parsePageParams = (options) => {
|
||||
const params = {}
|
||||
if (options.scene) {
|
||||
try {
|
||||
const decodedScene = decodeURIComponent(options.scene)
|
||||
decodedScene.split('&').forEach(item => {
|
||||
const [key, value] = item.split('=')
|
||||
if (key && value) {
|
||||
params[key] = value
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('解析scene参数失败:', error)
|
||||
}
|
||||
}
|
||||
Object.keys(options).forEach(key => {
|
||||
if (key !== 'scene' && options[key]) {
|
||||
params[key] = options[key]
|
||||
}
|
||||
})
|
||||
return params
|
||||
}
|
||||
|
||||
const loadOrderDetail = async () => {
|
||||
loading.value = true
|
||||
errorMsg.value = ''
|
||||
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const params = parsePageParams(currentPage.options || {})
|
||||
const params = proxy.$parsePageParams(currentPage.options || {})
|
||||
orderNo.value = params.order_no || ''
|
||||
|
||||
if (!orderNo.value) {
|
||||
|
||||
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -78,5 +78,9 @@ export const doctorApi = {
|
||||
getDoctorDetail: (id) => get('/api/doctor/detail', { id }, false),
|
||||
|
||||
// 获取医生排班
|
||||
getDoctorRoster: (doctorId, date) => get('/api/doctor/roster', { doctor_id: doctorId, date }, false)
|
||||
getDoctorRoster: (doctorId, date) => get('/api/doctor/roster', { doctor_id: doctorId, date }, false),
|
||||
|
||||
// 获取医生评价列表
|
||||
getDoctorReviews: (doctorId, pageNo = 1, pageSize = 10) =>
|
||||
get('/api/doctor/reviews', { doctor_id: doctorId, page_no: pageNo, page_size: pageSize }, false)
|
||||
}
|
||||
|
||||
@@ -100,6 +100,41 @@
|
||||
<el-input v-model="formData.title" placeholder="如:主任医师、副主任医师" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 执业证书编号 -->
|
||||
<el-form-item label="执业证书编号" prop="license_no">
|
||||
<el-input v-model="formData.license_no" placeholder="请输入执业证书编号" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 图文问诊 -->
|
||||
<el-form-item label="图文问诊">
|
||||
<el-switch
|
||||
v-model="formData.enable_image_consult"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<span class="ml-2 text-gray-500">{{ formData.enable_image_consult === 1 ? '已开启' : '已关闭' }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 视频问诊 -->
|
||||
<el-form-item label="视频问诊">
|
||||
<el-switch
|
||||
v-model="formData.enable_video_consult"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<span class="ml-2 text-gray-500">{{ formData.enable_video_consult === 1 ? '已开启' : '已关闭' }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否开启收费 -->
|
||||
<el-form-item label="是否开启收费">
|
||||
<el-switch
|
||||
v-model="formData.enable_charge"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<span class="ml-2 text-gray-500">{{ formData.enable_charge === 1 ? '已开启' : '已关闭' }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 所属科室 -->
|
||||
<el-form-item label="所属科室" prop="department">
|
||||
<el-input v-model="formData.department" placeholder="如:中医科、内科" clearable />
|
||||
@@ -228,6 +263,10 @@ const formData = reactive({
|
||||
jobs_id: [],
|
||||
avatar: '',
|
||||
title: '', // 职称
|
||||
license_no: '', // 执业证书编号
|
||||
enable_image_consult: 1, // 是否开启图文问诊
|
||||
enable_video_consult: 1, // 是否开启视频问诊
|
||||
enable_charge: 0, // 是否开启收费
|
||||
department: '', // 所属科室
|
||||
specialty: '', // 擅长领域
|
||||
education: '', // 教育背景
|
||||
@@ -353,6 +392,10 @@ const open = (type = 'add') => {
|
||||
jobs_id: [],
|
||||
avatar: '',
|
||||
title: '',
|
||||
license_no: '',
|
||||
enable_image_consult: 1,
|
||||
enable_video_consult: 1,
|
||||
enable_charge: 0,
|
||||
department: '',
|
||||
specialty: '',
|
||||
education: '',
|
||||
|
||||
@@ -37,6 +37,15 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="快捷日期">
|
||||
<el-radio-group v-model="formData.date_preset" @change="handleDatePresetChange" size="default">
|
||||
<el-radio-button label="">不限</el-radio-button>
|
||||
<el-radio-button label="today">今天</el-radio-button>
|
||||
<el-radio-button label="tomorrow">明天</el-radio-button>
|
||||
<el-radio-button label="day_after">后天</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
@@ -51,7 +60,7 @@
|
||||
<el-tab-pane label="全部" name="all" />
|
||||
<el-tab-pane name="1">
|
||||
<template #label>
|
||||
<span>已预约</span>
|
||||
<span>未就诊</span>
|
||||
<el-badge v-if="statusCount[1]" :value="statusCount[1]" class="ml-2" type="success" />
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
@@ -303,6 +312,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import { watch } from 'vue'
|
||||
import { appointmentLists, cancelAppointment, completeAppointment, appointmentDetail } from '@/api/doctor'
|
||||
import { getCallSignature, generateMiniProgramQrcode } from '@/api/tcm'
|
||||
import feedback from '@/utils/feedback'
|
||||
@@ -332,7 +342,8 @@ const formData = reactive({
|
||||
doctor_name: '',
|
||||
status: '' as string | number,
|
||||
start_date: '',
|
||||
end_date: ''
|
||||
end_date: '',
|
||||
date_preset: '' as '' | 'today' | 'tomorrow' | 'day_after'
|
||||
})
|
||||
|
||||
const activeTab = ref('all')
|
||||
@@ -348,6 +359,28 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
params: formData
|
||||
})
|
||||
|
||||
// 手动修改日期范围时清除快捷日期
|
||||
watch(
|
||||
() => [formData.start_date, formData.end_date],
|
||||
([start, end]) => {
|
||||
if (!formData.date_preset) return
|
||||
const today = new Date()
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
const todayStr = `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`
|
||||
const tomorrow = new Date(today)
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
const tomorrowStr = `${tomorrow.getFullYear()}-${pad(tomorrow.getMonth() + 1)}-${pad(tomorrow.getDate())}`
|
||||
const dayAfter = new Date(today)
|
||||
dayAfter.setDate(dayAfter.getDate() + 2)
|
||||
const dayAfterStr = `${dayAfter.getFullYear()}-${pad(dayAfter.getMonth() + 1)}-${pad(dayAfter.getDate())}`
|
||||
const expected = formData.date_preset === 'today' ? todayStr : formData.date_preset === 'tomorrow' ? tomorrowStr : dayAfterStr
|
||||
const startStr = (start || '').split(' ')[0]
|
||||
if (startStr !== expected) {
|
||||
formData.date_preset = ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 获取各状态数量
|
||||
const getStatusCount = async () => {
|
||||
try {
|
||||
@@ -387,15 +420,46 @@ const loadData = async () => {
|
||||
await getStatusCount()
|
||||
}
|
||||
|
||||
// 快捷日期变更
|
||||
const handleDatePresetChange = (val: string | number | boolean | undefined) => {
|
||||
const v = String(val || '')
|
||||
if (!v) {
|
||||
formData.start_date = ''
|
||||
formData.end_date = ''
|
||||
loadData()
|
||||
return
|
||||
}
|
||||
const today = new Date()
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
let target: Date
|
||||
if (v === 'today') {
|
||||
target = today
|
||||
} else if (v === 'tomorrow') {
|
||||
target = new Date(today)
|
||||
target.setDate(target.getDate() + 1)
|
||||
} else {
|
||||
target = new Date(today)
|
||||
target.setDate(target.getDate() + 2)
|
||||
}
|
||||
const dateStr = `${target.getFullYear()}-${pad(target.getMonth() + 1)}-${pad(target.getDate())}`
|
||||
formData.start_date = dateStr
|
||||
formData.end_date = dateStr
|
||||
pager.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = () => {
|
||||
resetPage()
|
||||
pager.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
activeTab.value = 'all'
|
||||
formData.date_preset = ''
|
||||
resetParams()
|
||||
getStatusCount()
|
||||
}
|
||||
|
||||
const detailVisible = ref(false)
|
||||
|
||||
@@ -70,6 +70,10 @@ class AdminLogic extends BaseLogic
|
||||
'education' => $params['education'] ?? null,
|
||||
'experience' => $params['experience'] ?? null,
|
||||
'honors' => $params['honors'] ?? null,
|
||||
'license_no' => $params['license_no'] ?? '',
|
||||
'enable_image_consult' => $params['enable_image_consult'] ?? 1,
|
||||
'enable_video_consult' => $params['enable_video_consult'] ?? 1,
|
||||
'enable_charge' => $params['enable_charge'] ?? 0,
|
||||
]);
|
||||
|
||||
// 角色
|
||||
@@ -120,6 +124,10 @@ class AdminLogic extends BaseLogic
|
||||
'education' => $params['education'] ?? null,
|
||||
'experience' => $params['experience'] ?? null,
|
||||
'honors' => $params['honors'] ?? null,
|
||||
'license_no' => $params['license_no'] ?? '',
|
||||
'enable_image_consult' => $params['enable_image_consult'] ?? 1,
|
||||
'enable_video_consult' => $params['enable_video_consult'] ?? 1,
|
||||
'enable_charge' => $params['enable_charge'] ?? 0,
|
||||
];
|
||||
|
||||
// 头像
|
||||
@@ -254,6 +262,7 @@ class AdminLogic extends BaseLogic
|
||||
'multipoint_login', 'avatar',
|
||||
'gender', 'age', 'phone', 'title', 'department',
|
||||
'specialty', 'education', 'experience', 'honors',
|
||||
'license_no', 'enable_image_consult', 'enable_video_consult', 'enable_charge',
|
||||
'work_wechat_userid'
|
||||
])->findOrEmpty($params['id'])->toArray();
|
||||
|
||||
|
||||
@@ -1437,6 +1437,75 @@ class DiagnosisLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 新建就诊卡(供小程序调用)
|
||||
* patient_id 可选:有则复用(同患者多张卡),无则自动生成(新患者首张卡)
|
||||
* @param array $params
|
||||
* @return array|bool 成功返回 ['id'=>诊单ID, 'patient_id'=>患者ID],失败返回false
|
||||
*/
|
||||
public static function addCard(array $params)
|
||||
{
|
||||
try {
|
||||
$userId = $params['user_id'] ?? 0;
|
||||
unset($params['user_id']); // 诊单表无此字段,仅用于创建 view_record
|
||||
|
||||
$patientId = $params['patient_id'] ?? 0;
|
||||
if (!$patientId) {
|
||||
$params['patient_id'] = self::generatePatientId();
|
||||
}
|
||||
$params['status'] = 1;
|
||||
|
||||
// 处理既往史数组
|
||||
if (isset($params['past_history']) && is_array($params['past_history'])) {
|
||||
$params['past_history'] = implode(',', $params['past_history']);
|
||||
}
|
||||
$multiSelectFields = [
|
||||
'diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition',
|
||||
'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition',
|
||||
'stool_condition', 'kidney_condition', 'local_hospital_diagnosis'
|
||||
];
|
||||
foreach ($multiSelectFields as $field) {
|
||||
if (isset($params[$field]) && is_array($params[$field])) {
|
||||
$params[$field] = implode(',', $params[$field]);
|
||||
}
|
||||
}
|
||||
if (isset($params['tongue_images']) && is_array($params['tongue_images'])) {
|
||||
$params['tongue_images'] = json_encode($params['tongue_images'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
if (isset($params['report_files']) && is_array($params['report_files'])) {
|
||||
$params['report_files'] = json_encode($params['report_files'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
if (isset($params['diagnosis_date'])) {
|
||||
$params['diagnosis_date'] = is_numeric($params['diagnosis_date']) ? $params['diagnosis_date'] : strtotime($params['diagnosis_date']);
|
||||
}
|
||||
|
||||
$model = Diagnosis::create($params);
|
||||
self::createPatientTrtcAccount($model->patient_id);
|
||||
|
||||
// 关联 diagnosis_view_records,便于用户通过 User::with('diagnosis') 获取就诊卡
|
||||
if ($userId) {
|
||||
$now = time();
|
||||
\app\common\model\DiagnosisViewRecord::create([
|
||||
'user_id' => $userId,
|
||||
'diagnosis_id' => $model->id,
|
||||
'patient_id' => $model->patient_id,
|
||||
'share_user_id' => 0,
|
||||
'view_count' => 1,
|
||||
'first_view_time' => $now,
|
||||
'last_view_time' => $now,
|
||||
'is_confirmed' => 0,
|
||||
'create_time' => $now,
|
||||
'update_time' => $now
|
||||
]);
|
||||
}
|
||||
|
||||
return ['id' => $model->id, 'patient_id' => $model->patient_id];
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 更新就诊卡(供小程序调用)
|
||||
* @param array $params
|
||||
|
||||
@@ -11,7 +11,7 @@ use app\api\logic\DoctorLogic;
|
||||
*/
|
||||
class DoctorController extends BaseApiController
|
||||
{
|
||||
public array $notNeedLogin = ['lists', 'detail'];
|
||||
public array $notNeedLogin = ['lists', 'detail', 'reviews'];
|
||||
|
||||
/**
|
||||
* @notes 获取医生列表
|
||||
@@ -70,4 +70,22 @@ class DoctorController extends BaseApiController
|
||||
$result = DoctorLogic::getDoctorRoster($doctorId, $date);
|
||||
return $this->success('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生评价列表
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function reviews()
|
||||
{
|
||||
$doctorId = $this->request->get('doctor_id', 0);
|
||||
$pageNo = $this->request->get('page_no', 1);
|
||||
$pageSize = $this->request->get('page_size', 10);
|
||||
|
||||
if (!$doctorId) {
|
||||
return $this->fail('医生ID不能为空');
|
||||
}
|
||||
|
||||
$result = DoctorLogic::getDoctorReviews($doctorId, $pageNo, $pageSize);
|
||||
return $this->success('', $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,74 @@ class TcmController extends BaseApiController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 新建就诊卡(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function addCard()
|
||||
{
|
||||
$params = [
|
||||
'user_id' => $this->userId,
|
||||
'patient_id' => $this->request->post('patient_id'),
|
||||
'patient_name' => $this->request->post('patient_name'),
|
||||
'gender' => $this->request->post('gender'),
|
||||
'age' => $this->request->post('age'),
|
||||
'id_card' => $this->request->post('id_card'),
|
||||
'height' => $this->request->post('height'),
|
||||
'weight' => $this->request->post('weight'),
|
||||
'systolic_pressure' => $this->request->post('systolic_pressure'),
|
||||
'diastolic_pressure' => $this->request->post('diastolic_pressure'),
|
||||
'fasting_blood_sugar' => $this->request->post('fasting_blood_sugar'),
|
||||
'diabetes_discovery_year' => $this->request->post('diabetes_discovery_year'),
|
||||
'local_hospital_diagnosis' => $this->request->post('local_hospital_diagnosis'),
|
||||
'local_hospital_name' => $this->request->post('local_hospital_name'),
|
||||
'local_hospital_visit_date' => $this->request->post('local_hospital_visit_date'),
|
||||
'diagnosis_date' => $this->request->post('diagnosis_date'),
|
||||
'diagnosis_type' => $this->request->post('diagnosis_type'),
|
||||
'syndrome_type' => $this->request->post('syndrome_type'),
|
||||
'diabetes_type' => $this->request->post('diabetes_type'),
|
||||
'appetite' => $this->request->post('appetite'),
|
||||
'water_intake' => $this->request->post('water_intake'),
|
||||
'diet_condition' => $this->request->post('diet_condition'),
|
||||
'weight_change' => $this->request->post('weight_change'),
|
||||
'body_feeling' => $this->request->post('body_feeling'),
|
||||
'sleep_condition' => $this->request->post('sleep_condition'),
|
||||
'eye_condition' => $this->request->post('eye_condition'),
|
||||
'head_feeling' => $this->request->post('head_feeling'),
|
||||
'sweat_condition' => $this->request->post('sweat_condition'),
|
||||
'skin_condition' => $this->request->post('skin_condition'),
|
||||
'urine_condition' => $this->request->post('urine_condition'),
|
||||
'stool_condition' => $this->request->post('stool_condition'),
|
||||
'kidney_condition' => $this->request->post('kidney_condition'),
|
||||
'fatty_liver_degree' => $this->request->post('fatty_liver_degree'),
|
||||
'past_history' => $this->request->post('past_history'),
|
||||
'trauma_history' => $this->request->post('trauma_history'),
|
||||
'surgery_history' => $this->request->post('surgery_history'),
|
||||
'allergy_history' => $this->request->post('allergy_history'),
|
||||
'family_history' => $this->request->post('family_history'),
|
||||
'pregnancy_history' => $this->request->post('pregnancy_history'),
|
||||
'symptoms' => $this->request->post('symptoms'),
|
||||
'tongue_coating' => $this->request->post('tongue_coating'),
|
||||
'tongue_images' => $this->request->post('tongue_images'),
|
||||
'report_files' => $this->request->post('report_files'),
|
||||
'pulse' => $this->request->post('pulse'),
|
||||
'treatment_principle' => $this->request->post('treatment_principle'),
|
||||
'prescription' => $this->request->post('prescription'),
|
||||
'doctor_advice' => $this->request->post('doctor_advice'),
|
||||
'remark' => $this->request->post('remark')
|
||||
];
|
||||
// patient_id 可选:不传则创建时自动生成(新患者首张就诊卡)
|
||||
try {
|
||||
$result = DiagnosisLogic::addCard($params);
|
||||
if ($result === false) {
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
return $this->success('创建成功', $result);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 更新就诊卡(供小程序调用)
|
||||
* @return \think\response\Json
|
||||
|
||||
@@ -6,6 +6,7 @@ use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\model\doctor\Roster;
|
||||
use app\common\model\doctor\Appointment;
|
||||
|
||||
/**
|
||||
* 医生逻辑层
|
||||
@@ -86,7 +87,7 @@ class DoctorLogic extends BaseLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生详情
|
||||
* @notes 获取医生详情(含完整档案信息)
|
||||
* @param int $doctorId
|
||||
* @return array|null
|
||||
*/
|
||||
@@ -95,7 +96,7 @@ class DoctorLogic extends BaseLogic
|
||||
// 检查该医生是否有医生角色(role_id = 1)
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
->count() > 0;
|
||||
|
||||
if (!$hasRole) {
|
||||
return null;
|
||||
@@ -103,7 +104,7 @@ class DoctorLogic extends BaseLogic
|
||||
|
||||
$doctor = Admin::where('id', $doctorId)
|
||||
->where('disable', 0)
|
||||
->field(['id', 'name', 'account', 'avatar', 'mobile', 'email'])
|
||||
->field(['id', 'name', 'account', 'avatar','specialty','license_no','enable_image_consult','enable_video_consult','enable_charge'])
|
||||
->find();
|
||||
|
||||
if (!$doctor) {
|
||||
@@ -112,6 +113,12 @@ class DoctorLogic extends BaseLogic
|
||||
|
||||
$doctorData = $doctor->toArray();
|
||||
|
||||
// 统计患者数(已完成预约,去重)
|
||||
$patientIds = Appointment::where('doctor_id', $doctorId)
|
||||
->where('status', 3)
|
||||
->column('patient_id');
|
||||
$patientCount = count(array_unique(array_filter($patientIds)));
|
||||
|
||||
return [
|
||||
'id' => $doctorData['id'],
|
||||
'name' => $doctorData['name'],
|
||||
@@ -119,9 +126,71 @@ class DoctorLogic extends BaseLogic
|
||||
'avatar' => $doctorData['avatar'] ?? '',
|
||||
'mobile' => $doctorData['mobile'] ?? '',
|
||||
'email' => $doctorData['email'] ?? '',
|
||||
'specialty' => '医生',
|
||||
'title' => '医生',
|
||||
'rating' => '4.8',
|
||||
'license_no' => $doctorData['license_no'] ?? '',
|
||||
'enable_image_consult' => $doctorData['enable_image_consult'] ?? 1,
|
||||
'enable_video_consult' => $doctorData['enable_video_consult'] ?? 1,
|
||||
'enable_charge' => $doctorData['enable_charge'] ?? 0,
|
||||
'specialty' => '中医',
|
||||
'title' => '主任医师',
|
||||
'hospital' => '甄养堂互联网医院',
|
||||
'experience' => '24年临床经验',
|
||||
'rating' => '4.9',
|
||||
'patient_count' => $patientCount,
|
||||
'papers' => 15,
|
||||
'about' => $doctorData['specialty'] ?? '擅长运用传统中医理论与现代诊断相结合,专注于慢性炎症及呼吸系统健康的调理。精通草本方剂,致力于为患者提供个性化的整体康复方案。',
|
||||
'expertise' => [
|
||||
['icon' => 'psychiatry', 'title' => '草本药方', 'desc' => '为整体康复提供量身定制的草本处方。'],
|
||||
['icon' => 'eco', 'title' => '草本药方', 'desc' => '为整体康复提供量身定制的草本处方。'],
|
||||
['icon' => 'vital_signs', 'title' => '切脉诊断', 'desc' => '运用传统方法对全身平衡和器官健康进行深度触诊评估。', 'wide' => true],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取医生评价列表
|
||||
* @param int $doctorId
|
||||
* @param int $pageNo
|
||||
* @param int $pageSize
|
||||
* @return array
|
||||
*/
|
||||
public static function getDoctorReviews($doctorId, $pageNo = 1, $pageSize = 10)
|
||||
{
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->count() > 0;
|
||||
|
||||
if (!$hasRole) {
|
||||
return ['lists' => [], 'count' => 0];
|
||||
}
|
||||
|
||||
// 默认评价数据(后续可扩展为独立评价表)
|
||||
$defaultReviews = [
|
||||
[
|
||||
'id' => 1,
|
||||
'patient_initials' => 'JS',
|
||||
'patient_name' => 'James S.',
|
||||
'rating' => 5,
|
||||
'content' => '李医生对我慢性疲劳的治疗改变了我的生活。他在建议方剂之前足足听我倾诉了45分钟。真是一位大师级的人物。',
|
||||
'create_time' => '2天前',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'patient_initials' => 'ML',
|
||||
'patient_name' => 'Mei Ling',
|
||||
'rating' => 5,
|
||||
'content' => '非常专业,知识渊博。针灸疗程显著缓解了我的偏头痛。非常推荐这家医院。',
|
||||
'create_time' => '1周前',
|
||||
],
|
||||
];
|
||||
|
||||
$offset = ($pageNo - 1) * $pageSize;
|
||||
$lists = array_slice($defaultReviews, $offset, $pageSize);
|
||||
|
||||
return [
|
||||
'lists' => $lists,
|
||||
'count' => count($defaultReviews),
|
||||
'page_no' => $pageNo,
|
||||
'page_size' => $pageSize,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -136,7 +205,7 @@ class DoctorLogic extends BaseLogic
|
||||
// 检查该医生是否有医生角色
|
||||
$hasRole = AdminRole::where('admin_id', $doctorId)
|
||||
->where('role_id', 1)
|
||||
->exists();
|
||||
->count() > 0;
|
||||
|
||||
if (!$hasRole) {
|
||||
return [];
|
||||
|
||||
@@ -70,6 +70,7 @@ class UserLogic extends BaseLogic
|
||||
*/
|
||||
public static function info(int $userId)
|
||||
{
|
||||
|
||||
$user = User::where(['id' => $userId])
|
||||
->with('diagnosis')
|
||||
->field('id,sn,sex,account,password,nickname,real_name,avatar,mobile,age,create_time,user_money')
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 医生表(la_admin)新增字段:执业证书编号、图文问诊、视频问诊、收费开关
|
||||
-- 若项目使用 zyt_ 前缀,请将 la_admin 替换为 zyt_admin 后执行
|
||||
|
||||
ALTER TABLE `la_admin` ADD COLUMN `license_no` varchar(100) NULL DEFAULT '' COMMENT '执业证书编号';
|
||||
ALTER TABLE `la_admin` ADD COLUMN `enable_image_consult` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否开启图文问诊:0-关闭 1-开启';
|
||||
ALTER TABLE `la_admin` ADD COLUMN `enable_video_consult` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否开启视频问诊:0-关闭 1-开启';
|
||||
ALTER TABLE `la_admin` ADD COLUMN `enable_charge` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否开启收费:0-关闭 1-开启';
|
||||