Compare commits

...

2 Commits

Author SHA1 Message Date
15608dcc80 Merge branch 'main' of http://47.97.125.165:8902/jiangs/lis8.0-vue3 2025-12-26 17:22:42 +08:00
465671bf8c 高级审核 2025-12-26 17:22:35 +08:00
7 changed files with 339 additions and 25 deletions

View File

@ -10,3 +10,21 @@ export function queryList(query?: Object) {
params: query,
});
}
//保存报告列表
export function updateRules(data: Object) {
return request({
url: '/labCheckrules/update',
method: 'post',
data: data,
});
}
//删除报告列表
export function deleteRules(data: Object) {
return request({
url: '/labCheckrules/delete',
method: 'get',
params: data,
});
}

View File

@ -359,3 +359,8 @@ aside {
color: #1f6dd3;
}
}
.dialog-footer {
margin-top: 20px;
text-align: right;
}

View File

@ -61,6 +61,7 @@ const props = withDefaults(defineProps<Props>(), {
dictType: '',
clearable: true,
objKey: 'value',
keyValue: false,
fields: () => [
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', width: 150, enablePinyinSearch: true },
@ -78,9 +79,6 @@ const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }
const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const currentIndex = ref(-1);
// 字典数据存储
const dictData = ref<Record<string, any[]>>({});
// 预处理数据:生成拼音信息
@ -116,7 +114,7 @@ const visibleChange = (val: any) => {
currentIndex.value = filterData.value.findIndex(item => item[props.value!] === props.data);
if (currentIndex.value >= 0) {
tableRef.value.setCurrentRow(filterData.value[currentIndex.value]);
tableRef.value.setScrollTop(currentIndex.value);
tableRef.value.setScrollTop(currentIndex.value * 30);
}
}, 10);
}
@ -140,7 +138,7 @@ const handleDataEcho = () => {
} else if (props.tableData) {
row = props.tableData?.find((item: any) => props.objKey && item[props.objKey] === props.data)
}
selectShowValue.value = row ? (props.label ? row[props.label] : (props.value ? row[props.value] : '')) : '';
selectShowValue.value = row ? (props.keyValue ? `${row[props.value]} ${row[props.label]}` : (props.label ? row[props.label] : (props.value ? row[props.value] : ''))) : '';
}
};
@ -238,7 +236,7 @@ const handleKeydown = (e: any) => {
tableRef.value.setCurrentRow(filterData.value[currentIndex.value]);
// 滚动到当前行
tableRef.value.setScrollTop(currentIndex.value);
tableRef.value.setScrollTop(currentIndex.value * 30);
// 回车确认
if (e.key === "Enter") {
@ -301,6 +299,8 @@ const setLabel = (val: any) => {
selectShowValue.value = val[props.label];
} else if (props.value) {
selectShowValue.value = val[props.value];
} else if (props.keyValue) {
selectShowValue.value = `${val[props.value]} ${val[props.label]}`;
}
if (props.objKey) {
// 返回值objKey对应的对象数据

View File

@ -33,12 +33,12 @@ export const getDictData = async (...args: string[]) => {
//字典数据格式化方法
export const formatDict = (v: string, dictType: string) => {
if (v == undefined) return;
return dictData.value[dictType]?.find((item: any) => item.value == v.trim())?.label;
return dictData.value[dictType]?.find((item: any) => item.value == v.trim())?.label || v;
}
//仪器格式化方法
export const formatInstr = (v: string) => {
return instrOptions.value.find((item: any) => item.yq == v.trim())?.yqmc;
return instrOptions.value.find((item: any) => item.yq == v.trim())?.yqmc || v;
}
export { dictData, instrOptions }

View File

@ -18,6 +18,7 @@ export interface Props {
disabled?: boolean;
dictType?: string;
clearable?: boolean;
keyValue?: boolean; //是否使用键值对模式
}

View File

@ -8,7 +8,7 @@
<div class="app-container">
<el-form ref="form" :model="queryParams" label-width="80px" inline>
<el-form-item label="仪器" prop="yq">
<YQSelectTable v-model:data="queryParams.yq" width="200px" clearable @get-data-value="getList" />
<YQSelectTable v-model:data="queryParams.rptunitid" width="200px" clearable @get-data-value="getList" />
</el-form-item>
</el-form>
<div class="mb10">
@ -17,33 +17,124 @@
<el-button type="danger" @click="handleDel">删除</el-button>
</div>
<div style="height: calc(100% - 110px);">
<CustomTable ref="tableRef" v-model:data="tableData" :columns="columns" :config="tableConfig">
<template #yq="{ row }">
<CustomTable ref="customTableRef" v-model:data="tableData" :columns="columns" :config="tableConfig"
@row-click="selectedRows = $event">
<template #checktype="{ row }">
<el-select v-model="row.checktype" placeholder="请选择" class="full-width-input">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</template>
<template #logflag="{ row }">
<el-switch v-model="row.logflag" active-value="1" inactive-value="0"
style="--el-switch-on-color: #13ce66; " />
</template>
<template #ruledisplay="{ row, $index }">
<el-input v-model="row.ruledisplay" class="full-width-input" readonly @dblclick="handleEdit(row, $index)" />
</template>
<template #logcontent="{ row }">
<el-input v-model="row.logcontent" class="full-width-input" />
</template>
<template #autocheck="{ row }">
<el-switch v-model="row.autocheck" active-value="1" inactive-value="0"
style="--el-switch-on-color: #13ce66; " />
</template>
</CustomTable>
</div>
<el-dialog v-model="dialogVisible" title="组合审核条件生成导向" width="38vw" :close-on-click-modal="false" :draggable="true">
<el-input type="textarea" v-model="ruledisplayText" :rows="5" />
<div class="btn_text">
<div class="tips"> 按钮说明: </div>
<div>
<el-input type="textarea" v-model="explanation" :rows="5" />
</div>
</div>
<el-row :gutter="15">
<el-col :span="12">
<el-input v-model="searchKey" ref="searchInput" size="small" placeholder="快速搜索(支持简拼)" class="mb5" clearable
@clear="filterDataHandle" @input="filterDataHandle" />
<CustomVxeTable ref="tableRefs" :data="filterData" :columns="xmColumns" class="mytable-style"
:scrollYConfig="{ enabled: true, rSize: 30, height: '350', }" @current-change="rowHandle" />
</el-col>
<el-col :span="12">
<div class="btns">
<el-button @click="btnClick('+')">+ 加</el-button>
<el-button @click="btnClick('-')">- 减</el-button>
<el-button @click="btnClick('*')">* 乘</el-button>
<el-button @click="btnClick('/')">/ 除</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('>')">&gt;</el-button>
<el-button @click="btnClick('>=')">>=</el-button>
<el-button @click="btnClick('=')">=</el-button>
<el-button @click="btnClick('<=')">&lt;=</el-button>
<el-button @click="btnClick('<')">&lt;</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('(')">(</el-button>
<el-button @click="btnClick(')')">)</el-button>
<el-button @click="btnClick('OR')">or</el-button>
<el-button @click="btnClick('AND')">and</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('[性别]')">性别</el-button>
<el-button @click="btnClick('[年龄]')">年龄</el-button>
<el-button @click="btnClick('[科室]')">科室</el-button>
<el-button @click="btnClick('[诊断]')">诊断</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('[病人来源]')">病人来源</el-button>
<el-button @click="btnClick('[标本类型]')">标本类型</el-button>
<el-button @click="btnClick('[报告单备注]')">报告单备注</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('[任意项目-ID]')">任意项目-ID</el-button>
<el-button @click="btnClick('[任意项目-数值结果]')">任意项目-数值结果</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('[任意项目-文字结果]')">任意项目-文字结果</el-button>
<el-button @click="btnClick('[任意项目-危急值标志]')">任意项目-危急值标志</el-button>
</div>
<div class="btns">
<el-button @click="btnClick('[任意项目-复查标志]')">任意项目-复查标志</el-button>
<el-button @click="btnClick('[任意项目-结果标志]')">任意项目-结果标志</el-button>
</div>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="subItem">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import YQSelectTable from '@/components/SelectTable/YQSelectTable/index.vue';
import { queryList } from '@/api/liswork/advancedReview';
import { queryList, updateRules, deleteRules } from '@/api/liswork/advancedReview';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getFirstLetter } from '@/utils/pinyin';
import { queryOldxminfo } from '@/api/liswork/xtwh/xmReqItemVsApi'
import { template } from 'lodash';
import { templateRef } from '@vueuse/core';
const queryParams = ref({
yq: '46',
rptunitid: '46',
});
const customTableRef = templateRef('customTableRef');
const dialogVisible = ref(false);
const checkrulesText = ref('')
const ruledisplayText = ref('')
const explanation = ref('')
const tableData = ref<Array<any>>([]);
const columns = ref([
{ label: '规则ID', prop: 'ruleid', visible: true, align: 'center', width: '80' },
{ label: '条件类型', prop: 'checktype', visible: true, align: 'center', width: '150' },
{ label: '审核启用', prop: 'logflag', visible: true, align: 'center', width: '80' },
{ label: '组合条件(双击弹窗编辑)', prop: 'ruledisplay', visible: true, align: 'center', },
{ label: '记录备注信息', prop: 'logcontent', visible: true, align: 'center', width: '300' },
{ label: '组合条件', prop: 'checkrules', visible: true, align: 'center', },
{ label: '自动审核', prop: 'autocheck', visible: true, align: 'center', width: '80' },
{ label: '规则ID', prop: 'ruleid', visible: true, align: 'center', width: '60' },
{ label: '条件类型', prop: 'checktype', visible: true, align: 'center', width: '150', slot: 'checktype', showOverflowTooltip: false },
{ label: '审核启用', prop: 'logflag', visible: true, align: 'center', width: '80', slot: 'logflag', showOverflowTooltip: false },
{ label: '组合条件(双击弹窗编辑)', prop: 'ruledisplay', visible: true, align: 'center', slot: 'ruledisplay', },
{ label: '记录备注信息', prop: 'logcontent', visible: true, align: 'center', width: '300', slot: 'logcontent', },
{ label: '组合条件', prop: 'checkrules', visible: true, },
{ label: '自动审核', prop: 'autocheck', visible: true, align: 'center', width: '80', slot: 'autocheck', showOverflowTooltip: false },
]);
const tableConfig = ref({
@ -58,13 +149,198 @@ const options = [
{ label: '审核生成报告备注', value: '4' },
]
const selectedRows: any = ref(null)
const searchKey = ref('');
const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const tableRefs = ref()
const currentIndex = ref(0)
const xmList: any = ref([])
const xmColumns = ref([
{ field: 'xmdh', title: '项目代号', align: 'center', resizable: true, width: 100 },
{ field: 'xmmc', title: '项目名称', resizable: true, }
])
const rowHandle = (row: any) => {
checkrulesText.value += '[' + row.xmdh + ']'
ruledisplayText.value += '[' + row.xmmc + ']'
}
const btnClick = (val: string) => {
checkrulesText.value += val
ruledisplayText.value += val
switch (val) {
case '[SEX]':
explanation.value = '性别:性别取值:1=男,2=女,所以如果下条件性别为男则应该if([SEX]=1,,)'
break;
case '[病人来源]':
explanation.value = '病人来源:病人来源,1门诊,3住院,4体检 例如:[病人来源]="1"'
break;
case '[标本类型]':
explanation.value = '标本类型:科室的值需要用双引号引起来,例如:[标本类型]="血清"'
break;
case '[任意项目-ID]':
explanation.value = '任意项目-ID:任意项目ID,指的是LIS项目维护界面的项目代码, 例如:我要判断24小时尿蛋白(代码是:24Pro)的值,[任意项目 - ID] = "24Pro"'
break;
case '[任意项目-数值结果]':
explanation.value = '任意项目-数值结果: 项目维护里面,只有维护了"结果类型"等于"数字型"的项目,才会生效例如表达式:[任意项目-数值结果] > 100'
break;
case '[任意项目-文字结果]':
explanation.value = '任意项目-文字结果: 项目维护里面,只有维护了"结果类型"等于"文字型"或者"数值文字混合型"的项目,才会生效注意,在写表达式的时候,要把数值都用双引号引起来,例如表达式:[任意项目 - 文字结果] = "黄色"或者([任意项目 - 文字结果] like "%++%") 注意使用like条件的时候,使用括号括起来'
break;
case '[任意项目-危急值标志]':
explanation.value = '任意项目-危急值标志:危急值标志的标识有两种,一种是H,代表高,一种是L代表低,例如表达式:[任意项目-危急值标志]="H"'
break;
case '[任意项目-复查标志]':
explanation.value = '任意项目-复查标志:复查标志有就是1,没有就是"",例如表达式:[任意项目-复查标志]="1"'
break;
case '[任意项目-结果标志]':
explanation.value = '任意项目-结果标志:结果标志的值有:H高 ,L低,N阴性,P阳性,Q弱阳性,M正常,为空也是正常例如:[任意项目-结果标志]="H"'
break;
}
}
const subItem = () => {
tableData.value[currentIndex.value].ruledisplay = ruledisplayText.value
tableData.value[currentIndex.value].checkrules = replaceBracketContent(ruledisplayText.value, nameMap);
dialogVisible.value = false
}
const replaceBracketContent = (expression: string, replaceMap: Record<string, string>) => {
// 正则匹配 [任意字符],注意匹配[]内的中文(支持[]前后有空格)
const regex = /\[([^\]]+)\]/g;
// 替换匹配到的内容
const result = expression.replace(regex, (match, content) => {
console.log('==>', match, content);
const trimContent = content.trim();
return replaceMap[trimContent] ? `[${replaceMap[trimContent]}]` : match;
});
return result;
}
const nameMap: Record<string, string> = {
'年龄': 'AGE',
'性别': 'SEX',
'病人来源': 'BRLY',
'科室': 'KSDH',
'标本类型': 'YBLX',
'报告单备注': 'BZ',
'诊断': 'ZD',
'任意项目-数值结果': 'ANY_NUMER',
'任意项目-文字结果': 'ANY_TEXT',
'任意项目-危急值标志': 'ANY_CRI',
'任意项目-复查标志': 'ANY_REV',
'任意项目-结果标志': 'ANY_JGBZ',
'任意项目-ID': 'ANY_ID'
};
const handleAdd = () => {
tableData.value.push({
actions: '0',
ruleid: '',
checktype: '0',
logflag: '0',
ruledisplay: '',
logcontent: '',
checkrules: '',
autocheck: '0',
rptunitid: queryParams.value.rptunitid
});
customTableRef.value.setScrollTop(tableData.value.length);
};
const handleSave = () => {
updateRules(tableData.value).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功');
getList();
}
});
};
const handleDel = () => {
if (!selectedRows.value) return ElMessage.warning('请选择要删除的行');
ElMessageBox.confirm('是否确认删除选中数据?', '提示', {
type: 'warning'
}).then(() => {
if (selectedRows.value.ruleid) {
deleteRules({ id: selectedRows.value.ruleid }).then((res: any) => {
if (res.code == 0) {
ElMessage.success('删除成功');
getList();
selectedRows.value = null
}
});
} else {
tableData.value = tableData.value.filter(item => item.ruleid !== selectedRows.value.ruleid)
selectedRows.value = null
}
})
};
const handleEdit = async (row: any, index: number) => {
currentIndex.value = index
const res = await queryOldxminfo({ yq: queryParams.value.rptunitid });
res.data.forEach((item: any) => {
nameMap[item.xmmc] = item.xmdh;
})
xmList.value = processTableData(res.data)
filterData.value = [...xmList.value]
checkrulesText.value = row.checkrules
ruledisplayText.value = row.ruledisplay
explanation.value = ''
dialogVisible.value = true
}
// 预处理数据:生成拼音信息
const processTableData = (data: object[]) => {
return data.map((item: any) => {
const pinyinMap: Record<string, string> = {};
// 处理label字段拼音
if (item.xmmc) {
pinyinMap.xmmc = getFirstLetter(item.xmmc).toLowerCase();
}
if (item.xmdh) {
pinyinMap.xmdh = getFirstLetter(item.xmdh).toLowerCase();
}
return { ...item, pinyinMap };
});
};
// 搜索过滤逻辑
const filterDataHandle = () => {
const key = searchKey.value.trim().toLowerCase();
if (!key) {
// 空搜索时显示全部预处理数据
filterData.value = [...xmList.value];
return;
}
filterData.value = xmList.value.filter((item: any) => {
// 1. 原文本匹配
const matchLabel = item.xmmc?.toString().toLowerCase().includes(key);
const matchValue = item.xmdh?.toString().toLowerCase().includes(key);
// 2. 拼音匹配
const matchLabelPinyin = item.pinyinMap.xmmc?.includes(key);
const matchValuePinyin = item.pinyinMap.xmdh?.includes(key);
return matchLabel || matchValue || matchLabelPinyin || matchValuePinyin;
});
if (filterData.value.length > 0) {
tableRefs.value.setCurrentRow(filterData.value[0]);
}
};
const getList = () => {
selectedRows.value = null
queryList(queryParams.value).then((res: any) => {
tableData.value = res.data;
});
@ -72,4 +348,18 @@ const getList = () => {
getList()
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.btn_text {
margin: 20px 0;
.tips {
font-size: 16px;
font-family: SourceHanSansCN, SourceHanSansCN;
color: #409eff;
}
}
.btns {
margin-bottom: 10px;
}
</style>