109 住院医生工作站-》住院病历的【存为模板】界面升级
This commit is contained in:
@@ -59,7 +59,7 @@ public class DocTemplateAppServiceImpl implements IDocTemplateAppService {
|
||||
docTemplateDto.setUserId(SecurityUtils.getLoginUser().getUserId());
|
||||
}
|
||||
docTemplate.setOrganizationId(docTemplateDto.getOrganizationId());
|
||||
docTemplate.setId(docTemplateDto.getUserId());
|
||||
docTemplate.setUserId(docTemplateDto.getUserId());
|
||||
docTemplate.setUseRange(docTemplateDto.getUseRange());
|
||||
docTemplate.setRemark(docTemplateDto.getRemark());
|
||||
docTemplateService.save(docTemplate);
|
||||
@@ -140,7 +140,7 @@ public class DocTemplateAppServiceImpl implements IDocTemplateAppService {
|
||||
docTemplate.setOrganizationId(docTemplateDto.getOrganizationId());
|
||||
docTemplate.setUserId(docTemplateDto.getUserId());
|
||||
docTemplate.setRemark(docTemplateDto.getRemark());
|
||||
docTemplateService.save(docTemplate);
|
||||
docTemplateService.updateById(docTemplate);
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
</div> -->
|
||||
<el-scrollbar class="emr-template-scrollbar-container" style="width: 100%">
|
||||
<div v-for="item in templateData" :key="item.id" class="scrollbar-item">
|
||||
<el-tooltip effect="dark" :content="`${item.name}`" placement="bottom">
|
||||
<el-text class="2" truncated @click="handleNodeClick(item)">
|
||||
<div class="template-item">
|
||||
{{ item.name }}
|
||||
@@ -19,7 +18,6 @@
|
||||
</el-space>
|
||||
</div>
|
||||
</el-text>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
@@ -74,10 +74,29 @@ export function updateTemplate(data) {
|
||||
}
|
||||
|
||||
// 删除模板
|
||||
export function deleteTemplate(ids) {
|
||||
export function deleteTemplate(id) {
|
||||
return request({
|
||||
url: '/document/template/delete',
|
||||
method: 'delete',
|
||||
data: ids,
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 保存或更新模板
|
||||
export function saveOrUpdate(data) {
|
||||
if (data.id && data.id !== null && data.id !== 0 && String(data.id).trim() !== '') {
|
||||
return request({
|
||||
url: '/document/template/update',
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
} else {
|
||||
const { id, ...restData } = data;
|
||||
return request({
|
||||
url: '/document/template/add',
|
||||
method: 'post',
|
||||
data: restData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
<template>
|
||||
<div class="template-editor">
|
||||
<el-form :model="formData" label-width="120px" class="form-container">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="name">
|
||||
<template #label> <span style="color: red">*</span>模板名称: </template>
|
||||
<el-input v-model="formData.name" placeholder="请输入模板名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="使用范围:">
|
||||
<el-select v-model="formData.useRange" placeholder="请选择使用范围">
|
||||
<el-option
|
||||
v-for="item in useRangeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-button type="primary" @click="handleAdd" class="add-btn">新增</el-button>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table :data="templateItems" border style="width: 100%">
|
||||
<el-table-column prop="fieldKey" label="病历元素">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
filterable
|
||||
v-model="scope.row.fieldKey"
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
@change="handleElementChange(scope.$index, scope.row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in elementOptions"
|
||||
:key="item.value"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="recordName" label="模板内容">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.recordName"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 1, maxRows: 10 }"
|
||||
></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="code" label="编码">
|
||||
<template #default="scope">
|
||||
<el-input disabled v-model="scope.row.code" placeholder="请输入编码"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operation" label="操作" width="80">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
circle
|
||||
@click="handleDelete(scope.$index)"
|
||||
></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { saveOrUpdate } from '../api';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
const userStore = useUserStore();
|
||||
|
||||
const emit = defineEmits(['close', 'submitOk', 'update:dialogVisible']);
|
||||
|
||||
const dialogVisible = defineModel('dialogVisible', {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
});
|
||||
|
||||
const elementOptions = ref([]);
|
||||
|
||||
const formData = reactive({
|
||||
name: '',
|
||||
useRange: '',
|
||||
useRangeName: '',
|
||||
organizationId: undefined,
|
||||
definitionId: undefined,
|
||||
id: null,
|
||||
});
|
||||
|
||||
const templateItems = ref([]);
|
||||
|
||||
const handleAdd = () => {
|
||||
for (let i = 0; i < templateItems.value.length; i++) {
|
||||
const item = templateItems.value[i];
|
||||
if (!item.fieldKey || item.fieldKey.trim() === '') {
|
||||
ElMessage.warning('请先选择病历元素');
|
||||
return;
|
||||
}
|
||||
if (!item.recordName || item.recordName.trim() === '') {
|
||||
ElMessage.warning('请先填写模版内容');
|
||||
return;
|
||||
}
|
||||
if (!item.code || item.code.trim() === '') {
|
||||
ElMessage.warning('请先填写编码');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
templateItems.value.push({
|
||||
recordName: '',
|
||||
fieldKey: '',
|
||||
code: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleElementChange = (index, row) => {
|
||||
const selectedItem = templateItems.value[index];
|
||||
const elementOption = elementOptions.value.find((item) => item.fieldKey === selectedItem.fieldKey);
|
||||
if (elementOption) {
|
||||
selectedItem.fieldKey = elementOption.fieldKey || '';
|
||||
selectedItem.code = elementOption.name + '-' + elementOption.fieldKey || '';
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (index) => {
|
||||
templateItems.value.splice(index, 1);
|
||||
ElMessage.success('删除成功');
|
||||
};
|
||||
|
||||
const clearData = () => {
|
||||
formData.name = '';
|
||||
templateItems.value = [];
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
clearData();
|
||||
dialogVisible.value = false;
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!formData.definitionId || formData.definitionId.trim() === '') {
|
||||
ElMessage.warning('请选择病历模版');
|
||||
return;
|
||||
}
|
||||
if (!formData.name || formData.name.trim() === '') {
|
||||
ElMessage.warning('请填写模版名称');
|
||||
return;
|
||||
}
|
||||
|
||||
if (templateItems.value.length === 0) {
|
||||
ElMessage.warning('请至少添加一条模板内容');
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < templateItems.value.length; i++) {
|
||||
const item = templateItems.value[i];
|
||||
if (!item.fieldKey || item.fieldKey.trim() === '') {
|
||||
ElMessage.warning('请先选择病历元素');
|
||||
return;
|
||||
}
|
||||
if (!item.recordName || item.recordName.trim() === '') {
|
||||
ElMessage.warning('请先填写模版内容');
|
||||
return;
|
||||
}
|
||||
|
||||
const validationResult = validateField(item.fieldKey, item.recordName);
|
||||
if (!validationResult.valid) {
|
||||
const fieldLabel = emrFieldLabels[item.fieldKey] || item.fieldKey;
|
||||
ElMessage.warning(`${fieldLabel}:${validationResult.message}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const params = {
|
||||
name: formData.name,
|
||||
definitionId: formData.definitionId,
|
||||
contextJson: JSON.stringify(templateItems.value),
|
||||
useRange: parseInt(formData.useRange) || 3,
|
||||
organizationId: formData.organizationId,
|
||||
userId: formData.userId,
|
||||
remark: formData.remark,
|
||||
};
|
||||
const res = await saveOrUpdate(params);
|
||||
if (res.code == 200) {
|
||||
ElMessage.success('保存成功');
|
||||
clearData();
|
||||
dialogVisible.value = false;
|
||||
emit('submitOk');
|
||||
emit('close');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
}
|
||||
};
|
||||
|
||||
const useRangeOptions = ref([
|
||||
{
|
||||
label: '个人',
|
||||
value: '3',
|
||||
},
|
||||
{
|
||||
label: '科室',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '全院',
|
||||
value: '1',
|
||||
},
|
||||
]);
|
||||
|
||||
const useRangeFind = (name) => {
|
||||
const mapArr = [
|
||||
{ label: '个人', value: '3' },
|
||||
{ label: '科室', value: '2' },
|
||||
{ label: '全院', value: '1' },
|
||||
];
|
||||
const findObj = mapArr.find((item) => item.label === name);
|
||||
return findObj;
|
||||
};
|
||||
|
||||
const initData = async (data) => {
|
||||
try {
|
||||
elementOptions.value = Object.keys(emrFieldLabels).map((key) => ({
|
||||
label: emrFieldLabels[key],
|
||||
value: key,
|
||||
fieldKey: key,
|
||||
name: emrFieldLabels[key],
|
||||
code: key,
|
||||
}));
|
||||
|
||||
formData.definitionId = data.definitionId;
|
||||
if (formData.useRange === '1') {
|
||||
formData.organizationId = userStore.orgId;
|
||||
} else {
|
||||
formData.organizationId = undefined;
|
||||
}
|
||||
if (data.isEdit) {
|
||||
formData.name = data.templateType;
|
||||
formData.id = data.id;
|
||||
if (data.useRange == '1') {
|
||||
formData.useRange = '1';
|
||||
formData.useRangeName = '全院';
|
||||
} else if (data.useRange == '2') {
|
||||
formData.useRange = '2';
|
||||
formData.useRangeName = '科室';
|
||||
} else {
|
||||
formData.useRange = '3';
|
||||
formData.useRangeName = '个人';
|
||||
}
|
||||
const content = typeof data.contextJson === 'string' ? JSON.parse(data.contextJson) : data.contextJson;
|
||||
templateItems.value = content;
|
||||
} else {
|
||||
formData.id = null;
|
||||
formData.useRange = useRangeFind(data.templateType)?.value || '3';
|
||||
formData.useRangeName = useRangeFind(data.templateType)?.label || '个人';
|
||||
templateItems.value = [];
|
||||
if (data.contextJson) {
|
||||
let content = typeof data.contextJson === 'string' ? JSON.parse(data.contextJson) : data.contextJson;
|
||||
if (Array.isArray(content)) {
|
||||
templateItems.value = content.map((item) => ({
|
||||
fieldKey: item.fieldKey || '',
|
||||
recordName: item.recordName || '',
|
||||
code: item.code || '',
|
||||
}));
|
||||
} else {
|
||||
templateItems.value = Object.keys(content)
|
||||
.filter((key) => content[key] !== null && content[key] !== undefined && content[key] !== '')
|
||||
.map((key) => ({
|
||||
fieldKey: key,
|
||||
recordName: typeof content[key] === 'object' ? JSON.stringify(content[key]) : String(content[key]),
|
||||
code: (emrFieldLabels[key] || key) + '-' + key,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('初始化模板数据失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
const emrFieldLabels = {
|
||||
chiefComplaint: '主诉',
|
||||
complaint: '主诉',
|
||||
currentIllnessHistory: '现病史',
|
||||
pastMedicalHistory: '既往史',
|
||||
pastHistory: '既往史',
|
||||
menstrualHistory: '个人史',
|
||||
allergyHistory: '过敏史',
|
||||
physicalExamination: '体格检查',
|
||||
treatment: '处理',
|
||||
auxiliaryExamination: '辅助检查',
|
||||
diagnosis: '诊断',
|
||||
diagnosisName: '诊断名称',
|
||||
diagnosisCode: '诊断编码',
|
||||
height: '身高',
|
||||
weight: '体重',
|
||||
temperature: '体温',
|
||||
pulse: '脉搏',
|
||||
respiration: '呼吸',
|
||||
bloodPressure: '血压',
|
||||
smokingHistory: '吸烟史',
|
||||
drinkingHistory: '饮酒史',
|
||||
familyHistory: '家族史',
|
||||
surgicalHistory: '手术史',
|
||||
traumaHistory: '外伤史',
|
||||
onsetDate: '发病日期',
|
||||
signDate: '签字日期',
|
||||
recordTime: '记录时间',
|
||||
admissionTime: '入院时间',
|
||||
reliability: '可靠性',
|
||||
patientName: '患者姓名',
|
||||
hospitalNo: '住院号',
|
||||
gender: '性别',
|
||||
age: '年龄',
|
||||
medicationList: '用药列表',
|
||||
nursingLevel: '护理等级',
|
||||
medicalOrder: '医嘱',
|
||||
};
|
||||
|
||||
const fieldValidationRules = {
|
||||
patientName: {
|
||||
label: '患者姓名',
|
||||
type: 'chinese',
|
||||
message: '患者姓名必须是中文',
|
||||
},
|
||||
hospitalNo: {
|
||||
label: '住院号',
|
||||
type: 'alphanumeric',
|
||||
message: '住院号必须是字母或数字',
|
||||
},
|
||||
admissionTime: {
|
||||
label: '入院时间',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
recordTime: {
|
||||
label: '记录时间',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
signDate: {
|
||||
label: '签字日期',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
onsetDate: {
|
||||
label: '发病日期',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
inHospitalTime: {
|
||||
label: '入院时间',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
outHospitalTime: {
|
||||
label: '出院时间',
|
||||
type: 'datetime',
|
||||
message: '请输入有效的日期时间格式',
|
||||
},
|
||||
gender: {
|
||||
label: '性别',
|
||||
type: 'select',
|
||||
options: ['男', '女', '未知'],
|
||||
message: '请选择性别',
|
||||
},
|
||||
genderEnum: {
|
||||
label: '性别',
|
||||
type: 'select',
|
||||
options: ['男', '女', '未知'],
|
||||
message: '请选择性别',
|
||||
},
|
||||
genderEnum_enumText: {
|
||||
label: '性别',
|
||||
type: 'select',
|
||||
options: ['男', '女', '未知'],
|
||||
message: '请选择性别',
|
||||
},
|
||||
age: {
|
||||
label: '年龄',
|
||||
type: 'number',
|
||||
message: '年龄必须是正整数',
|
||||
},
|
||||
height: {
|
||||
label: '身高',
|
||||
type: 'decimal',
|
||||
message: '身高必须是数字',
|
||||
},
|
||||
weight: {
|
||||
label: '体重',
|
||||
type: 'decimal',
|
||||
message: '体重必须是数字',
|
||||
},
|
||||
temperature: {
|
||||
label: '体温',
|
||||
type: 'decimal',
|
||||
message: '体温必须是数字',
|
||||
},
|
||||
pulse: {
|
||||
label: '脉搏',
|
||||
type: 'integer',
|
||||
message: '脉搏必须是整数',
|
||||
},
|
||||
respiration: {
|
||||
label: '呼吸',
|
||||
type: 'integer',
|
||||
message: '呼吸必须是整数',
|
||||
},
|
||||
bloodPressure: {
|
||||
label: '血压',
|
||||
type: 'bloodPressure',
|
||||
message: '血压格式应为收缩压/舒张压(如:120/80)',
|
||||
},
|
||||
inHospitalDays: {
|
||||
label: '住院天数',
|
||||
type: 'integer',
|
||||
message: '住院天数必须是正整数',
|
||||
},
|
||||
};
|
||||
|
||||
const getValidationRule = (fieldKey) => {
|
||||
return fieldValidationRules[fieldKey];
|
||||
};
|
||||
|
||||
const validateField = (fieldKey, value) => {
|
||||
if (!value || value.trim() === '') return { valid: true };
|
||||
const rule = fieldValidationRules[fieldKey];
|
||||
if (!rule) return { valid: true };
|
||||
const val = String(value).trim();
|
||||
|
||||
switch (rule.type) {
|
||||
case 'chinese':
|
||||
if (!/^[\u4e00-\u9fa5]+$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'alphanumeric':
|
||||
if (!/^[A-Za-z0-9]+$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'datetime':
|
||||
if (!/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'number':
|
||||
if (!/^[0-9]+$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'integer':
|
||||
if (!/^[0-9]+$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'decimal':
|
||||
if (!/^[0-9]+(\.[0-9]+)?$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'bloodPressure':
|
||||
if (!/^[0-9]+\/[0-9]+$/.test(val)) {
|
||||
return { valid: false, message: rule.message };
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
break;
|
||||
}
|
||||
return { valid: true };
|
||||
};
|
||||
|
||||
const getFieldLabel = (key) => {
|
||||
const fieldLabels = {
|
||||
chiefComplaint: '主诉',
|
||||
currentIllnessHistory: '现病史',
|
||||
pastMedicalHistory: '既往史',
|
||||
menstrualHistory: '个人史',
|
||||
allergyHistory: '过敏史',
|
||||
physicalExamination: '体格检查',
|
||||
treatment: '处理',
|
||||
auxiliaryExamination: '辅助检查',
|
||||
diagnosis: '诊断',
|
||||
height: '身高',
|
||||
weight: '体重',
|
||||
temperature: '体温',
|
||||
pulse: '脉搏',
|
||||
respiration: '呼吸',
|
||||
bloodPressure: '血压',
|
||||
smokingHistory: '吸烟史',
|
||||
drinkingHistory: '饮酒史',
|
||||
familyHistory: '家族史',
|
||||
surgicalHistory: '手术史',
|
||||
traumaHistory: '外伤史',
|
||||
onsetDate: '发病日期',
|
||||
diagnosisName: '诊断名称',
|
||||
diagnosisCode: '诊断编码',
|
||||
encounterId: '就诊ID',
|
||||
statusEnum: '状态',
|
||||
busNo: '住院号',
|
||||
inHospitalTime: '入院时间',
|
||||
outHospitalTime: '出院时间',
|
||||
patientId: '患者ID',
|
||||
patientName: '患者姓名',
|
||||
genderEnum: '性别',
|
||||
genderEnum_enumText: '性别',
|
||||
birthDate: '出生日期',
|
||||
age: '年龄',
|
||||
wardName: '病区',
|
||||
houseName: '科室',
|
||||
bedName: '床位',
|
||||
inOrgTime: '入院时间',
|
||||
inHospitalDays: '住院天数',
|
||||
inHospitalOrgId: '入院科室ID',
|
||||
inHospitalOrgName: '入院科室',
|
||||
contractNo: '合同编号',
|
||||
contractName: '合同名称',
|
||||
accountId: '账户ID',
|
||||
advanceAmount: '预交金额',
|
||||
totalAmount: '总金额',
|
||||
balanceAmount: '余额',
|
||||
medicalOrder: '医嘱',
|
||||
nursingLevel: '护理等级',
|
||||
diagnosisList: '诊断列表',
|
||||
medicationList: '用药列表',
|
||||
examinationResult: '检查结果',
|
||||
labResult: '检验结果',
|
||||
};
|
||||
return fieldLabels[key] || key;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
initData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.template-editor {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: 15px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.el-table th {
|
||||
background-color: #f5f7fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@@ -9,7 +9,6 @@
|
||||
</div> -->
|
||||
<el-scrollbar class="emr-template-scrollbar-container" style="width: 100%">
|
||||
<div v-for="item in templateData" :key="item.id" class="scrollbar-item">
|
||||
<el-tooltip effect="dark" :content="`${item.name}`" placement="bottom">
|
||||
<el-text class="2" truncated @click="handleNodeClick(item)">
|
||||
<div class="template-item">
|
||||
{{ item.name }}
|
||||
@@ -19,7 +18,6 @@
|
||||
</el-space>
|
||||
</div>
|
||||
</el-text>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
@@ -108,12 +106,18 @@ const handleEdit = (data) => {
|
||||
// 删除模板
|
||||
const handleDelete = async (item) => {
|
||||
try {
|
||||
const templateId = item.id || item.templateId;
|
||||
if (!templateId) {
|
||||
console.error('模板数据:', item);
|
||||
ElMessage.error('模板ID不存在');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm('确定要删除该模板吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await deleteTemplate(item.id);
|
||||
await deleteTemplate(templateId);
|
||||
ElMessage.success('删除成功');
|
||||
// 清除缓存中的数据,强制重新加载
|
||||
templateCache.delete(unref(definitionId));
|
||||
@@ -127,7 +131,10 @@ const handleDelete = async (item) => {
|
||||
};
|
||||
|
||||
const currentSelectTemplate = ref({});
|
||||
defineExpose({ queryList });
|
||||
const clearCache = () => {
|
||||
templateCache.delete(unref(definitionId));
|
||||
};
|
||||
defineExpose({ queryList, clearCache });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<template>
|
||||
<!-- 病历文件基本信息弹窗 -->
|
||||
<el-dialog
|
||||
:title="formData.id ? '编辑模板' : '新增模板'"
|
||||
v-model="dialogVisible"
|
||||
@@ -7,42 +6,85 @@
|
||||
destroy-on-close
|
||||
@open="handleOpen"
|
||||
>
|
||||
<!-- 使用el-form包裹表单 -->
|
||||
<el-form :model="formData" ref="formRef" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="24" class="mb8">
|
||||
<el-col :span="24">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="模板名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入模板名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="使用范围" prop="useRange">
|
||||
<el-radio-group v-model="formData.useRange">
|
||||
<el-radio :value="0" size="large">全院</el-radio>
|
||||
<el-radio :value="1" size="large">指定机构</el-radio>
|
||||
<el-radio :value="2" size="large">指定用户</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入版本"></el-input>
|
||||
<el-select v-model="formData.useRange" placeholder="请选择使用范围">
|
||||
<el-option label="个人" :value="3" />
|
||||
<el-option label="科室" :value="2" />
|
||||
<el-option label="全院" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-button type="primary" @click="handleAdd" class="add-btn">新增</el-button>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table :data="templateItems" border style="width: 100%">
|
||||
<el-table-column prop="fieldKey" label="病历元素">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
filterable
|
||||
v-model="scope.row.fieldKey"
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
@change="handleElementChange(scope.$index, scope.row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in elementOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="recordName" label="模板内容">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.recordName"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 1, maxRows: 10 }"
|
||||
></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="code" label="编码">
|
||||
<template #default="scope">
|
||||
<el-input disabled v-model="scope.row.code" placeholder="请输入编码"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operation" label="操作" width="80">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
circle
|
||||
@click="handleDelete(scope.$index)"
|
||||
></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer"></div>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref} from 'vue';
|
||||
import {addTemplate, updateTemplate} from '../api';
|
||||
import {ElMessage} from 'element-plus';
|
||||
import { ref, reactive } from 'vue';
|
||||
import { addTemplate, updateTemplate } from '../api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({
|
||||
@@ -58,7 +100,6 @@ const dialogVisible = defineModel('dialogVisible', {
|
||||
default: false,
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
@@ -68,65 +109,156 @@ const formData = ref({
|
||||
useRange: 2,
|
||||
organizationId: 0,
|
||||
userId: 0,
|
||||
remark: '',
|
||||
});
|
||||
// 表单验证规则(响应式,支持动态验证)
|
||||
|
||||
const templateItems = ref([]);
|
||||
const elementOptions = ref([]);
|
||||
|
||||
const emrFieldLabels = {
|
||||
chiefComplaint: '主诉',
|
||||
complaint: '主诉',
|
||||
currentIllnessHistory: '现病史',
|
||||
pastMedicalHistory: '既往史',
|
||||
pastHistory: '既往史',
|
||||
menstrualHistory: '个人史',
|
||||
allergyHistory: '过敏史',
|
||||
physicalExamination: '体格检查',
|
||||
treatment: '处理',
|
||||
auxiliaryExamination: '辅助检查',
|
||||
diagnosis: '诊断',
|
||||
diagnosisName: '诊断名称',
|
||||
diagnosisCode: '诊断编码',
|
||||
height: '身高',
|
||||
weight: '体重',
|
||||
temperature: '体温',
|
||||
pulse: '脉搏',
|
||||
respiration: '呼吸',
|
||||
bloodPressure: '血压',
|
||||
smokingHistory: '吸烟史',
|
||||
drinkingHistory: '饮酒史',
|
||||
familyHistory: '家族史',
|
||||
surgicalHistory: '手术史',
|
||||
traumaHistory: '外伤史',
|
||||
onsetDate: '发病日期',
|
||||
signDate: '签字日期',
|
||||
recordTime: '记录时间',
|
||||
admissionTime: '入院时间',
|
||||
reliability: '可靠性',
|
||||
patientName: '患者姓名',
|
||||
hospitalNo: '住院号',
|
||||
gender: '性别',
|
||||
age: '年龄',
|
||||
medicationList: '用药列表',
|
||||
nursingLevel: '护理等级',
|
||||
medicalOrder: '医嘱',
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
});
|
||||
|
||||
const handleOpen = () => {
|
||||
if (props.formData) {
|
||||
formData.value = props.formData;
|
||||
formData.value = JSON.parse(JSON.stringify(props.formData));
|
||||
if (props.formData.contextJson) {
|
||||
try {
|
||||
templateItems.value = typeof props.formData.contextJson === 'string'
|
||||
? JSON.parse(props.formData.contextJson)
|
||||
: JSON.parse(JSON.stringify(props.formData.contextJson));
|
||||
} catch (e) {
|
||||
templateItems.value = [];
|
||||
}
|
||||
} else {
|
||||
templateItems.value = [];
|
||||
}
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
elementOptions.value = Object.keys(emrFieldLabels).map((key) => ({
|
||||
label: emrFieldLabels[key],
|
||||
value: key,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
templateItems.value.push({
|
||||
fieldKey: '',
|
||||
recordName: '',
|
||||
code: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleElementChange = (index, row) => {
|
||||
row.code = (emrFieldLabels[row.fieldKey] || row.fieldKey) + '-' + row.fieldKey;
|
||||
};
|
||||
|
||||
const handleDelete = (index) => {
|
||||
templateItems.value.splice(index, 1);
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = () => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
// 表单验证通过,执行保存操作
|
||||
saveForm();
|
||||
} else {
|
||||
// 表单验证失败
|
||||
ElMessage.error('请填写必填项');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 保存表单
|
||||
|
||||
const saveForm = async () => {
|
||||
try {
|
||||
// 如果有当前节点数据,表示是编辑操作
|
||||
if (formData.value.id && formData.value.id !== '') {
|
||||
const res = await updateTemplate(formData.value);
|
||||
const params = {
|
||||
...formData.value,
|
||||
contextJson: JSON.stringify(templateItems.value),
|
||||
};
|
||||
if (formData.value.id && formData.value.id !== 0 && String(formData.value.id).trim() !== '') {
|
||||
const res = await updateTemplate(params);
|
||||
if (res.code == 200) {
|
||||
ElMessage.success('更新成功');
|
||||
emits('submitOk');
|
||||
dialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error('更新失败', error);
|
||||
ElMessage.error(res.msg || '更新失败');
|
||||
}
|
||||
} else {
|
||||
// 新建操作
|
||||
const res = await addTemplate(formData.value);
|
||||
const res = await addTemplate(params);
|
||||
if (res.code == 200) {
|
||||
ElMessage.success('保存成功');
|
||||
emits('submitOk');
|
||||
dialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error('保存失败', error);
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// ElMessage.error('保存失败',error);
|
||||
}
|
||||
};
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: 0,
|
||||
name: '',
|
||||
displayOrder: 0,
|
||||
contextJson: '',
|
||||
definitionId: 0,
|
||||
useRange: 2,
|
||||
organizationId: 0,
|
||||
userId: 0,
|
||||
};
|
||||
templateItems.value = [];
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-btn {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.table-container {
|
||||
margin-bottom: 15px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -101,6 +101,19 @@
|
||||
@submitOk="templateEditSubmitOk"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
v-model="createTemplateVisible"
|
||||
title="创建模板"
|
||||
width="50%"
|
||||
destroy-on-close
|
||||
>
|
||||
<CreateTemplate
|
||||
ref="createTemplateRef"
|
||||
@close="createTemplateVisible = false"
|
||||
@submitOk="createTemplateSubmitOk"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<el-drawer v-model="openDrawer" size="100%">
|
||||
<template #default>
|
||||
<div>
|
||||
@@ -124,6 +137,7 @@ import useUserStore from '@/store/modules/user';
|
||||
import History from './components/history';
|
||||
import Template from './components/template';
|
||||
import TemplateEdit from './components/templateEdit.vue';
|
||||
import CreateTemplate from './components/createTemplate.vue';
|
||||
|
||||
const userStore = useUserStore();
|
||||
// 移除未使用的变量
|
||||
@@ -249,12 +263,20 @@ const newEmr = () => {
|
||||
};
|
||||
|
||||
const saveAsModel = async () => {
|
||||
try {
|
||||
currentOperate.value = 'addTemplate';
|
||||
await emrComponentRef.value?.submit();
|
||||
} catch (error) {
|
||||
ElMessage.error('存为模版失败');
|
||||
if (!currentSelectTemplate.value || !currentSelectTemplate.value.id) {
|
||||
ElMessage.warning('请先选择病历模板');
|
||||
return;
|
||||
}
|
||||
const emrFormData = emrComponentRef.value?.formData || {};
|
||||
createTemplateVisible.value = true;
|
||||
nextTick(() => {
|
||||
createTemplateRef.value?.initData({
|
||||
definitionId: currentSelectTemplate.value.id,
|
||||
templateType: '全院',
|
||||
isEdit: false,
|
||||
contextJson: emrFormData,
|
||||
});
|
||||
});
|
||||
};
|
||||
const editForm = ref({
|
||||
id: '',
|
||||
@@ -728,9 +750,24 @@ const templateRef = ref(null);
|
||||
const handleTemplateClick = async (data) => {
|
||||
try {
|
||||
newEmr();
|
||||
editForm.value = data;
|
||||
editForm.value = {
|
||||
...data,
|
||||
id: '',
|
||||
recordTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
nextTick(() => {
|
||||
emrComponentRef.value?.setFormData(JSON.parse(editForm.value.contextJson));
|
||||
if (data.contextJson) {
|
||||
let parsedData = typeof data.contextJson === 'string' ? JSON.parse(data.contextJson) : data.contextJson;
|
||||
if (Array.isArray(parsedData)) {
|
||||
parsedData = parsedData.reduce((acc, item) => {
|
||||
if (item.fieldKey) {
|
||||
acc[item.fieldKey] = item.recordName;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
emrComponentRef.value?.setFormData(parsedData);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
ElMessage.error('设置模板数据失败');
|
||||
@@ -738,56 +775,31 @@ const handleTemplateClick = async (data) => {
|
||||
}
|
||||
};
|
||||
const templateEdit = (data) => {
|
||||
editTemplateForm.value = data;
|
||||
editTemplateForm.value = JSON.parse(JSON.stringify(data));
|
||||
templateEditVisible.value = true;
|
||||
};
|
||||
// ====dialog
|
||||
const templateEditVisible = ref(false);
|
||||
const createTemplateVisible = ref(false);
|
||||
const createTemplateRef = ref(null);
|
||||
const templateEditSubmitOk = async () => {
|
||||
try {
|
||||
// 根据是否存在ID来决定是新增还是编辑
|
||||
if (!editTemplateForm.value.id) {
|
||||
// 新增模板
|
||||
const addResult = await addTemplate(editTemplateForm.value);
|
||||
if (addResult && addResult.code === 200) {
|
||||
ElMessage.success('模板新增成功');
|
||||
// 刷新模板列表
|
||||
if (templateRef.value && typeof templateRef.value.queryList === 'function') {
|
||||
// 在模板操作后,需要清除缓存并重新加载
|
||||
if (templateRef.value) {
|
||||
if (templateRef.value.clearCache) {
|
||||
templateRef.value.clearCache();
|
||||
}
|
||||
templateRef.value.queryList();
|
||||
}
|
||||
// 关闭模板编辑弹窗
|
||||
templateEditVisible.value = false;
|
||||
// 清空模板编辑表单
|
||||
Object.assign(editTemplateForm.value, {
|
||||
id: '',
|
||||
name: '',
|
||||
displayOrder: 0,
|
||||
contextJson: '',
|
||||
definitionId: '',
|
||||
useRange: 2,
|
||||
organizationId: userStore.orgId,
|
||||
userId: '',
|
||||
useRanges: [1],
|
||||
remark: '',
|
||||
});
|
||||
} else {
|
||||
ElMessage.error('模板新增失败');
|
||||
console.error('模板新增失败:', addResult);
|
||||
};
|
||||
|
||||
// 新增模板成功后的回调
|
||||
const createTemplateSubmitOk = () => {
|
||||
if (templateRef.value) {
|
||||
if (templateRef.value.clearCache) {
|
||||
templateRef.value.clearCache();
|
||||
}
|
||||
} else {
|
||||
// 编辑模板 - 暂不实现编辑功能,只支持新增
|
||||
ElMessage.warning('暂不支持编辑模板');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理模板编辑提交失败:', error);
|
||||
ElMessage.error('处理模板编辑提交失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
// 无论成功失败都刷新列表
|
||||
historyRef.value?.queryList();
|
||||
// 模板查询已经在模板组件内部通过防抖和缓存优化,这里不再重复调用
|
||||
// templateRef.value?.queryList();
|
||||
templateRef.value.queryList();
|
||||
}
|
||||
ElMessage.success('保存模板成功');
|
||||
};
|
||||
// onBeforeMount(() => {});
|
||||
// 监听患者信息变化,实现联动显示
|
||||
|
||||
Reference in New Issue
Block a user