456 lines
9.4 KiB
Vue
456 lines
9.4 KiB
Vue
<template>
|
||
<view class="card-container">
|
||
<!-- 顶部标题栏 -->
|
||
|
||
|
||
<!-- 就诊卡列表 -->
|
||
<view class="card-list">
|
||
<view
|
||
v-for="card in cardList"
|
||
:key="card.id"
|
||
class="card-item"
|
||
@click="viewCardDetail(card)"
|
||
>
|
||
<!-- 卡片头部 -->
|
||
<view class="card-header">
|
||
<view class="card-id">
|
||
<text class="label">诊单编号:</text>
|
||
<text class="value">{{ card.id }}</text>
|
||
</view>
|
||
<view :class="['status-badge', getStatusClass(card.status)]">
|
||
{{ card.status_desc || getStatusText(card.status) }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 患者信息 -->
|
||
<view class="card-info">
|
||
<view class="info-row">
|
||
<text class="info-label">患者姓名:</text>
|
||
<text class="info-value">{{ card.patient_name || '-' }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">性别年龄:</text>
|
||
<text class="info-value">{{ card.gender_desc || '-' }} / {{ card.age }}岁</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">诊断类型:</text>
|
||
<text class="info-value">{{ getDiagnosisTypeName(card.diagnosis_type) }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">证型:</text>
|
||
<text class="info-value">{{ getSyndromeTypeName(card.syndrome_type) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 时间信息 -->
|
||
<view class="card-footer">
|
||
<view class="time-info">
|
||
<text class="time-label">诊断日期:</text>
|
||
<text class="time-value">{{ card.diagnosis_date_text || formatDate(card.diagnosis_date) }}</text>
|
||
</view>
|
||
<view class="time-info">
|
||
<text class="time-label">创建时间:</text>
|
||
<text class="time-value">{{ card.create_time || '-' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 快捷入口 -->
|
||
<view class="card-actions">
|
||
<view class="card-action-btn primary" @click.stop="viewDailyRecord(card)">
|
||
<text class="card-action-icon">📈</text>
|
||
<text class="card-action-text">日常记录</text>
|
||
</view>
|
||
<view class="card-action-btn" @click.stop="viewCardDetail(card)">
|
||
<text class="card-action-icon">📝</text>
|
||
<text class="card-action-text">查看 / 编辑</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-if="!loading && cardList.length === 0" class="empty-state">
|
||
<uni-icons type="wallet" size="80" color="#1890ff"></uni-icons>
|
||
<text class="empty-text">暂无就诊卡</text>
|
||
<view class="add-card-btn" @click="createCard">新建就诊卡</view>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view v-if="loading" class="loading-state">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, getCurrentInstance } from 'vue'
|
||
import { onLaunch, onShow, onHide, onError ,onShareAppMessage} from '@dcloudio/uni-app'
|
||
const { proxy } = getCurrentInstance()
|
||
|
||
// 数据
|
||
const cardList = ref([])
|
||
const loading = ref(false)
|
||
|
||
// 页面加载时获取数据
|
||
onMounted(() => {
|
||
loadCardList()
|
||
})
|
||
onShow(() => {
|
||
loadCardList()
|
||
})
|
||
// 加载就诊卡列表
|
||
const loadCardList = async () => {
|
||
loading.value = true
|
||
try {
|
||
// 检查token是否存在
|
||
const token = uni.getStorageSync('token')
|
||
if (!token) {
|
||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||
// setTimeout(() => {
|
||
// uni.redirectTo({ url: '/pages/login/login' })
|
||
// }, 1500)
|
||
return
|
||
}
|
||
|
||
// 获取当前用户的patient_id
|
||
const userData = uni.getStorageSync('userData')
|
||
const patientId = userData?.diagnosis?.patient_id
|
||
|
||
if (!patientId) {
|
||
//uni.showToast({ title: '患者信息不存在', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 调用后端接口获取就诊卡列表
|
||
const res = await proxy.apiUrl({
|
||
url: '/api/tcm/getCardList',
|
||
method: 'GET',
|
||
data: {
|
||
patient_id: patientId
|
||
}
|
||
})
|
||
|
||
if (res.code === 1) {
|
||
cardList.value = res.data || []
|
||
} else {
|
||
uni.showToast({ title: res.msg || '获取失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
console.error('加载就诊卡列表失败:', err)
|
||
//uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 查看卡片详情
|
||
const viewCardDetail = (card) => {
|
||
uni.navigateTo({
|
||
url: `/pages/Card/edit_card?id=${card.id}`
|
||
})
|
||
}
|
||
|
||
// 查看日常记录(血糖血压 / 饮食 / 运动 / 跟踪备注 + 波浪图)
|
||
const viewDailyRecord = (card) => {
|
||
const url = `/tongji/pages/index?diagnosis_id=${card.id}` +
|
||
`&patient_id=${card.patient_id || ''}` +
|
||
`&patient_name=${encodeURIComponent(card.patient_name || '')}` +
|
||
`&age=${card.age || ''}` +
|
||
`&gender=${card.gender || ''}`
|
||
uni.navigateTo({ url })
|
||
}
|
||
|
||
// 新建就诊卡(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 ? '启用' : '禁用'
|
||
}
|
||
|
||
// 获取状态样式类
|
||
const getStatusClass = (status) => {
|
||
return status === 1 ? 'status-confirmed' : 'status-pending'
|
||
}
|
||
|
||
// 诊断类型字典
|
||
const diagnosisTypeMap = {
|
||
'first_visit': '初诊',
|
||
'follow_up': '复诊',
|
||
'consultation': '会诊'
|
||
}
|
||
|
||
// 证型字典
|
||
const syndromeTypeMap = {
|
||
'qi_deficiency': '气虚',
|
||
'blood_deficiency': '血虚',
|
||
'yin_deficiency': '阴虚',
|
||
'yang_deficiency': '阳虚',
|
||
'qi_stagnation': '气滞',
|
||
'blood_stasis': '血瘀',
|
||
'phlegm_dampness': '痰湿',
|
||
'damp_heat': '湿热',
|
||
'cold_dampness': '寒湿',
|
||
'wind_cold': '风寒',
|
||
'wind_heat': '风热'
|
||
}
|
||
|
||
// 获取诊断类型名称
|
||
const getDiagnosisTypeName = (type) => {
|
||
return diagnosisTypeMap[type] || type || '-'
|
||
}
|
||
|
||
// 获取证型名称
|
||
const getSyndromeTypeName = (type) => {
|
||
return syndromeTypeMap[type] || type || '-'
|
||
}
|
||
|
||
// 格式化日期(时间戳转日期)
|
||
const formatDate = (timestamp) => {
|
||
if (!timestamp) return '-'
|
||
const date = new Date(timestamp * 1000)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
/* 适老化设计:50-80岁 - 大字号、高对比、大触控区 */
|
||
.card-container {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #e8f0fa 0%, #ffffff 100%);
|
||
padding-bottom: 56rpx;
|
||
}
|
||
|
||
.header {
|
||
background: #ffffff;
|
||
padding: 48rpx 40rpx;
|
||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||
|
||
.header-title {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #1890ff;
|
||
}
|
||
}
|
||
|
||
.card-list {
|
||
padding: 40rpx;
|
||
}
|
||
|
||
.card-item {
|
||
background: #ffffff;
|
||
border-radius: 28rpx;
|
||
padding: 44rpx;
|
||
margin-bottom: 36rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s;
|
||
min-height: 200rpx;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
padding-bottom: 28rpx;
|
||
border-bottom: 4rpx solid #e8eaed;
|
||
}
|
||
|
||
.card-id {
|
||
.label {
|
||
font-size: 34rpx;
|
||
color: #555;
|
||
}
|
||
|
||
.value {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #1a1a1a;
|
||
}
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 12rpx 28rpx;
|
||
border-radius: 28rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
|
||
&.status-confirmed {
|
||
background: #f6ffed;
|
||
color: #204e2b;
|
||
border: 4rpx solid #204e2b;
|
||
}
|
||
|
||
&.status-pending {
|
||
background: #fff7e6;
|
||
color: #fa8c16;
|
||
border: 4rpx solid #ffd591;
|
||
}
|
||
}
|
||
|
||
.card-info {
|
||
margin-bottom: 28rpx;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
|
||
.info-label {
|
||
font-size: 34rpx;
|
||
color: #555;
|
||
min-width: 200rpx;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 36rpx;
|
||
color: #1a1a1a;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.card-footer {
|
||
padding-top: 28rpx;
|
||
border-top: 4rpx solid #e8eaed;
|
||
}
|
||
|
||
.card-actions {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-top: 28rpx;
|
||
padding-top: 28rpx;
|
||
border-top: 4rpx solid #e8eaed;
|
||
}
|
||
|
||
.card-action-btn {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 12rpx;
|
||
padding: 24rpx 12rpx;
|
||
background: #f6faff;
|
||
border-radius: 20rpx;
|
||
border: 2rpx solid #d6e6fb;
|
||
min-height: 80rpx;
|
||
transition: all 0.2s;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.85;
|
||
}
|
||
|
||
&.primary {
|
||
background: linear-gradient(135deg, #1890ff, #0ea5a4);
|
||
border-color: transparent;
|
||
|
||
.card-action-icon,
|
||
.card-action-text {
|
||
color: #ffffff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-action-icon {
|
||
font-size: 36rpx;
|
||
color: #1890ff;
|
||
}
|
||
|
||
.card-action-text {
|
||
font-size: 30rpx;
|
||
color: #1890ff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.time-info {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.time-label {
|
||
font-size: 30rpx;
|
||
color: #555;
|
||
min-width: 180rpx;
|
||
}
|
||
|
||
.time-value {
|
||
font-size: 32rpx;
|
||
color: #555;
|
||
}
|
||
}
|
||
|
||
.view-count {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 12rpx;
|
||
|
||
.count-icon {
|
||
font-size: 34rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.count-text {
|
||
font-size: 30rpx;
|
||
color: #555;
|
||
}
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 160rpx 0;
|
||
|
||
.empty-icon {
|
||
font-size: 140rpx;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 40rpx;
|
||
color: #555;
|
||
margin-bottom: 56rpx;
|
||
}
|
||
}
|
||
|
||
.add-card-btn {
|
||
padding: 36rpx 88rpx;
|
||
background: #1890ff;
|
||
color: #ffffff;
|
||
font-size: 40rpx;
|
||
font-weight: 500;
|
||
border-radius: 56rpx;
|
||
min-height: 100rpx;
|
||
}
|
||
|
||
.loading-state {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 120rpx 0;
|
||
|
||
.loading-text {
|
||
font-size: 36rpx;
|
||
color: #555;
|
||
}
|
||
}
|
||
</style>
|