e6e451b6 by lxs Committed by lihua

数据定价更新

1 parent 3703e25a
......@@ -52,13 +52,11 @@ const dictionaryData: any = ref([]);
const diseaseData: any = ref([]);
const qualityScoreData: any = ref({});
const disScore: any = ref([]);
const buildInData: any = ref([]);
const exportData: any = ref([]);
const dataUsage = ref({
field: '',
dictValue: ''
});
const currModelGuid = ref('');
// 基础设置
const baseConfigFormRef = ref();
const baseConfigFormItems: any = ref([
......@@ -267,7 +265,6 @@ const getDetail = () => {
flowDetail.value = data;
dataTransactionPrice.value = flowDetail.value.dataTransactionPrice;
dataUsage.value.dictValue = data.dataUsage || '';
currModelGuid.value = flowDetail.value.modelGuid;
const mItem = typeMap.value.modelGuid.find(m => m.guid == flowDetail.value.modelGuid);
if (!mItem) {
const mtem = { guid: flowDetail.value.modelGuid, modelName: flowDetail.value.modelName };
......@@ -299,7 +296,7 @@ const getDataTypeList = () => {
// 设置数据字典选项
const setDictFormItems = (dictList) => {
dictList.map(d => {
const dictName = d.targetName;
const dictName = d.dictionaryName;
const dictField = `dict_${d.guid}`;
baseConfigFormItems.value.push({
label: dictName,
......@@ -312,11 +309,11 @@ const setDictFormItems = (dictList) => {
filterable: true,
required: true,
});
baseConfigFormRules.value[dictField] = [{ required: true, trigger: 'change', message: `请选择${dictName}` }];
d.dictionaryName == '数据用途' && (dataUsage.value.field = dictField);
baseConfigFormRules.value[dictField] = { required: true, trigger: 'change', message: `请选择${dictName}` };
dictName == '数据用途' && (dataUsage.value.field = dictField);
(() => {
if (typeMap.value[dictField] == undefined) {
getDataType(d.dictionaryName, dictField)
getDataType(dictName, dictField)
} else {
let item = baseConfigFormItems.value.find(item => item.field == dictField);
item && (item.options = typeMap.value[dictField]);
......@@ -346,7 +343,7 @@ const setDiseaseFormItems = () => {
clearable: true,
required: true,
});
baseConfigFormRules.value.diseaseGuid = [{ required: true, trigger: 'change', message: "请选择所属疾病" }];
baseConfigFormRules.value.diseaseGuid = { required: true, trigger: 'change', message: "请选择所属疾病" };
if (typeMap.value['diseaseGuid'] == undefined) {
getDiseaseData();
} else {
......@@ -369,47 +366,16 @@ const setBuildInFormItems = (buildList) => {
buildList.map(b => {
const buildName = b.targetName;
const buildField = `build_${b.guid}`;
buildInData.value.push({
guid: b.guid,
targetName: buildName,
targetValue: b.defaultValue || '',
isInputParameter: b.isInputParameter,
})
baseConfigFormItems.value.push({
label: buildName,
type: 'input',
placeholder: '',
field: buildField,
default: b.isInputParameter != 'Y' ? changeNum(b.defaultValue, 2) : b.defaultValue != '' && b.defaultValue != null ? parseFloat(b.defaultValue).toFixed(2) : '',
inputType: 'moneyNumber',
maxlength: 18,
default: b.defaultValue || '',
clearable: true,
disabled: b.isInputParameter != 'Y',
required: true
disabled: b.isInputParameter == 'Y'
});
baseConfigFormRules.value[buildField] = [
{ required: true, message: `请填写${buildName}`, trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error(`请填写${buildName}`));
return;
}
const num = parseFloat(value);
if (isNaN(num)) {
callback(new Error('请输入有效的数字'));
return;
}
// 已自动保留两位小数,不需再验证小数位数
if (num < 0 || num > b.defaultValue) {
callback(new Error(`输入值必须在0到${b.defaultValue}之间`));
} else {
callback();
}
}, trigger: "blur",
},
]
baseConfigFormRules.value[buildField] = { required: true, trigger: 'blur', message: `请填写${buildName}` };
})
};
......@@ -452,7 +418,7 @@ const setFormItemData = async () => {
// 添加数据字典
dictionaryList.length > 0 && await setDictFormItems(dictionaryList);
// 添加内置指标
buildInList.length > 0 && await setBuildInFormItems(buildInList);
// buildInList.length > 0 && await setBuildInFormItems(buildInList);
setTimeout(() => {
baseConfigFormRef.value.ruleFormRef?.clearValidate();
......@@ -643,7 +609,6 @@ const getResourceInfo = (sGuid) => {
}
}
// 需求表字段匹配
const matchTableFields = (rData, tData) => {
rData.dataFields.map(t => {
const match = tData.find(d => d.chName == t.fieldName);
......@@ -658,7 +623,9 @@ const matchTableFields = (rData, tData) => {
}, 0);
}
const setRowData = (rowData, dGuid, detailDataTable) => {
const setTableRowData = (dGuid, rIndex) => {
let rowData = tableData.value[rIndex];
if (guid && dGuid == rowData.dataTableGuid) {
const pricingDemandField = detailDataTable?.pricingDemandFieldRQVOS || [];
rowData.dataFields.map(f => {
......@@ -687,7 +654,86 @@ const setTableRowData = (dGuid, rIndex, setRow = true) => {
tableLoading.value = false;
if (res.code == proxy.$passCode) {
const data = res.data || {};
const damTableField = data.damCatalogTableField || [];
// const damTableField = data.damCatalogTableField || [];
const damTableField = [
{
"guid": "dec5a8a8b4bc4ba0b879105f89c1b245",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_id",
"chName": "编号",
"isRequired": "Y",
"orderNum": 1,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "28c1d258fca448e6b22f59c0c6e566e6",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_xm",
"chName": "姓名缩写",
"isRequired": "Y",
"orderNum": 4,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "08b27a077049434bbdee0c6ab51c8b1a",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_sj",
"chName": "时间",
"isRequired": "Y",
"orderNum": 5,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "a0d536a24fb64102acdd3f142f5eda77",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_sf",
"chName": "随访次数",
"isRequired": "Y",
"orderNum": 6,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "5402d6915458404aa59115fbff40c965",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_sg",
"chName": "身高",
"isRequired": "Y",
"orderNum": 7,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "752ee34845044ad2bbe30f5b738a1ef0",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_igf",
"chName": "IGF-1",
"isRequired": "Y",
"orderNum": 8,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
},
{
"guid": "8dce74c208184f958f0ac8587fe66839",
"tenantGuid": "f150638598f74d1bb2d9c542aaf2296c",
"relationMenuGuid": "4c2042543d3c413c851f483c3d5e854a",
"enName": "patient_tz",
"chName": "体重",
"isRequired": "Y",
"orderNum": 8,
"bizState": "Y",
"createTime": "2025-07-01 13:09:46"
}
];
const damFieldOptions = damTableField.map(d => {
return {
...d,
......@@ -699,9 +745,8 @@ const setTableRowData = (dGuid, rIndex, setRow = true) => {
t.damFieldTable = JSON.parse(JSON.stringify(damFieldOptions));
})
// 匹配
if (!guid || (guid && (dGuid != currDataTableGuid || currModelGuid.value != flowDetail.value.modelGuid))) {
matchTableFields(rowData, damTableField);
}
!guid && matchTableFields(rowData, damTableField);
// console.log('rowData', rowData)
} else {
proxy.$ElMessage.error(res.msg);
}
......@@ -818,7 +863,34 @@ const toPath = () => {
})
}
// 获取疾病得分
// 获取维度公式计算结果
const getSignatory = (row) => {
let formulaVal = 0;
const pricingTargetData = row.pricingTargetRSVOS || [];
if (!row.computationalFormula || row.computationalFormula == 'custom') {
let formula = row.customize;
// 遍历数组,检查 customize 是否包含对应的 targetName,若包含则替换为 tNum
pricingTargetData.forEach((item) => {
if (formula.includes(item.targetName)) {
formula = formula.replace(new RegExp(item.targetName, 'g'), item.tNum);
}
});
// 使用 eval 计算公式结果(注意:eval 存在安全风险,仅适用于受控环境)
try {
formulaVal = eval(formula);
} catch (error) {
console.error('公式计算错误:', error);
}
} else {
const formula = pricingTargetData.map(item => item.tNum);
if (row.computationalFormula == '3') {
formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) * parseFloat(currentValue), 1); // 初始值为1
} else {
formulaVal = formula.reduce((accumulator, currentValue) => parseFloat(accumulator) + parseFloat(currentValue), 0); // 初始值为0
}
}
return (Math.round(formulaVal * 100) / 100).toFixed(2);
};
const getTargetNum = (params) => {
// loading.value = true;
getPriceResult(params).then((res: any) => {
......@@ -834,6 +906,151 @@ const getTargetNum = (params) => {
});
}
// 生成报告内容
const reporting = (formInfo) => {
let resultInfo: any = [];
const signatoryData = JSON.parse(JSON.stringify(modelData.value.pricingDimensionalityRSVOS || '[]'));
signatoryData.map((sign, s) => {
resultInfo.push({
dimensionalityName: sign.dimensionalityName,
computationalFormula: sign.computationalFormula,
customize: sign.customize,
pricingTargetRSVOS: []
});
const targets = sign.pricingTargetRSVOS || [];
const signTargets = targets.map(t => {
let tNum: any = 0, tCustomize = '';
if (t.targetType == '3') { // 指标类型-数据字典
const tName = dictionaryData.value.find(d => d.guid == t.guid) ? `dict_${t.guid}` : '';
if (tName) {
const pVal = typeMap.value[tName].find(t => t.value == formInfo[tName]);
const dictionary = t.dictionaryJson.find(d => d.name == pVal.label);
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * parseFloat(dictionary?.value || t.defaultValue || 0);
tCustomize = `权重${parseFloat(t.weight) / 100} * 因子/默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
} else { // 其他
tNum = parseFloat(dictionary?.value || t.defaultValue || 0);
tCustomize = `默认值${parseFloat(dictionary?.value || t.defaultValue || 0)}`;
}
t.dictionaryName == '数据用途' && (dataUsage.value.dictValue = pVal.value || '');
}
} else if (t.targetType == '2') {// 指标类型-系统功能
if (t.functionName == '1') { // 功能名称-质量评价模型
const score = parseFloat(qualityScoreData.value.qualityScore || 0);
tNum = parseFloat(t.weight || 1) / 100 * score / 100;
tCustomize = `权重${parseFloat(t.weight || 1) / 100} * 模型评分${score}/100`;
} else if (t.functionName == '2') { // 功能名称-疾病管理
if (sign.computationalFormula == '1') {// 加权平均
const score = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
tNum = parseFloat(t.weight) / 100 * score;
tCustomize = `权重${parseFloat(t.weight) / 100} * 疾病得分${score}`;
} else { //其他
tNum = parseFloat(disScore.value.find(d => d.guid == t.guid)?.factor || 0);
tCustomize = `疾病得分${tNum}`;
}
} else if (t.functionName == '3') {// 功能名称-需求表管理
const tData = tableData.value.find(f => f.demandTableGuid == t.demandTableGuid || f.guid == t.demandTableGuid);
if (tData) {
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * (parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0));
tCustomize = `权重${parseFloat(t.weight) / 100} * 匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
} else { //其他
tNum = parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0);
tCustomize = `匹配率/默认值${parseFloat(tData.dataFieldsNum) / tData.dataFields.length || parseFloat(t.defaultValue || 0)}`;
}
}
}
} else { // 指标类型-系统内置
if (sign.computationalFormula == '1') {// 加权平均
tNum = parseFloat(t.weight) / 100 * parseFloat(t.defaultValue || 0);
tCustomize = `权重${parseFloat(t.weight) / 100} * 默认值${parseFloat(t.defaultValue || 0)}`;
} else { //其他
tNum = parseFloat(t.defaultValue || 0);
tCustomize = `默认值${parseFloat(t.defaultValue || 0)}`;
}
}
t.tNum = (Math.round(parseFloat(tNum) * 100) / 100).toFixed(2);
resultInfo[s].pricingTargetRSVOS.push({
targetName: t.targetName,
targetType: t.targetType,
functionName: t.functionName,
customize: tCustomize,
tNum: t.tNum,
})
return t;
})
sign.pricingTargetRSVOS = signTargets;
sign.sNum = getSignatory(sign);
resultInfo[s].sNum = sign.sNum;
})
// exportData.value = resultInfo;
return { signatoryData, resultInfo };
}
// 计算价格
const calculatePrice = (pData) => {
let modelFormula = modelData.value.modelFormula;
// 1. 移除所有干扰的引号(确保是数学表达式)
modelFormula = modelFormula.replace(/["']/g, "").trim();
// 2. 定义允许的数学运算符和函数
const allowedOperators = /[+\-*/%^() .\d]/;
const mathFunctions = ['sin', 'cos', 'tan', 'log', 'sqrt', 'abs', 'pow'];
// 3. 提取变量名
const variableRegex = /[\u4e00-\u9fa5a-zA-Z_][\u4e00-\u9fa5a-zA-Z0-9_]*/g;
const variableNames = [...new Set(modelFormula.match(variableRegex) || [])];
// 4. 构建变量映射
const variables = {};
variableNames.forEach(name => {
const dim = pData.find(d => d.dimensionalityName === name);
variables[name] = dim ? parseFloat(dim.sNum) || 0 : 0;
});
// 5. 替换变量为数值(考虑边界情况)
let expression = modelFormula;
Object.keys(variables).forEach(name => {
expression = expression.replace(new RegExp(name, 'g'), variables[name]);
});
// 6. 表达式规范化(不丢失括号)
expression = expression
.replace(/\s+/g, '') // 去空格
.replace(/\^/g, '**') // 幂运算转换
.replace(/"|'/g, '') // 去引号(不破坏括号)
.replace(/(\d)\(/g, '$1*(') // 处理隐式乘法
.replace(/\)\(/g, ')*('); // 括号间乘法
// 7. 验证括号配对
const balance = expression.split('').reduce((acc, char) => {
if (char === '(') acc++;
if (char === ')') acc--;
return acc;
}, 0);
if (balance !== 0) {
console.error('括号不匹配');
return NaN;
}
// 8. 安全计算
try {
const result = new Function('return ' + expression)();
const roundedResult = Math.round(parseFloat(result) * 100) / 100;
dataTransactionPrice.value = roundedResult.toFixed(2);
return roundedResult;
} catch (error) {
console.error('计算错误:', {
error,
original: modelFormula,
processed: expression
});
return NaN;
}
};
// 获取定价计算配置参数
const getCalculateParams = (baseConfigFormObj, baseConfigFormInfo) => {
const modelName = typeMap.value.modelGuid.find(d => d.guid == baseConfigFormInfo.modelGuid)?.modelName || '';
......
......@@ -222,7 +222,7 @@ const signatoryFormItems: any = ref([
placeholder: '请输入',
field: 'weight',
default: '',
inputType: 'factorNumber',
inputType: 'scoreNumber',
maxlength: 10,
clearable: true,
},
......@@ -314,7 +314,7 @@ const targetFormItems: any = ref([
placeholder: '请输入',
field: 'weight',
default: '',
inputType: 'factorNumber',
inputType: 'scoreNumber',
maxlength: 10,
clearable: true,
required: true,
......@@ -354,7 +354,7 @@ const targetFormItems: any = ref([
placeholder: '请输入',
field: 'defaultValue',
default: '',
inputType: 'factorNumber',
inputType: 'moneyNumber',
maxlength: 18,
clearable: true,
required: false
......@@ -548,35 +548,44 @@ const drawerInfo: any = ref({
});
const setTableField = (data) => {
tableData.value = [];
const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
const dictionaryJson = dictionaryName && dictionaryName == currTableData.value.dictionaryName ? (currTableData.value.dictionaryJson || []) : [];
// if (dictionaryType.value == '1') {
// const tData = JSON.parse(JSON.stringify(mergeTableData));
// if (dictionaryJson.length) {
// tData.map((item, i) => {
// item.factor = dictionaryJson[i]?.value || '';
// tableData.value = [];
// const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
// const dictionaryJson = dictionaryName && dictionaryName == currTableData.value.dictionaryName ? (currTableData.value.dictionaryJson || []) : [];
// if (dictionaryJson.length) {
// dictionaryJson.map(item => {
// tableData.value.push({
// label: item.name,
// factor: item.value || '',
// })
// }
// tableData.value = tData;
// getMergeRow();
// })
// } else {
if (dictionaryJson.length) {
dictionaryJson.map(item => {
tableData.value.push({
label: item.name,
factor: item.value || '',
})
})
} else {
data.map((item: any) => {
tableData.value.push({
label: item.label,
factor: '',
})
})
}
// data.map((item: any) => {
// tableData.value.push({
// label: item.label,
// factor: '',
// })
// })
// }
tableData.value = [];
const dictionaryName = typeMap.value['dictionaryType'].find(item => item.value == dictionaryType.value)?.label || '';
const dictionaryJson = dictionaryName && dictionaryName == currTableData.value.dictionaryName ? (currTableData.value.dictionaryJson || []) : [];
// 创建已有数据的映射表,以 label 为键
const existingDataMap = {};
if (dictionaryJson.length) {
dictionaryJson.forEach(item => {
existingDataMap[item.name] = item.value || '';
});
}
// 遍历最新数据,如果有匹配的已有数据则使用其值
data.forEach((item: any) => {
tableData.value.push({
label: item.label,
factor: existingDataMap[item.label] || '', // 使用已有值或空字符串
});
});
}
const getDictionaryRuleData = () => {
......@@ -833,6 +842,7 @@ const selectChange = async (val, row, info) => {
await setFormItems(tInfo, 'target');
if (row.field == 'targetType') {
targetFormItems.value[3].default = val == '1' ? 'N' : 'Y';
targetFormItems.value[4].default = val == '1' ? 'N' : 'Y';
}
}
}
......@@ -1266,30 +1276,6 @@ onMounted(() => {
<template v-if="showFactorTable">
<span class="required_mark" style="line-height: 21px;">字典值对应因子</span>
<div class="table_panel">
<!-- <el-table border :data="tableData" :span-method="tableSpanMethod" tooltip-effect="light" style="height: 100%;"
v-if="dictionaryType == '1'">
<el-table-column label="医院等级">
<el-table-column prop="level" label="级别" width="100" />
<el-table-column prop="grade" label="等次" width="100" />
</el-table-column>
<el-table-column prop="factor" label="因子" class-name="edit-col">
<template #default="scope">
<el-input v-model="scope.row.factor" placeholder="请输入"
@change="(val) => inputChange(val, scope, 'factor')"
@input="(val) => inputEventChange(val, scope, 'factor')" />
</template>
</el-table-column>
</el-table>
<el-table border :data="tableData" tooltip-effect="light" style="height: 100%;" v-else>
<el-table-column label="字典名称" prop="label" width="140" />
<el-table-column prop="factor" label="因子" class-name="edit-col">
<template #default="scope">
<el-input v-model="scope.row.factor" placeholder="请输入"
@change="(val) => inputChange(val, scope, 'factor')"
@input="(val) => inputEventChange(val, scope, 'factor')" />
</template>
</el-table-column>
</el-table> -->
<el-table border :data="tableData" tooltip-effect="light" style="height: 100%;">
<el-table-column label="字典名称" prop="label" width="140" />
<el-table-column prop="factor" label="因子" class-name="edit-col">
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!