新增
This commit is contained in:
@@ -117,7 +117,7 @@
|
||||
</div> -->
|
||||
<div class="patient-info">
|
||||
<div class="patient-name">{{ row.patient_name }}</div>
|
||||
<div class="patient-phone">{{ row.patient_phone }}</div>
|
||||
<div class="patient-phone">{{ maskPhone(row.patient_phone) }}</div>
|
||||
<div class="patient-extra">
|
||||
{{ [row.gender === 1 ? '男' : row.gender === 0 ? '女' : '', row.age != null ? row.age + '岁' : '', row.height != null ? row.height + 'cm' : '', row.weight != null ? row.weight + 'kg' : ''].filter(Boolean).join(' ') || '-' }}
|
||||
</div>
|
||||
@@ -404,9 +404,9 @@
|
||||
病史信息
|
||||
</div>
|
||||
<div class="detail-grid">
|
||||
<div v-if="detailData.diabetes_discovery_year != null" class="detail-item">
|
||||
<div v-if="isDiabetesDiscoveryFilled(detailData.diabetes_discovery_year)" class="detail-item">
|
||||
<span class="detail-label">发现糖尿病病史</span>
|
||||
<span class="detail-value">{{ detailData.diabetes_discovery_year }}年</span>
|
||||
<span class="detail-value">{{ formatDiabetesDiscoveryDisplay(detailData.diabetes_discovery_year) }}</span>
|
||||
</div>
|
||||
<div v-if="detailData.local_hospital_name" class="detail-item">
|
||||
<span class="detail-label">当地就诊医院</span>
|
||||
@@ -513,6 +513,10 @@ import { appointmentLists, cancelAppointment, completeAppointment, appointmentDe
|
||||
import { getCallSignature, generateMiniProgramQrcode, tcmDiagnosisDetail, prescriptionGetByAppointment } from '@/api/tcm'
|
||||
import { getDictData } from '@/api/app'
|
||||
import feedback from '@/utils/feedback'
|
||||
import {
|
||||
formatDiabetesDiscoveryDisplay,
|
||||
isDiabetesDiscoveryFilled
|
||||
} from '@/utils/diabetes-discovery-display'
|
||||
|
||||
// 重组件异步加载,避免与列表同 chunk 阻塞路由进入与首帧渲染
|
||||
const EditPopup = defineAsyncComponent(() => import('../diagnosis/edit.vue'))
|
||||
@@ -540,6 +544,12 @@ import {
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
|
||||
// 手机号脱敏处理
|
||||
const maskPhone = (phone: string) => {
|
||||
if (!phone || phone.length < 11) return phone
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
}
|
||||
|
||||
// 判断是否为管理员(非医生、非医助)
|
||||
const isAdmin = computed(() => {
|
||||
const roleId = userInfo.value.role_id
|
||||
|
||||
@@ -134,12 +134,10 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="空腹血糖(mmol/L)" prop="fasting_blood_sugar">
|
||||
<el-input-number
|
||||
<el-input
|
||||
v-model="formData.fasting_blood_sugar"
|
||||
:min="0"
|
||||
:max="50"
|
||||
:step="0.1"
|
||||
placeholder="请输入空腹血糖"
|
||||
placeholder="请输入空腹血糖,如:8.5 或 8-9"
|
||||
maxlength="20"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -206,14 +204,14 @@
|
||||
<div class="section-title">主诉</div>
|
||||
|
||||
<el-form-item label="发现糖尿病就患病史" prop="diabetes_discovery_year">
|
||||
<el-input-number
|
||||
<el-input
|
||||
v-model="formData.diabetes_discovery_year"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="请输入年数"
|
||||
clearable
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
placeholder="可填数字或文字,如:5、1年、半年"
|
||||
class="w-full"
|
||||
/>
|
||||
<template #append>年</template>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="当地医院诊断结果">
|
||||
@@ -521,7 +519,7 @@ const formData = ref({
|
||||
diagnosis_type: '',
|
||||
syndrome_type: '',
|
||||
diabetes_type: '',
|
||||
diabetes_discovery_year: undefined as number | undefined,
|
||||
diabetes_discovery_year: '',
|
||||
local_hospital_diagnosis: [],
|
||||
local_hospital_name: '',
|
||||
marital_status: undefined as number | undefined,
|
||||
@@ -530,7 +528,7 @@ const formData = ref({
|
||||
region: '',
|
||||
systolic_pressure: undefined as number | undefined,
|
||||
diastolic_pressure: undefined as number | undefined,
|
||||
fasting_blood_sugar: undefined as number | undefined,
|
||||
fasting_blood_sugar: '',
|
||||
appetite: '',
|
||||
water_intake: '',
|
||||
diet_condition: [],
|
||||
@@ -592,7 +590,8 @@ const formRules = {
|
||||
age: [{ required: true, message: '请输入年龄', trigger: 'blur' }],
|
||||
diagnosis_type: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
|
||||
syndrome_type: [{ required: true, message: '请选择证型', trigger: 'change' }],
|
||||
diabetes_type: [{ required: true, message: '请选择糖尿病期数', trigger: 'change' }]
|
||||
diabetes_type: [{ required: true, message: '请选择糖尿病期数', trigger: 'change' }],
|
||||
diabetes_discovery_year: [{ max: 50, message: '最多50个字符', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const diagnosisTypeOptions = ref<any[]>([])
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
v-model="formData.id_card"
|
||||
placeholder="请输入身份证号"
|
||||
maxlength="18"
|
||||
@focus="handleIdCardFocus"
|
||||
@blur="handleIdCardBlur"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -53,6 +54,7 @@
|
||||
v-model="formData.phone"
|
||||
placeholder="请输入手机号"
|
||||
maxlength="11"
|
||||
@focus="handlePhoneFocus"
|
||||
@blur="handlePhoneBlur"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -163,12 +165,10 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="空腹血糖(mmol/L)" prop="fasting_blood_sugar">
|
||||
<el-input-number
|
||||
<el-input
|
||||
v-model="formData.fasting_blood_sugar"
|
||||
:min="0"
|
||||
:max="50"
|
||||
:step="0.1"
|
||||
placeholder="请输入空腹血糖"
|
||||
placeholder="请输入空腹血糖,如:8.5 或 8-9"
|
||||
maxlength="20"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -239,14 +239,14 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发现糖尿病患病史" prop="diabetes_discovery_year">
|
||||
<el-input-number
|
||||
<el-input
|
||||
v-model="formData.diabetes_discovery_year"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="请输入年数"
|
||||
clearable
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
placeholder="可填数字或文字,如:5、1年、半年"
|
||||
class="w-full"
|
||||
/>
|
||||
<template #append>年</template>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -511,7 +511,7 @@
|
||||
<el-empty v-else description="请先保存诊单后再添加血糖血压记录" />
|
||||
</el-tab-pane>
|
||||
<!-- 病历记录标签页(开方后自动显示) -->
|
||||
<el-tab-pane label="处方" name="bingli" :disabled="!formData.id">
|
||||
<el-tab-pane label="处方" name="bingli" :disabled="!formData.id" v-if="hasPermission(['tcm.diagnosis/chufang'])">
|
||||
<div v-if="formData.id" class="bingli-tab-content">
|
||||
<!-- 诊断信息:舌苔照片、检查报告 -->
|
||||
<div class="bingli-diagnosis-section">
|
||||
@@ -720,7 +720,7 @@ const formData = ref({
|
||||
diagnosis_type: '',
|
||||
syndrome_type: '',
|
||||
diabetes_type: '',
|
||||
diabetes_discovery_year: undefined as number | undefined,
|
||||
diabetes_discovery_year: '',
|
||||
local_hospital_diagnosis: [],
|
||||
local_hospital_name: '',
|
||||
marital_status: undefined as number | undefined,
|
||||
@@ -729,7 +729,7 @@ const formData = ref({
|
||||
region: '',
|
||||
systolic_pressure: undefined as number | undefined,
|
||||
diastolic_pressure: undefined as number | undefined,
|
||||
fasting_blood_sugar: undefined as number | undefined,
|
||||
fasting_blood_sugar: '',
|
||||
appetite: [] as string[],
|
||||
water_intake: '',
|
||||
diet_condition: [],
|
||||
@@ -750,6 +750,7 @@ const formData = ref({
|
||||
allergy_history: 0,
|
||||
family_history: 0,
|
||||
pregnancy_history: 0,
|
||||
assistant_id: undefined as number | undefined,
|
||||
tongue_images: [] as string[],
|
||||
report_files: [],
|
||||
symptoms: '',
|
||||
@@ -764,13 +765,124 @@ const formData = ref({
|
||||
external_userid: ''
|
||||
})
|
||||
|
||||
// 保存原始完整数据
|
||||
const originalPhone = ref('')
|
||||
const originalIdCard = ref('')
|
||||
const isPhoneFocused = ref(false)
|
||||
const isIdCardFocused = ref(false)
|
||||
|
||||
// 脱敏函数
|
||||
const maskPhone = (phone: string) => {
|
||||
if (!phone || phone.length < 11) return phone
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
}
|
||||
|
||||
const maskIdCard = (idCard: string) => {
|
||||
if (!idCard) return idCard
|
||||
if (idCard.length === 15) {
|
||||
return idCard.replace(/(\d{6})\d{5}(\d{4})/, '$1*****$2')
|
||||
} else if (idCard.length === 18) {
|
||||
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
|
||||
}
|
||||
return idCard
|
||||
}
|
||||
|
||||
// 手机号聚焦 - 显示完整数据
|
||||
const handlePhoneFocus = () => {
|
||||
isPhoneFocused.value = true
|
||||
if (originalPhone.value) {
|
||||
formData.value.phone = originalPhone.value
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号失焦 - 恢复脱敏并验证
|
||||
const handlePhoneBlur = async () => {
|
||||
isPhoneFocused.value = false
|
||||
|
||||
// 保存原始数据并脱敏显示
|
||||
if (formData.value.phone && formData.value.phone.length === 11) {
|
||||
originalPhone.value = formData.value.phone
|
||||
|
||||
// 验证手机号唯一性
|
||||
if (/^1[3-9]\d{9}$/.test(formData.value.phone)) {
|
||||
try {
|
||||
const result = await checkPhone({
|
||||
phone: formData.value.phone,
|
||||
id: formData.value.id || ''
|
||||
})
|
||||
|
||||
if (result.exists) {
|
||||
ElMessage.warning(result.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查手机号失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 脱敏显示
|
||||
formData.value.phone = maskPhone(formData.value.phone)
|
||||
}
|
||||
}
|
||||
|
||||
// 身份证聚焦 - 显示完整数据
|
||||
const handleIdCardFocus = () => {
|
||||
isIdCardFocused.value = true
|
||||
if (originalIdCard.value) {
|
||||
formData.value.id_card = originalIdCard.value
|
||||
}
|
||||
}
|
||||
|
||||
// 身份证失焦 - 恢复脱敏并验证
|
||||
const handleIdCardBlur = async () => {
|
||||
isIdCardFocused.value = false
|
||||
|
||||
// 保存原始数据并脱敏显示
|
||||
if (formData.value.id_card && (formData.value.id_card.length === 15 || formData.value.id_card.length === 18)) {
|
||||
originalIdCard.value = formData.value.id_card
|
||||
|
||||
// 验证身份证格式并计算年龄
|
||||
if (/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(formData.value.id_card)) {
|
||||
// 从身份证提取出生日期计算年龄
|
||||
const id = formData.value.id_card
|
||||
const birthYear = parseInt(id.substring(6, 10))
|
||||
const birthMonth = parseInt(id.substring(10, 12))
|
||||
const birthDay = parseInt(id.substring(12, 14))
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birthYear
|
||||
if (today.getMonth() + 1 < birthMonth || (today.getMonth() + 1 === birthMonth && today.getDate() < birthDay)) {
|
||||
age--
|
||||
}
|
||||
formData.value.age = age
|
||||
|
||||
// 验证身份证唯一性
|
||||
try {
|
||||
const result = await checkIdCard({
|
||||
id_card: formData.value.id_card,
|
||||
id: formData.value.id || ''
|
||||
})
|
||||
|
||||
if (result.exists) {
|
||||
ElMessage.warning(result.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查身份证号失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 脱敏显示
|
||||
formData.value.id_card = maskIdCard(formData.value.id_card)
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义验证规则
|
||||
const validatePhone = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
// 使用原始数据进行验证
|
||||
const phoneToValidate = originalPhone.value || value
|
||||
if (!phoneToValidate) {
|
||||
callback(new Error('请输入手机号'))
|
||||
return
|
||||
}
|
||||
if (!/^1[3-9]\d{9}$/.test(value)) {
|
||||
if (!/^1[3-9]\d{9}$/.test(phoneToValidate)) {
|
||||
callback(new Error('手机号格式不正确'))
|
||||
return
|
||||
}
|
||||
@@ -778,7 +890,9 @@ const validatePhone = (rule: any, value: any, callback: any) => {
|
||||
}
|
||||
|
||||
const validateIdCard = (rule: any, value: any, callback: any) => {
|
||||
if (value && !/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(value)) {
|
||||
// 使用原始数据进行验证
|
||||
const idCardToValidate = originalIdCard.value || value
|
||||
if (idCardToValidate && !/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(idCardToValidate)) {
|
||||
callback(new Error('身份证号格式不正确'))
|
||||
return
|
||||
}
|
||||
@@ -793,6 +907,7 @@ const formRules = {
|
||||
age: [{ required: true, message: '请输入年龄', trigger: 'blur' }],
|
||||
fasting_blood_sugar: [{ required: true, message: '请输入空腹血糖', trigger: 'blur' }],
|
||||
diagnosis_type: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
|
||||
diabetes_discovery_year: [{ max: 50, message: '最多50个字符', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
// 获取字典选项
|
||||
@@ -881,58 +996,6 @@ const getDictOptions = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号失焦检查
|
||||
const handlePhoneBlur = async () => {
|
||||
if (!formData.value.phone || !/^1[3-9]\d{9}$/.test(formData.value.phone)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await checkPhone({
|
||||
phone: formData.value.phone,
|
||||
id: formData.value.id || ''
|
||||
})
|
||||
|
||||
if (result.exists) {
|
||||
ElMessage.warning(result.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查手机号失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 身份证号失焦检查
|
||||
const handleIdCardBlur = async () => {
|
||||
if (!formData.value.id_card || !/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(formData.value.id_card)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 从身份证提取出生日期计算年龄
|
||||
const id = formData.value.id_card
|
||||
const birthYear = parseInt(id.substring(6, 10))
|
||||
const birthMonth = parseInt(id.substring(10, 12))
|
||||
const birthDay = parseInt(id.substring(12, 14))
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birthYear
|
||||
if (today.getMonth() + 1 < birthMonth || (today.getMonth() + 1 === birthMonth && today.getDate() < birthDay)) {
|
||||
age--
|
||||
}
|
||||
formData.value.age = age
|
||||
|
||||
try {
|
||||
const result = await checkIdCard({
|
||||
id_card: formData.value.id_card,
|
||||
id: formData.value.id || ''
|
||||
})
|
||||
|
||||
if (result.exists) {
|
||||
ElMessage.warning(result.message)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查身份证号失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const open = async (type: string, id?: number) => {
|
||||
mode.value = type
|
||||
visible.value = true
|
||||
@@ -943,7 +1006,19 @@ const open = async (type: string, id?: number) => {
|
||||
|
||||
if (type === 'edit' && id) {
|
||||
const data = await tcmDiagnosisDetail({ id })
|
||||
|
||||
// 保存原始完整数据
|
||||
originalPhone.value = data.phone || ''
|
||||
originalIdCard.value = data.id_card || ''
|
||||
|
||||
// 显示脱敏数据
|
||||
data.phone = maskPhone(data.phone || '')
|
||||
data.id_card = maskIdCard(data.id_card || '')
|
||||
|
||||
formData.value = data
|
||||
const y = data.diabetes_discovery_year
|
||||
formData.value.diabetes_discovery_year =
|
||||
y == null || y === '' ? '' : String(y)
|
||||
} else if (type === 'add') {
|
||||
const userStore = useUserStore()
|
||||
formData.value.assistant_id = userStore.userInfo?.id || ''
|
||||
@@ -955,8 +1030,15 @@ const handleSubmit = async () => {
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
// 准备提交数据,使用原始完整数据
|
||||
const submitData = {
|
||||
...formData.value,
|
||||
phone: originalPhone.value || formData.value.phone,
|
||||
id_card: originalIdCard.value || formData.value.id_card
|
||||
}
|
||||
|
||||
if (mode.value === 'add') {
|
||||
const result = await tcmDiagnosisAdd(formData.value)
|
||||
const result = await tcmDiagnosisAdd(submitData)
|
||||
|
||||
// 如果是新增,保存成功后切换到编辑模式,这样可以添加血糖血压记录
|
||||
if (result && result.id) {
|
||||
@@ -967,12 +1049,20 @@ const handleSubmit = async () => {
|
||||
try {
|
||||
const detail = await tcmDiagnosisDetail({ id: result.id })
|
||||
formData.value.patient_id = detail.patient_id
|
||||
|
||||
// 更新原始数据
|
||||
originalPhone.value = detail.phone || ''
|
||||
originalIdCard.value = detail.id_card || ''
|
||||
|
||||
// 显示脱敏数据
|
||||
formData.value.phone = maskPhone(detail.phone || '')
|
||||
formData.value.id_card = maskIdCard(detail.id_card || '')
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await tcmDiagnosisEdit(formData.value)
|
||||
await tcmDiagnosisEdit(submitData)
|
||||
}
|
||||
|
||||
emit('success')
|
||||
@@ -1011,17 +1101,21 @@ const handleClose = () => {
|
||||
id_card: '',
|
||||
phone: '',
|
||||
gender: 1,
|
||||
age: undefined as number | undefined,
|
||||
diagnosis_date: '',
|
||||
diagnosis_type: '',
|
||||
syndrome_type: '',
|
||||
diabetes_type: '',
|
||||
diabetes_discovery_year: '',
|
||||
local_hospital_diagnosis: [],
|
||||
local_hospital_name: '',
|
||||
marital_status: undefined as number | undefined,
|
||||
height: undefined as number | undefined,
|
||||
weight: undefined as number | undefined,
|
||||
region: '',
|
||||
systolic_pressure: undefined as number | undefined,
|
||||
diastolic_pressure: undefined as number | undefined,
|
||||
fasting_blood_sugar: undefined as number | undefined,
|
||||
diabetes_discovery_year: undefined as number | undefined,
|
||||
local_hospital_diagnosis: [],
|
||||
local_hospital_name: '',
|
||||
fasting_blood_sugar: '',
|
||||
appetite: [] as string[],
|
||||
water_intake: '',
|
||||
diet_condition: [],
|
||||
@@ -1042,6 +1136,7 @@ const handleClose = () => {
|
||||
allergy_history: 0,
|
||||
family_history: 0,
|
||||
pregnancy_history: 0,
|
||||
assistant_id: undefined as number | undefined,
|
||||
tongue_images: [] as string[],
|
||||
report_files: [],
|
||||
symptoms: '',
|
||||
@@ -1055,6 +1150,13 @@ const handleClose = () => {
|
||||
status: 1,
|
||||
external_userid: ''
|
||||
}
|
||||
|
||||
// 重置原始数据
|
||||
originalPhone.value = ''
|
||||
originalIdCard.value = ''
|
||||
isPhoneFocused.value = false
|
||||
isIdCardFocused.value = false
|
||||
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
|
||||
@@ -86,17 +86,31 @@
|
||||
stripe
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" />
|
||||
<el-table-column label="患者" min-width="150">
|
||||
<el-table-column label="ID" min-width="50">
|
||||
<template #default="{ row }">
|
||||
<div class="patient-cell">
|
||||
<!-- <div class="patient-avatar">{{ (row.patient_name || '患').charAt(0) }}</div> -->
|
||||
<div class="patient-info">
|
||||
<div class="patient-name">{{ row.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="患者" min-width="60">
|
||||
<template #default="{ row }">
|
||||
<div class="patient-cell">
|
||||
<!-- <div class="patient-avatar">{{ (row.patient_name || '患').charAt(0) }}</div> -->
|
||||
<div class="patient-info">
|
||||
<div class="patient-name">{{ row.patient_name }}</div>
|
||||
<div class="patient-meta">{{ row.id }} · {{ row.gender_desc || '-' }} · {{ row.age ?? '-' }}岁</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label=" 性别 / 年龄" width="100" align="left">
|
||||
<template #default="{ row }">
|
||||
<div >{{ row.gender_desc || '-' }} · {{ row.age ?? '-' }}岁</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="挂号" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
@@ -104,9 +118,23 @@
|
||||
:class="appointmentCellClasses(row)"
|
||||
>
|
||||
<template v-if="row.has_appointment">
|
||||
<div class="apt-badge">{{ appointmentStatusLabel(row) }}</div>
|
||||
<div class="apt-doctor">{{ row.appointment_doctor_name || '-' }}</div>
|
||||
<div class="apt-time">{{ row.appointment_time_text || '-' }}</div>
|
||||
<template v-if="appointmentRows(row).length">
|
||||
<div
|
||||
v-for="apt in appointmentRows(row)"
|
||||
:key="apt.id || `${apt.doctor_id}-${apt.time_text}`"
|
||||
class="apt-item"
|
||||
:class="appointmentItemClass(apt)"
|
||||
>
|
||||
<div class="apt-badge">{{ appointmentStatusLabelByStatus(apt.status) }}</div>
|
||||
<div class="apt-doctor">{{ apt.doctor_name || '-' }}</div>
|
||||
<div class="apt-time">{{ apt.time_text || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="apt-badge">{{ appointmentStatusLabel(row) }}</div>
|
||||
<div class="apt-doctor">{{ row.appointment_doctor_name || '-' }}</div>
|
||||
<div class="apt-time">{{ row.appointment_time_text || '-' }}</div>
|
||||
</template>
|
||||
</template>
|
||||
<span v-else class="apt-none">未挂号</span>
|
||||
</div>
|
||||
@@ -118,14 +146,22 @@
|
||||
<span v-else class="status-unconfirmed">未确认</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="诊断" min-width="130">
|
||||
<el-table-column label="复诊" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<div class="diagnosis-cell">
|
||||
<span class="diagnosis-date">{{ row.diagnosis_date_text || '-' }}</span>
|
||||
<template v-if="getDictLabel(diagnosisTypeOptions, row.diagnosis_type) || getDictLabel(syndromeTypeOptions, row.syndrome_type)">
|
||||
<span class="diagnosis-extra">{{ [getDictLabel(diagnosisTypeOptions, row.diagnosis_type), getDictLabel(syndromeTypeOptions, row.syndrome_type)].filter(Boolean).join(' · ') }}</span>
|
||||
</template>
|
||||
<div v-if="row.has_prescription" class="followup-cell">
|
||||
<div class="followup-time">{{ row.followup_time_text || '—' }}</div>
|
||||
<div class="followup-doctor">{{ row.followup_doctor_name || '—' }}</div>
|
||||
<el-tag
|
||||
v-if="row.followup_rx_voided"
|
||||
type="info"
|
||||
size="small"
|
||||
effect="plain"
|
||||
class="followup-void-tag"
|
||||
>
|
||||
处方已作废
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else class="followup-none">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="助理" width="72" show-overflow-tooltip>
|
||||
@@ -231,6 +267,7 @@
|
||||
<img :src="qrcodeUrl" alt="小程序二维码" class="w-64 h-64 border border-gray-200 rounded" />
|
||||
<div class="mt-4 text-sm text-gray-600">
|
||||
<div>患者:{{ currentQRCodePatient?.patient_name }}</div>
|
||||
<div class="mt-2">挂号时间:{{ qrcodeAppointmentTimeText }}</div>
|
||||
<div class="mt-2">请使用企业微信扫描二维码,然后转发给患者</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -306,7 +343,7 @@
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="患者">
|
||||
<span class="font-medium">{{ orderForm.patient_name }} ({{ orderForm.phone }})</span>
|
||||
<span class="font-medium">{{ orderForm.patient_name }} ({{ maskPhone(orderForm.phone) }})</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="订单类型" prop="order_type">
|
||||
@@ -386,7 +423,7 @@
|
||||
<div v-if="wechatContactInfo" class="mb-4">
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="患者姓名">{{ wechatCurrentPatient?.patient_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ wechatContactInfo.phone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ maskPhone(wechatContactInfo.phone) || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企微联系人ID">
|
||||
<span v-if="wechatContactInfo.external_userid">{{ wechatContactInfo.external_userid }}</span>
|
||||
<el-tag v-else type="info" size="small">未关联</el-tag>
|
||||
@@ -496,7 +533,7 @@ import { hasPermission } from '@/utils/perm'
|
||||
import { Loading, Warning, List, CircleCheck, Clock, ArrowDown, Picture, User, Document, Delete, CircleClose } from '@element-plus/icons-vue'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { defineAsyncComponent, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { computed, defineAsyncComponent, onMounted, onUnmounted, watch } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const EditPopup = defineAsyncComponent(() => import('./edit.vue'))
|
||||
@@ -512,6 +549,23 @@ const formatDateTime = (timestamp: number | string) => {
|
||||
return dayjs(ts).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
// 手机号脱敏
|
||||
const maskPhone = (phone: string) => {
|
||||
if (!phone || phone.length < 11) return phone
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
}
|
||||
|
||||
// 身份证号脱敏
|
||||
const maskIdCard = (idCard: string) => {
|
||||
if (!idCard) return idCard
|
||||
if (idCard.length === 15) {
|
||||
return idCard.replace(/(\d{6})\d{5}(\d{4})/, '$1*****$2')
|
||||
} else if (idCard.length === 18) {
|
||||
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
|
||||
}
|
||||
return idCard
|
||||
}
|
||||
|
||||
const formData = reactive({
|
||||
patient_name: '',
|
||||
diagnosis_type: '',
|
||||
@@ -832,7 +886,9 @@ const orderForm = reactive({
|
||||
phone: '',
|
||||
order_type: '' as string | number,
|
||||
amount: 0,
|
||||
remark: ''
|
||||
remark: '',
|
||||
has_appointment: false,
|
||||
appointment_time_text: ''
|
||||
})
|
||||
const orderRules = {
|
||||
order_type: [{ required: true, message: '请选择订单类型', trigger: 'change' }],
|
||||
@@ -846,6 +902,8 @@ const handleCreateOrder = (row: any) => {
|
||||
orderForm.order_type = ''
|
||||
orderForm.amount = 0
|
||||
orderForm.remark = ''
|
||||
orderForm.has_appointment = !!row.has_appointment
|
||||
orderForm.appointment_time_text = row.appointment_time_text || ''
|
||||
orderFormRef.value?.clearValidate()
|
||||
createOrderVisible.value = true
|
||||
}
|
||||
@@ -857,6 +915,8 @@ const resetOrderForm = () => {
|
||||
orderForm.order_type = ''
|
||||
orderForm.amount = 0
|
||||
orderForm.remark = ''
|
||||
orderForm.has_appointment = false
|
||||
orderForm.appointment_time_text = ''
|
||||
orderFormRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
@@ -878,7 +938,11 @@ const submitOrder = async () => {
|
||||
qrcodeDialogVisible.value = true
|
||||
qrcodeLoading.value = true
|
||||
qrcodeUrl.value = ''
|
||||
currentQRCodePatient.value = { patient_name: orderForm.patient_name }
|
||||
currentQRCodePatient.value = {
|
||||
patient_name: orderForm.patient_name,
|
||||
has_appointment: orderForm.has_appointment,
|
||||
appointment_time_text: orderForm.appointment_time_text
|
||||
}
|
||||
try {
|
||||
const qrRes = await generateOrderQrcode({ order_no: orderNo })
|
||||
if (qrRes?.qrcode_url) {
|
||||
@@ -904,6 +968,13 @@ const qrcodeDialogVisible = ref(false)
|
||||
const qrcodeLoading = ref(false)
|
||||
const qrcodeUrl = ref('')
|
||||
const currentQRCodePatient = ref<any>(null)
|
||||
/** 二维码弹窗展示的挂号时间(与列表「挂号」列一致) */
|
||||
const qrcodeAppointmentTimeText = computed(() => {
|
||||
const p = currentQRCodePatient.value
|
||||
if (!p) return '—'
|
||||
if (!p.has_appointment) return '未挂号'
|
||||
return p.appointment_time_text || '—'
|
||||
})
|
||||
const qrcodeDialogTitle = ref('小程序二维码')
|
||||
const lastQRCodeType = ref<'video' | 'confirm'>('confirm')
|
||||
|
||||
@@ -1021,6 +1092,35 @@ const appointmentStatusLabel = (row: any) => {
|
||||
return '已挂号'
|
||||
}
|
||||
|
||||
const appointmentStatusLabelByStatus = (status: number | string) => {
|
||||
const s = Number(status)
|
||||
if (s === 3) return '已完成'
|
||||
if (s === 4) return '已过号'
|
||||
return '已挂号'
|
||||
}
|
||||
|
||||
const appointmentRows = (row: any) => {
|
||||
const list = Array.isArray(row.appointments) ? row.appointments : []
|
||||
if (list.length > 0) return list
|
||||
if (!row.has_appointment) return []
|
||||
return [{
|
||||
id: row.appointment_id,
|
||||
status: row.appointment_status,
|
||||
doctor_id: row.appointment_doctor_id,
|
||||
doctor_name: row.appointment_doctor_name,
|
||||
time_text: row.appointment_time_text
|
||||
}]
|
||||
}
|
||||
|
||||
const appointmentItemClass = (apt: any) => {
|
||||
const s = Number(apt?.status)
|
||||
return {
|
||||
'apt-item-done': s === 3,
|
||||
'apt-item-missed': s === 4,
|
||||
'apt-item-active': s === 1
|
||||
}
|
||||
}
|
||||
|
||||
const appointmentCellClasses = (row: any) => {
|
||||
const s = Number(row.appointment_status)
|
||||
return {
|
||||
@@ -1540,21 +1640,28 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.patient-meta-col {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.appointment-cell {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
min-height: 36px;
|
||||
|
||||
&.has-apt {
|
||||
background: linear-gradient(135deg, rgba(var(--el-color-success-rgb), 0.12), rgba(var(--el-color-success-rgb), 0.06));
|
||||
border: 1px solid rgba(var(--el-color-success-rgb), 0.3);
|
||||
.apt-item + .apt-item {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.apt-item {
|
||||
.apt-badge {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--el-color-success-dark-2);
|
||||
background: var(--el-color-success-light-5);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2px;
|
||||
@@ -1568,37 +1675,56 @@ onUnmounted(() => {
|
||||
|
||||
.apt-time {
|
||||
font-size: 12px;
|
||||
color: var(--el-color-success);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
&.apt-item-active {
|
||||
.apt-badge {
|
||||
color: #3a4acc;
|
||||
background: rgba(74, 93, 255, 0.15);
|
||||
}
|
||||
|
||||
.apt-time {
|
||||
color: #4A5DFF;
|
||||
}
|
||||
}
|
||||
|
||||
&.apt-item-done {
|
||||
.apt-badge {
|
||||
color: var(--el-color-info-dark-2);
|
||||
background: var(--el-color-info-light-7);
|
||||
}
|
||||
|
||||
.apt-time {
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
}
|
||||
|
||||
&.apt-item-missed {
|
||||
.apt-badge {
|
||||
color: var(--el-color-warning-dark-2);
|
||||
background: var(--el-color-warning-light-7);
|
||||
}
|
||||
|
||||
.apt-time {
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.has-apt {
|
||||
background: linear-gradient(135deg, rgba(74, 93, 255, 0.12), rgba(74, 93, 255, 0.06));
|
||||
border: 1px solid rgba(74, 93, 255, 0.3);
|
||||
}
|
||||
|
||||
&.apt-row-missed {
|
||||
background: linear-gradient(135deg, rgba(var(--el-color-warning-rgb), 0.12), rgba(var(--el-color-warning-rgb), 0.05));
|
||||
border-color: rgba(var(--el-color-warning-rgb), 0.35);
|
||||
|
||||
.apt-badge {
|
||||
color: var(--el-color-warning-dark-2);
|
||||
background: var(--el-color-warning-light-7);
|
||||
}
|
||||
|
||||
.apt-time {
|
||||
color: var(--el-color-warning);
|
||||
}
|
||||
}
|
||||
|
||||
&.apt-row-done {
|
||||
background: linear-gradient(135deg, rgba(var(--el-color-info-rgb), 0.1), rgba(var(--el-color-info-rgb), 0.04));
|
||||
border-color: rgba(var(--el-color-info-rgb), 0.28);
|
||||
|
||||
.apt-badge {
|
||||
color: var(--el-color-info-dark-2);
|
||||
background: var(--el-color-info-light-7);
|
||||
}
|
||||
|
||||
.apt-time {
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
}
|
||||
|
||||
.apt-none {
|
||||
@@ -1621,7 +1747,7 @@ onUnmounted(() => {
|
||||
|
||||
.status-prescribed {
|
||||
font-size: 13px;
|
||||
color: var(--el-color-success);
|
||||
color: #4A5DFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -1647,6 +1773,33 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.followup-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
line-height: 1.35;
|
||||
|
||||
.followup-time {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.followup-doctor {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.followup-void-tag {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.followup-none {
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
|
||||
.assistant-cell {
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user