Browse Source

feat:[模板管理][update]

lkf
luojie 2 months ago
parent
commit
4cb5141ff0
5 changed files with 446 additions and 10 deletions
  1. +15
    -1
      src/components/Template/CustomTable.vue
  2. +183
    -0
      src/utils/conConverter.js
  3. +223
    -0
      src/utils/volConverter.js
  4. +3
    -0
      src/views/business/comps/template/comps/sp/SP003.vue
  5. +22
    -9
      src/views/business/comps/template/mixins/templateMixin.js

+ 15
- 1
src/components/Template/CustomTable.vue View File

@ -3,6 +3,9 @@
<div class="custom-table-wrapper" :class="{'no-border': !isBorder}">
<div class="custom-table-header" v-if="isBorder">
<div class="custom-table-row">
<div v-if="showSort" class="custom-table-cell header-cell sort-cell">
序号
</div>
<div v-for="(col, index) in columns" :key="index" class="custom-table-cell header-cell"
:style="getCellWidth(col)">
<div class="header-cell-content">
@ -33,6 +36,9 @@
<div class="custom-table-body">
<div v-for="(row, rowIndex) in localDataSource" :key="rowIndex" class="custometable-row">
<div v-if="showSort" class="custom-table-cell body-cell sort-cell">
{{ rowIndex + 1 }}
</div>
<div v-for="(col, colIndex) in columns" :key="colIndex" class="custom-table-cell body-cell"
:style="getCellWidth(col)">
<div class="inner-table-cell">
@ -235,6 +241,10 @@ export default {
type: Boolean,
default: true,
},
showSort: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -734,7 +744,7 @@ export default {
}
}
this.$emit("blur", { rowIndex, colKey, value,dataSource:this.localDataSource, item: this.localDataSource[rowIndex] });
this.$emit("blur", { rowIndex, colKey, value,dataSource:this.localDataSource,headerSelectFields:this.headerSelectFields, item: this.localDataSource[rowIndex] });
},
onSubBlur(rowIndex, colKey, value) {
//
@ -942,4 +952,8 @@ export default {
.mr-5{
margin-right: 5px;
}
.sort-cell{
text-align: center;
width: 100px;
}
</style>

+ 183
- 0
src/utils/conConverter.js View File

@ -0,0 +1,183 @@
/**
* 浓度单位转换 同一类别内转换
*/
class ConcentrationConverter {
// 单位换算系数
unitFactors = {
// 质量单位(以g为基准)
'g': 1,
'mg': 1e-3,
'μg': 1e-6,
'ug': 1e-6, // μg的别名
'ng': 1e-9,
'pg': 1e-12,
'fg': 1e-15,
// 体积单位(以L为基准)
'L': 1,
'mL': 1e-3,
'μL': 1e-6,
'uL': 1e-6, // μL的别名
'nL': 1e-9,
'pL': 1e-12,
'fL': 1e-15,
// 摩尔单位(以mol为基准)
'mol': 1,
'mmol': 1e-3,
'μmol': 1e-6,
'umol': 1e-6, // μmol的别名
'nmol': 1e-9,
'pmol': 1e-12,
'fmol': 1e-15,
// 活性单位(以U为基准)
'U': 1,
'IU': 1, // 假设1 IU = 1 U
'mU': 1e-3,
'mIU': 1e-3,
};
// 支持的浓度单位(按类别分组)
unitCategories = {
'mass': ['mg/mL', 'μg/mL', 'ug/mL', 'ng/mL', 'pg/mL', 'fg/mL', 'g/L', 'mg/L', 'μg/L'],
'mole': ['mol/mL', 'mol/L', 'mmol/L', 'μmol/L', 'umol/L', 'nmol/L', 'pmol/L', 'mmol/mL', 'μmol/mL'],
'activity': ['U/mL', 'IU/mL', 'U/L', 'IU/L']
};
/**
* 规范化单位统一别名
*/
normalizeUnit(unit) {
const aliasMap = {
'ug/mL': 'μg/mL',
'umol/L': 'μmol/L',
'uL': 'μL',
'ul': 'μL',
'IU/mL': 'U/mL',
'IU/L': 'U/L'
};
return aliasMap[unit] || unit;
}
/**
* 获取单位类别
*/
getUnitCategory(unit) {
const normalizedUnit = this.normalizeUnit(unit);
for (const [category, units] of Object.entries(this.unitCategories)) {
if (units.includes(normalizedUnit)) {
return category;
}
}
throw new Error(`不支持的浓度单位: ${unit}`);
}
/**
* 检查两个单位是否属于同一类别
*/
isSameCategory(unit1, unit2) {
try {
const cat1 = this.getUnitCategory(unit1);
const cat2 = this.getUnitCategory(unit2);
return cat1 === cat2;
} catch {
return false;
}
}
/**
* 浓度单位转换核心方法
* @param {number|string} value - 待转换的值
* @param {string} targetUnit - 目标单位
* @returns {number} 转换后的值
*/
convert(value, targetUnit) {
// 1. 解析输入值
let inputValue, inputUnit;
if (typeof value === 'string') {
const match = value.toString().trim().match(/^([\d.]+(?:[eE][+-]?\d+)?)\s*([a-zA-Zμ/]+)$/);
if (!match) {
throw new Error(`输入格式错误: ${value}`);
}
inputValue = parseFloat(match[1]);
inputUnit = this.normalizeUnit(match[2]);
} else if (typeof value === 'number') {
inputValue = value;
inputUnit = ''; // 无量纲
} else {
throw new Error('输入值必须是数字或字符串');
}
const normalizedTargetUnit = this.normalizeUnit(targetUnit);
// 2. 检查是否需要转换
if (!inputUnit) {
return inputValue; // 无量纲输入,直接返回
}
if (inputUnit === normalizedTargetUnit) {
return inputValue; // 相同单位,直接返回
}
// 3. 验证单位类别
if (!this.isSameCategory(inputUnit, normalizedTargetUnit)) {
throw new Error(`单位类别不匹配: ${inputUnit} 不能转换为 ${targetUnit}`);
}
// 4. 执行转换
return this._convertValue(inputValue, inputUnit, normalizedTargetUnit);
}
/**
* 内部转换逻辑
*/
_convertValue(value, fromUnit, toUnit) {
// 解析浓度单位:分子/分母
const [fromNum, fromDen] = fromUnit.split('/');
const [toNum, toDen] = toUnit.split('/');
// 获取各部分的换算系数
const numFactor = this.unitFactors[fromNum] / this.unitFactors[toNum];
const denFactor = this.unitFactors[toDen] / this.unitFactors[fromDen];
// 计算总转换系数
return value * numFactor * denFactor;
}
/**
* 批量转换
*/
convertAll(values, targetUnit) {
if (!Array.isArray(values)) {
throw new Error('输入必须是数组');
}
return values.map(value => this.convert(value, targetUnit));
}
/**
* 验证单位是否支持
*/
isSupportedUnit(unit) {
try {
this.getUnitCategory(unit);
return true;
} catch {
return false;
}
}
}
// 将静态方法绑定到实例上,以便可以直接通过实例调用
export const convertConcentration = new ConcentrationConverter();
// // 将静态方法复制到实例上
// converterInstance.convert = ConcentrationConverter.convert.bind(ConcentrationConverter);
// converterInstance.convertAll = ConcentrationConverter.convertAll.bind(ConcentrationConverter);
// converterInstance.isSupportedUnit = ConcentrationConverter.isSupportedUnit.bind(ConcentrationConverter);
// export const convertConcentration = converterInstance;

+ 223
- 0
src/utils/volConverter.js View File

@ -0,0 +1,223 @@
/**
* 体积单位转换
* 支持的体积单位pL, nL, uL (μL), mL, L
*/
class VolumeConverter {
// 体积单位换算系数(以升L为基准)
unitFactors = {
'L': 1,
'mL': 1e-3, // 毫升
'μL': 1e-6, // 微升
'uL': 1e-6, // 微升(别名)
'nL': 1e-9, // 纳升
'pL': 1e-12 // 皮升
};
// 支持的单位列表
supportedUnits = ['L', 'mL', 'μL', 'uL', 'nL', 'pL'];
/**
* 规范化单位统一别名
*/
normalizeUnit(unit) {
const aliasMap = {
'ul': 'uL',
'Ul': 'uL',
'UL': 'uL',
'μl': 'μL',
'Μl': 'μL',
'ΜL': 'μL'
};
// 转换为标准形式
let normalized = unit;
// 处理μ符号的各种表示
if (unit.toLowerCase() === 'ul' || unit === 'μL' || unit === 'uL') {
normalized = 'μL';
}
// 其他别名处理
else if (aliasMap[unit]) {
normalized = aliasMap[unit];
}
return normalized;
}
/**
* 验证单位是否支持
*/
isSupportedUnit(unit) {
const normalizedUnit = this.normalizeUnit(unit);
return this.supportedUnits.includes(normalizedUnit);
}
/**
* 获取所有支持的单位
*/
getSupportedUnits() {
return [...this.supportedUnits];
}
/**
* 体积单位转换核心方法
* @param {number|string} value - 待转换的值
* @param {string} targetUnit - 目标单位
* @returns {number} 转换后的值
*/
convert(value, targetUnit) {
// 1. 解析输入值
let inputValue, inputUnit;
if (typeof value === 'string') {
const result = this.parseValue(value);
inputValue = result.value;
inputUnit = result.unit;
} else if (typeof value === 'number') {
inputValue = value;
inputUnit = ''; // 无量纲
} else {
throw new Error('输入值必须是数字或字符串');
}
const normalizedTargetUnit = this.normalizeUnit(targetUnit);
// 2. 验证目标单位
if (!this.isSupportedUnit(normalizedTargetUnit)) {
throw new Error(`不支持的单位: ${targetUnit}。支持的单位: ${this.supportedUnits.join(', ')}`);
}
// 3. 检查是否需要转换
if (!inputUnit) {
return inputValue; // 无量纲输入,直接返回
}
if (inputUnit === normalizedTargetUnit) {
return inputValue; // 相同单位,直接返回
}
// 4. 执行转换
return this._convertValue(inputValue, inputUnit, normalizedTargetUnit);
}
/**
* 解析带单位的数值字符串
*/
parseValue(valueStr) {
const str = valueStr.toString().trim();
// 支持科学计数法
const match = str.match(/^([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*([a-zA-Zμ]+)$/);
if (!match) {
throw new Error(`格式错误: ${valueStr}。正确格式如: "10mL", "5.5μL", "1.23e-6L"`);
}
const value = parseFloat(match[1]);
const rawUnit = match[2];
const unit = this.normalizeUnit(rawUnit);
// 验证单位是否支持
if (!this.isSupportedUnit(unit)) {
throw new Error(`不支持的单位: ${rawUnit}。支持的单位: ${this.supportedUnits.join(', ')}`);
}
return { value, unit };
}
/**
* 内部转换逻辑
*/
_convertValue(value, fromUnit, toUnit) {
// 统一处理μL的多种表示
const normalizedFromUnit = this.normalizeUnit(fromUnit);
const normalizedToUnit = this.normalizeUnit(toUnit);
// 获取换算系数
const fromFactor = this.unitFactors[normalizedFromUnit];
const toFactor = this.unitFactors[normalizedToUnit];
if (fromFactor === undefined || toFactor === undefined) {
throw new Error(`单位换算系数未定义: ${fromUnit}${toUnit}`);
}
// 转换公式:value * (fromFactor / toFactor)
return value * (fromFactor / toFactor);
}
/**
* 批量转换
*/
convertAll(values, targetUnit) {
if (!Array.isArray(values)) {
throw new Error('输入必须是数组');
}
return values.map(value => this.convert(value, targetUnit));
}
/**
* 格式化输出
*/
format(value, unit, precision = 6) {
const numValue = typeof value === 'string' ? parseFloat(value) : value;
if (isNaN(numValue)) {
return 'NaN';
}
// 根据数值大小选择合适的显示格式
const absValue = Math.abs(numValue);
if (absValue === 0) {
return `0 ${unit}`;
}
if (absValue < 0.001 || absValue >= 1000000) {
return `${numValue.toExponential(precision)} ${unit}`;
}
// 确定小数位数
let decimalPlaces = precision;
if (absValue < 1) {
decimalPlaces = Math.max(precision, Math.ceil(-Math.log10(absValue)) + 2);
} else if (absValue >= 100) {
decimalPlaces = Math.max(0, precision - 2);
}
return `${numValue.toFixed(decimalPlaces)} ${unit}`;
}
/**
* 获取单位换算关系
*/
getConversionFactor(fromUnit, toUnit) {
const normalizedFromUnit = this.normalizeUnit(fromUnit);
const normalizedToUnit = this.normalizeUnit(toUnit);
if (!this.isSupportedUnit(normalizedFromUnit) || !this.isSupportedUnit(normalizedToUnit)) {
throw new Error('单位不支持');
}
const fromFactor = this.unitFactors[normalizedFromUnit];
const toFactor = this.unitFactors[normalizedToUnit];
return fromFactor / toFactor;
}
/**
* 创建快捷转换方法
*/
createShortcutMethods() {``
const shortcuts = {};
this.supportedUnits.forEach(unit => {
const methodName = `to${unit.replace('μ', 'u')}`;
shortcuts[methodName] = (value) => this.convert(value, unit);
});
return shortcuts;
}
}
export const volumeConverter = new VolumeConverter();

+ 3
- 0
src/views/business/comps/template/comps/sp/SP003.vue View File

@ -51,6 +51,7 @@ import TableOpertaion from "@/components/Template/operation/TableOpertaion.vue";
import { EventBus } from "@/utils/eventBus";
import { addTj, uniqeResource, uniqeResourceOne, addDecimals } from "@/utils/calUnitTools";
import { isCommonUnit } from "@/utils/conTools";
import { convertConcentration } from "@/utils/conConverter";
export default {
name: "SP003",
components: { BaseInfoFormPackage, LineLabel, TableList, Step, CustomTable, SelectReagentDialog, TableOpertaion },
@ -429,6 +430,8 @@ export default {
if (this.fillType === "actFill") {
this.handleUpdateCode(formData);
}
const re = convertConcentration.convert('100mg/mL',"ng/mL")
console.log(re,"res")
},
methods: {
//

+ 22
- 9
src/views/business/comps/template/mixins/templateMixin.js View File

@ -322,10 +322,9 @@ export default {
onHandleBlur(fields) {
const {
key,
effectivePeriodUnit,
effectivePeriod,
codeSTD,
targetStartSolution
targetStartSolution,
subTargetStartSolution,//预设起始源溶液浓度单位
} = fields
const { startDate } = this.formData
if (key === 'codeSTD') {
@ -340,19 +339,24 @@ export default {
this.$refs.stepTableRef.updateDataSource(arr)
} else if (key === 'targetStartSolution') {
//起始溶液体积失焦时,更新目标溶液预计浓度
const arr = this.$refs.stepTableRef?.getDataSource()
const arr = this.$refs.stepTableRef?.getDataSource();
const {headerSelectFields} = this.$refs.stepTableRef?.getFilledFormData();
const params = {
startUnit:subTargetStartSolution,
headerSelectFields
}
arr.forEach((item, rowIndex) => {
this.updateTargetStartSolutionVolume(
rowIndex,
item,
targetStartSolution
targetStartSolution,
params
)
})
}
},
//统一处理table失焦事件
onHandleTableBlur(params) {
const { rowIndex, colKey, value, item, dataSource } = params
const { rowIndex, colKey, value, item, dataSource,headerSelectFields } = params
console.log(params, "params");
if (
colKey === 'targetSolutionVolume' ||
@ -364,10 +368,18 @@ export default {
this.$refs.stepFormPackageRef?.getFormDataByKey(
'targetStartSolution'
)
const subTargetStartSolution =
this.$refs.stepFormPackageRef?.getFormDataByKey(
'subTargetStartSolution'
)
const params = {
startUnit:subTargetStartSolution,
headerSelectFields
}
if (isValueEmpty(volume)) {
this.$message.error('请先选择预设起始源溶液浓度')
} else {
this.updateTargetStartSolutionVolume(item, volume)
this.updateTargetStartSolutionVolume(item, volume,params)
}
} else if (
colKey === 'actStartSolutionVolume' ||
@ -444,7 +456,8 @@ export default {
return this.calcNd(item, targetAcSolution)
},
//更新起始溶液体积时,计算目标溶液预计浓度
updateTargetStartSolutionVolume(item, volume) {
updateTargetStartSolutionVolume(item, volume,unitParams) {
const {startUnit,headerSelectFields} = unitParams
const precision = item.targetStartSolutionVolumePrecision || 0
const concentration = item.targetSolutionConcentration || 0
const targetVolume = item.targetSolutionVolume || 0

Loading…
Cancel
Save