Merge branch 'master' into tongji-99

This commit is contained in:
Your Name
2026-05-07 15:43:49 +08:00
16 changed files with 11147 additions and 117 deletions
@@ -190,6 +190,18 @@
<span v-if="formData.prescription_type !== '浓缩水丸' && formData.prescription_type !== '饮片'" class="ml-2">{{ formData.dosage_unit }}</span>
</el-form-item>
</el-col>
<el-col v-if="formData.prescription_type === '浓缩水丸'" :span="8">
<el-form-item label=" " prop="dosage_bag_count" label-width="12px">
<el-select v-model="formData.dosage_bag_count" placeholder="请选择袋数" class="!w-[120px]">
<el-option label="1袋" :value="1" />
<el-option label="2袋" :value="2" />
<el-option label="3袋" :value="3" />
<el-option label="4袋" :value="4" />
<el-option label="5袋" :value="5" />
</el-select>
</el-form-item>
</el-col>
<!-- 饮片每贴出包数 -->
<el-col v-if="formData.prescription_type === '饮片'" :span="8">
@@ -676,6 +688,7 @@ interface PrescriptionForm {
prescription_type: string
dosage_amount: number | undefined // 用量数值
dosage_unit: string // 用量单位 (g 或 ml)
dosage_bag_count: number // 用量袋数(浓缩水丸,1-5袋)
need_decoction: boolean // 是否代煎(仅饮片)
bags_per_dose: number // 每贴出包数(仅饮片,1-9包)
patient_name: string
@@ -764,6 +777,7 @@ const formData = reactive<PrescriptionForm>({
prescription_type: '浓缩水丸',
dosage_amount: 10,
dosage_unit: 'g',
dosage_bag_count: 1,
need_decoction: false,
bags_per_dose: 1,
patient_name: '',
@@ -954,6 +968,7 @@ const open = async (data: any, options?: { templateId?: number; stationName?: st
formData.prescription_type = '浓缩水丸'
formData.dosage_unit = 'g'
formData.dosage_amount = 10
formData.dosage_bag_count = 1
formData.patient_name = data.patient_name || diagnosis.patient_name || ''
formData.gender = data.patient_gender ?? diagnosis.gender ?? 0
formData.gender_desc = formData.gender === 1 ? '男' : '女'
@@ -980,6 +995,8 @@ const open = async (data: any, options?: { templateId?: number; stationName?: st
formData.doctor_name = userStore.userInfo?.name || ''
formData.doctor_signature = ''
formData.is_shared = 0
formData.need_decoction = false
formData.bags_per_dose = 1
savedPrescription.value = null
visible.value = true
@@ -1206,16 +1223,19 @@ watch(() => formData.prescription_type, (newType) => {
if (newType === '浓缩水丸') {
formData.dosage_unit = 'g'
formData.dosage_amount = 10
formData.dosage_bag_count = 1
formData.need_decoction = false
formData.bags_per_dose = 1
} else if (newType === '饮片') {
formData.dosage_unit = 'ml'
formData.dosage_amount = 50
formData.dosage_bag_count = 1
formData.bags_per_dose = 1
// need_decoction 保持用户选择
} else {
formData.dosage_unit = 'g'
formData.dosage_amount = undefined
formData.dosage_bag_count = 1
formData.need_decoction = false
formData.bags_per_dose = 1
}
@@ -1275,6 +1295,7 @@ const handleSave = async () => {
dose_unit: formData.dose_unit,
dosage_amount: formData.dosage_amount,
dosage_unit: formData.dosage_unit,
dosage_bag_count: formData.dosage_bag_count,
need_decoction: formData.need_decoction,
bags_per_dose: formData.bags_per_dose,
usage_days: formData.usage_days,
@@ -1354,12 +1375,15 @@ const handleNewPrescription = async () => {
if (formData.prescription_type === '浓缩水丸') {
formData.dosage_unit = 'g'
formData.dosage_amount = 10
formData.dosage_bag_count = 1
} else if (formData.prescription_type === '饮片') {
formData.dosage_unit = 'ml'
formData.dosage_amount = 50
formData.dosage_bag_count = 1
} else {
formData.dosage_unit = 'g'
formData.dosage_amount = undefined
formData.dosage_bag_count = 1
}
formData.patient_name = saved.patient_name || ''
formData.gender = saved.gender ?? 0
@@ -798,6 +798,17 @@
/>
</el-form-item>
</el-col>
<el-col v-if="editForm.prescription_type === '浓缩水丸'" :span="8">
<el-form-item label=" " prop="dosage_bag_count" label-width="12px">
<el-select v-model="editForm.dosage_bag_count" placeholder="请选择袋数" class="w-[120px]">
<el-option label="1袋" :value="1" />
<el-option label="2袋" :value="2" />
<el-option label="3袋" :value="3" />
<el-option label="4袋" :value="4" />
<el-option label="5袋" :value="5" />
</el-select>
</el-form-item>
</el-col>
<!-- 饮片代煎选项 -->
<el-col v-if="editForm.prescription_type === '饮片'" :span="8">
@@ -2077,15 +2088,19 @@ const rxUsageText = computed(() => {
if (!v) return '—'
if (v.usage_text) return v.usage_text
const times = Number(v.times_per_day) > 0 ? Number(v.times_per_day) : 3
const bags = Number(v.bags_per_dose) > 0 ? Number(v.bags_per_dose) : 1
const amount = v.dosage_amount != null ? Number(v.dosage_amount) : 10
const unit = v.dosage_unit || 'g'
const usageWay = v.usage_way || '温水送服'
const usageTime = v.usage_time || ''
const seg: string[] = []
seg.push(`每天${times}次`)
seg.push(`一次${bags}包(约${amount}${unit})`)
seg.push(`每包${amount}${unit}`)
if ((v.prescription_type || '浓缩水丸') === '浓缩水丸') {
const bags = Number(v.dosage_bag_count) > 0 ? Number(v.dosage_bag_count) : 1
seg.push(`一次${bags}袋`)
seg.push(`每袋${amount}${unit}`)
} else {
seg.push(`一次${amount}${unit}`)
}
seg.push(usageWay)
if (usageTime) seg.push(usageTime)
return seg.join(', ')
@@ -2254,6 +2269,7 @@ const editForm = reactive({
prescription_type: '浓缩水丸',
dosage_amount: 1 as number | undefined,
dosage_unit: 'g',
dosage_bag_count: 1,
need_decoction: false,
bags_per_dose: 1,
patient_name: '',
@@ -2367,16 +2383,19 @@ watch(() => editForm.prescription_type, (newType) => {
if (newType === '浓缩水丸') {
editForm.dosage_unit = 'g'
editForm.dosage_amount = 1
editForm.dosage_bag_count = 1
editForm.need_decoction = false
editForm.bags_per_dose = 1
} else if (newType === '饮片') {
editForm.dosage_unit = 'ml'
editForm.dosage_amount = 50
editForm.dosage_bag_count = 1
editForm.bags_per_dose = 1
// need_decoction 保持用户选择
} else {
editForm.dosage_unit = 'g'
editForm.dosage_amount = undefined
editForm.dosage_bag_count = 1
editForm.need_decoction = false
editForm.bags_per_dose = 1
}
@@ -2953,6 +2972,11 @@ const removeHerb = (index: number) => {
const resetForm = () => {
editForm.id = 0
editForm.prescription_type = '浓缩水丸'
editForm.dosage_amount = 1
editForm.dosage_unit = 'g'
editForm.dosage_bag_count = 1
editForm.need_decoction = false
editForm.bags_per_dose = 1
editForm.patient_name = ''
editForm.gender = 1
editForm.age = 0
@@ -3070,6 +3094,7 @@ const handleEdit = async (row: any) => {
}
}
editForm.dosage_bag_count = src.dosage_bag_count ? Number(src.dosage_bag_count) : 1
editForm.need_decoction = src.need_decoction === 1 || src.need_decoction === true
editForm.bags_per_dose = src.bags_per_dose ? Number(src.bags_per_dose) : 1
editForm.patient_name = src.patient_name || ''
@@ -801,6 +801,9 @@
<el-descriptions-item label="用量">
<template v-if="detailPrescription.dosage_amount">
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}袋
</template>
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
</span>
@@ -4459,7 +4462,8 @@ const slipPerBagDosageText = computed(() => {
const d = prescriptionViewData.value
if (!d || (d.prescription_type || '浓缩水丸') !== '浓缩水丸') return '—'
if (d.dosage_amount == null || d.dosage_amount === '') return '—'
return `${d.dosage_amount}${d.dosage_unit || 'g'}`
const bags = Number(d.dosage_bag_count) > 0 ? Number(d.dosage_bag_count) : 1
return `${d.dosage_amount}${d.dosage_unit || 'g'} · ${bags}袋`
})
const slipPillGrams = computed(() => {
@@ -4564,15 +4568,19 @@ const rxUsageText = computed(() => {
if (!v) return '—'
if (v.usage_text) return v.usage_text
const times = Number(v.times_per_day) > 0 ? Number(v.times_per_day) : 3
const bags = Number(v.bags_per_dose) > 0 ? Number(v.bags_per_dose) : 1
const amount = v.dosage_amount != null ? Number(v.dosage_amount) : 10
const unit = v.dosage_unit || 'g'
const usageWay = v.usage_way || '温水送服'
const usageTime = v.usage_time || ''
const seg: string[] = []
seg.push(`每天${times}次`)
seg.push(`一次${bags}包(约${amount}${unit})`)
seg.push(`每包${amount}${unit}`)
if ((v.prescription_type || '浓缩水丸') === '浓缩水丸') {
const bags = Number(v.dosage_bag_count) > 0 ? Number(v.dosage_bag_count) : 1
seg.push(`一次${bags}袋`)
seg.push(`每袋${amount}${unit}`)
} else {
seg.push(`一次${amount}${unit}`)
}
seg.push(usageWay)
if (usageTime) seg.push(usageTime)
return seg.join(', ')
@@ -819,6 +819,9 @@
<el-descriptions-item label="用量">
<template v-if="detailPrescription.dosage_amount">
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}
</template>
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
</span>
@@ -4510,7 +4513,8 @@ const slipPerBagDosageText = computed(() => {
const d = prescriptionViewData.value
if (!d || (d.prescription_type || '浓缩水丸') !== '浓缩水丸') return '—'
if (d.dosage_amount == null || d.dosage_amount === '') return '—'
return `${d.dosage_amount}${d.dosage_unit || 'g'}`
const bags = Number(d.dosage_bag_count) > 0 ? Number(d.dosage_bag_count) : 1
return `${d.dosage_amount}${d.dosage_unit || 'g'} · ${bags}`
})
const slipPillGrams = computed(() => {
@@ -4615,15 +4619,19 @@ const rxUsageText = computed(() => {
if (!v) return '—'
if (v.usage_text) return v.usage_text
const times = Number(v.times_per_day) > 0 ? Number(v.times_per_day) : 3
const bags = Number(v.bags_per_dose) > 0 ? Number(v.bags_per_dose) : 1
const amount = v.dosage_amount != null ? Number(v.dosage_amount) : 10
const unit = v.dosage_unit || 'g'
const usageWay = v.usage_way || '温水送服'
const usageTime = v.usage_time || ''
const seg: string[] = []
seg.push(`每天${times}`)
seg.push(`一次${bags}包(约${amount}${unit})`)
seg.push(`每包${amount}${unit}`)
if ((v.prescription_type || '浓缩水丸') === '浓缩水丸') {
const bags = Number(v.dosage_bag_count) > 0 ? Number(v.dosage_bag_count) : 1
seg.push(`一次${bags}`)
seg.push(`每袋${amount}${unit}`)
} else {
seg.push(`一次${amount}${unit}`)
}
seg.push(usageWay)
if (usageTime) seg.push(usageTime)
return seg.join(', ')
File diff suppressed because it is too large Load Diff
@@ -3,9 +3,9 @@
<div class="toolbar">
<el-button
type="primary"
:disabled="!props.diagnosisId"
:disabled="!props.diagnosisId || !!props.readOnly || !hasDailyRecordPermission"
@click="handleAdd"
v-perms="['tcm.diagnosisTodo/add']"
v-perms="DAILY_RECORD_PERMS"
>
+ 新增待办
</el-button>
@@ -51,11 +51,11 @@
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default="{ row }">
<el-button
v-if="row.status === 0 && row.can_cancel"
v-if="!props.readOnly && row.status === 0 && row.can_cancel"
link
type="danger"
@click="handleCancel(row)"
v-perms="['tcm.diagnosisTodo/cancel']"
v-perms="DAILY_RECORD_PERMS"
>
取消
</el-button>
@@ -114,15 +114,21 @@
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import { diagnosisTodoLists, diagnosisTodoAdd, diagnosisTodoCancel } from '@/api/tcm'
import { computed } from 'vue'
import { hasPermission } from '@/utils/perm'
import feedback from '@/utils/feedback'
const DAILY_RECORD_PERMS = ['tcm.diagnosis/dailyRecord']
interface Props {
diagnosisId: number | null | undefined
patientId?: number | null
patientName?: string
readOnly?: boolean
}
const props = defineProps<Props>()
const hasDailyRecordPermission = computed(() => hasPermission(DAILY_RECORD_PERMS))
// pager 与项目内 <pagination v-model="pager" /> 约定的字段保持一致
const pager = reactive({
@@ -136,7 +142,7 @@ const pager = reactive({
const filterStatus = ref<number | ''>('')
const fetchList = async () => {
if (!props.diagnosisId || Number(props.diagnosisId) <= 0) {
if (!props.diagnosisId || Number(props.diagnosisId) <= 0 || !hasDailyRecordPermission.value) {
pager.lists = []
pager.count = 0
return
@@ -220,7 +226,7 @@ const disablePastDate = (date: Date) => {
}
const handleAdd = () => {
if (!props.diagnosisId) {
if (!props.diagnosisId || props.readOnly || !hasDailyRecordPermission.value) {
feedback.msgWarning('诊单未保存,无法添加待办')
return
}
@@ -248,7 +254,6 @@ const handleAddSubmit = async () => {
content: addForm.content.trim(),
remind_time: Math.floor(remindMs / 1000)
})
feedback.msgSuccess('已创建')
addDialogVisible.value = false
fetchList()
} catch {
@@ -260,6 +265,7 @@ const handleAddSubmit = async () => {
// ========== 取消 ==========
const handleCancel = async (row: any) => {
if (!hasDailyRecordPermission.value || props.readOnly) return
try {
await feedback.confirm(`确认取消「${row.remind_time_text} · ${truncate(row.content, 30)}」吗?`)
} catch {
+12 -44
View File
@@ -573,54 +573,22 @@
</div>
<el-empty v-else description="请先保存诊单后查看" />
</el-tab-pane>
<!-- 血糖血压记录标签页 -->
<el-tab-pane label="血糖血压记录" name="blood" :disabled="!formData.id">
<blood-record-list
v-if="formData.id"
:diagnosis-id="Number(formData.id)"
:patient-id="Number(formData.patient_id)"
:read-only="viewOnly"
/>
<el-empty v-else description="请先保存诊单后再添加血糖血压记录" />
</el-tab-pane>
<!-- 饮食记录标签页 -->
<el-tab-pane label="饮食记录" name="diet" :disabled="!formData.id">
<diet-record-list
v-if="formData.id"
:diagnosis-id="Number(formData.id)"
:patient-id="Number(formData.patient_id)"
:read-only="viewOnly"
/>
<el-empty v-else description="请先保存诊单后再添加饮食记录" />
</el-tab-pane>
<!-- 运动打卡记录标签页 -->
<el-tab-pane label="运动打卡记录" name="exercise" :disabled="!formData.id">
<exercise-record-list
v-if="formData.id"
:diagnosis-id="Number(formData.id)"
:patient-id="Number(formData.patient_id)"
:read-only="viewOnly"
/>
<el-empty v-else description="请先保存诊单后再添加运动打卡记录" />
</el-tab-pane>
<!-- 待办事项标签页T5企微推送给创建人本人 -->
<el-tab-pane
label="待办事项"
name="todo"
v-if="hasDailyRecordPermission"
label="日常记录"
name="daily"
:disabled="!formData.id"
lazy
v-if="hasPermission(['tcm.diagnosisTodo/lists'])"
>
<diagnosis-todo-list
<daily-matrix
v-if="formData.id"
:diagnosis-id="Number(formData.id)"
:patient-id="Number(formData.patient_id)"
:age="formData.age"
:patient-name="formData.patient_name"
:read-only="viewOnly"
/>
<el-empty v-else description="请先保存诊单后再添加待办事项" />
<el-empty v-else description="请先保存诊单后查看" />
</el-tab-pane>
<!-- 病历记录标签页开方后自动显示 -->
@@ -746,10 +714,7 @@ import useUserStore from '@/stores/modules/user'
import useAppStore from '@/stores/modules/app'
import { ElMessage } from 'element-plus'
import { QuestionFilled } from '@element-plus/icons-vue'
import BloodRecordList from './components/BloodRecordList.vue'
import DietRecordList from './components/DietRecordList.vue'
import ExerciseRecordList from './components/ExerciseRecordList.vue'
import DiagnosisTodoList from './components/DiagnosisTodoList.vue'
import DailyMatrix from './components/DailyMatrix.vue'
import CaseRecordList from './components/CaseRecordList.vue'
import CallRecordPanel from './components/CallRecordPanel.vue'
import ImChatRecordPanel from './components/ImChatRecordPanel.vue'
@@ -777,6 +742,7 @@ const submitting = ref(false)
/** 拥有后手机号可正常编辑,失焦不再强制脱敏 */
const hasPhonePlainPermission = computed(() => hasPermission(['tcm.diagnosis/phonePlain']))
const hasDailyRecordPermission = computed(() => hasPermission(['tcm.diagnosis/dailyRecord']))
/** 编辑且无明文权限:只读脱敏,点击切换查看完整号 */
const showPhoneMaskedEdit = computed(() => mode.value === 'edit' && !hasPhonePlainPermission.value)
const phoneRevealUnlocked = ref(false)
@@ -831,6 +797,9 @@ watch([visible, activeTab], () => {
if (activeTab.value === 'appointmentRecords' && !hasPermission(['doctor.appointment/lists'])) {
activeTab.value = 'basic'
}
if (activeTab.value === 'daily' && !hasDailyRecordPermission.value) {
activeTab.value = 'basic'
}
})
@@ -1857,4 +1826,3 @@ defineExpose({
}
}
</style>