20260118:优化计算公式-新增计算模型页面正则,样式;隐藏产品管理页面轮询任务

This commit is contained in:
George 2026-01-18 10:08:25 +08:00
parent 271e2102db
commit 0a309d5fec
2 changed files with 148 additions and 16 deletions

View File

@ -69,16 +69,16 @@
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改物模型对话框 --> <!-- 添加或修改物模型对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" append-to-body v-dialogDrag> <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="800px" append-to-body v-dialogDrag>
<el-form ref="form" :model="form" :rules="rules" label-width="100px"> <el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="物模型名称" prop="modelName"> <el-form-item label="物模型名称" prop="modelName">
<el-input v-model="form.modelName" placeholder="请输入物模型名称" style="width: 385px" /> <el-input v-model="form.modelName" placeholder="请输入物模型名称" style="width: 580px" />
</el-form-item> </el-form-item>
<el-form-item label="标识符" prop="identifier"> <el-form-item label="标识符" prop="identifier">
<el-input v-model="form.identifier" placeholder="请输入标识符" style="width: 385px" :disabled="!!form.modelId" /> <el-input v-model="form.identifier" placeholder="请输入标识符" style="width: 580px" :disabled="!!form.modelId" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="modelOrder"> <el-form-item label="排序" prop="modelOrder">
<el-input v-model="form.modelOrder" placeholder="请输入排序" type="number" style="width: 385px" /> <el-input v-model="form.modelOrder" placeholder="请输入排序" type="number" style="width: 580px" />
</el-form-item> </el-form-item>
<el-form-item label="模型特性" prop="property"> <el-form-item label="模型特性" prop="property">
<el-checkbox name="isChart" label="图表展示" v-model="form.isChart" :true-label="1" :false-label="0"></el-checkbox> <el-checkbox name="isChart" label="图表展示" v-model="form.isChart" :true-label="1" :false-label="0"></el-checkbox>
@ -107,10 +107,10 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="单位"> <el-form-item label="单位">
<el-input v-model="specsForm.unit" placeholder="请输入单位,如:℃、%" style="width: 385px" /> <el-input v-model="specsForm.unit" placeholder="请输入单位,如:℃、%" style="width: 580px" />
</el-form-item> </el-form-item>
<el-form-item label="步长"> <el-form-item label="步长">
<el-input v-model="specsForm.step" placeholder="请输入步长" type="number" style="width: 385px" /> <el-input v-model="specsForm.step" placeholder="请输入步长" type="number" style="width: 580px" />
</el-form-item> </el-form-item>
</div> </div>
@ -125,7 +125,7 @@
</el-form-item> </el-form-item>
</div> </div>
<el-form-item label="计算公式" prop="formula"> <el-form-item label="计算公式" prop="formula" class="formula-form-item">
<template slot="label"> <template slot="label">
<span slot="label">{{$t('device.device-edit.148398-83')}}</span> <span slot="label">{{$t('device.device-edit.148398-83')}}</span>
<el-tooltip style="cursor: pointer" effect="light" placement="top"> <el-tooltip style="cursor: pointer" effect="light" placement="top">
@ -202,10 +202,11 @@
<style scoped> <style scoped>
/* 公式编辑器容器 */ /* 公式编辑器容器 */
.formula-editor-container { .formula-editor-container {
width: 420px; width: 100%;
max-width: 580px;
border: 1px solid #e4e7ed; border: 1px solid #e4e7ed;
border-radius: 8px; border-radius: 8px;
padding: 14px; padding: 16px;
background: linear-gradient(to bottom, #fafbfc, #ffffff); background: linear-gradient(to bottom, #fafbfc, #ffffff);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
@ -225,7 +226,7 @@
.operator-buttons { .operator-buttons {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: nowrap;
gap: 8px; gap: 8px;
} }
@ -261,9 +262,9 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
max-height: 80px; max-height: 120px;
overflow-y: auto; overflow-y: auto;
padding: 2px 0; padding: 4px 0;
} }
.var-tag { .var-tag {
@ -325,6 +326,47 @@
.clear-btn:hover { .clear-btn:hover {
background-color: #fef0f0; background-color: #fef0f0;
} }
/* 计算公式错误提示样式 - 更加醒目(仅针对计算公式一栏) */
::v-deep .formula-form-item.is-error .el-form-item__error {
font-size: 14px;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #f56c6c 0%, #e64242 100%);
padding: 10px 16px;
border-radius: 6px;
margin-top: 10px;
display: inline-flex;
align-items: center;
box-shadow: 0 4px 12px rgba(245, 108, 108, 0.4);
animation: shake 0.5s ease-in-out;
}
::v-deep .formula-form-item.is-error .el-form-item__error::before {
content: "⚠";
margin-right: 8px;
font-size: 16px;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
20%, 40%, 60%, 80% { transform: translateX(4px); }
}
/* 公式编辑器容器错误状态 */
::v-deep .formula-form-item.is-error .formula-editor-container {
border-color: #f56c6c;
border-width: 2px;
box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.15);
}
/* 公式输入框错误状态边框 */
::v-deep .formula-form-item.is-error .el-textarea__inner {
border-color: #f56c6c;
border-width: 2px;
box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.2);
}
</style> </style>
<script> <script>
@ -392,7 +434,10 @@ export default {
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '以字母开头,只含字母、数字、下划线', trigger: 'blur' }, { pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '以字母开头,只含字母、数字、下划线', trigger: 'blur' },
], ],
datatype: [{ required: true, message: '请选择数据类型', trigger: 'change' }], datatype: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
formula: [{ required: true, message: '请输入计算公式', trigger: 'blur' }], formula: [
{ required: true, message: '请输入计算公式', trigger: 'none' },
{ validator: this.validateFormula, trigger: 'none' },
],
}, },
}; };
}, },
@ -411,6 +456,93 @@ export default {
}, },
}, },
methods: { methods: {
/** 校验计算公式格式 */
validateFormula(rule, value, callback) {
if (!value) {
callback();
return;
}
// 去除空格后校验
const formula = value.replace(/\s+/g, '');
// 1. 校验是否包含非法字符(只允许:数字、小数点、运算符、括号、大括号变量、sqrt)
// 合法字符:0-9 . + - * / % ^ ( ) { } 字母(用于变量和sqrt)
const validCharsPattern = /^[0-9a-zA-Z_+\-*/%^(){}.\s]+$/;
if (!validCharsPattern.test(formula)) {
callback(new Error('公式包含非法字符'));
return;
}
// 2. 校验变量格式:{标识符},标识符以字母开头,只含字母、数字、下划线
const variablePattern = /\{([^}]*)\}/g;
let match;
while ((match = variablePattern.exec(formula)) !== null) {
const varName = match[1];
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(varName)) {
callback(new Error(`变量格式错误: {${varName}},标识符需以字母开头,只含字母、数字、下划线`));
return;
}
}
// 3. 校验括号是否匹配
let parenthesesCount = 0;
let braceCount = 0;
for (const char of formula) {
if (char === '(') parenthesesCount++;
if (char === ')') parenthesesCount--;
if (char === '{') braceCount++;
if (char === '}') braceCount--;
if (parenthesesCount < 0) {
callback(new Error('括号不匹配:存在多余的右括号'));
return;
}
if (braceCount < 0) {
callback(new Error('变量大括号不匹配:存在多余的右大括号'));
return;
}
}
if (parenthesesCount !== 0) {
callback(new Error('括号不匹配:左右括号数量不一致'));
return;
}
if (braceCount !== 0) {
callback(new Error('变量大括号不匹配:左右大括号数量不一致'));
return;
}
// 4. 校验不能以运算符开头(除了负号和左括号)
if (/^[*/%^)]/.test(formula)) {
callback(new Error('公式不能以 * / % ^ ) 开头'));
return;
}
// 5. 校验不能以运算符结尾(除了右括号和右大括号)
if (/[+\-*/%^(]$/.test(formula)) {
callback(new Error('公式不能以运算符结尾'));
return;
}
// 6. 校验不能有连续的运算符(除了负号可以跟在其他运算符后面)
if (/[+\-*/%^]{2,}/.test(formula.replace(/([+*/%^(])-/g, '$1NEG'))) {
callback(new Error('公式中不能有连续的运算符'));
return;
}
// 7. 校验sqrt后面必须跟左括号或数字或变量
if (/sqrt(?![({0-9])/.test(formula)) {
callback(new Error('sqrt后面必须跟括号、数字或变量'));
return;
}
// 8. 校验公式必须包含至少一个变量
if (!/\{[a-zA-Z][a-zA-Z0-9_]*\}/.test(formula)) {
callback(new Error('计算公式必须包含至少一个物模型变量,如:{temperature}'));
return;
}
callback();
},
/** 查询设备计算物模型列表 */ /** 查询设备计算物模型列表 */
getList() { getList() {
if (!this.queryParams.deviceId) return; if (!this.queryParams.deviceId) return;

View File

@ -216,8 +216,8 @@
<product-modbus ref="productModbus" :product="form" @sendPollType="sendPollType" /> <product-modbus ref="productModbus" :product="form" @sendPollType="sendPollType" />
</el-tab-pane> </el-tab-pane>
<!-- 轮询任务 --> <!-- 轮询任务(已隐藏) -->
<el-tab-pane <!-- <el-tab-pane
name="productModbusTask" name="productModbusTask"
:disabled="form.productId == 0" :disabled="form.productId == 0"
v-if="form.deviceType !== 3 && form.deviceType !== 2 && polltype === 0 && (form.protocolCode == 'MODBUS-RTU' || form.protocolCode == 'MODBUS-TCP')" v-if="form.deviceType !== 3 && form.deviceType !== 2 && polltype === 0 && (form.protocolCode == 'MODBUS-RTU' || form.protocolCode == 'MODBUS-TCP')"
@ -225,7 +225,7 @@
> >
<span slot="label">{{ $t('product.product-edit.473153-84') }}</span> <span slot="label">{{ $t('product.product-edit.473153-84') }}</span>
<product-modbus-task ref="productModbusTask" :product="form" @getSendData="getData($event)" /> <product-modbus-task ref="productModbusTask" :product="form" @getSendData="getData($event)" />
</el-tab-pane> </el-tab-pane> -->
<el-tab-pane label="" name="productAuthorize" :disabled="form.productId == 0" v-if="form.deviceType !== 3"> <el-tab-pane label="" name="productAuthorize" :disabled="form.productId == 0" v-if="form.deviceType !== 3">
<span slot="label" v-hasPermi="['iot:authorize:query']">{{ $t('product.product-edit.473153-40') }}</span> <span slot="label" v-hasPermi="['iot:authorize:query']">{{ $t('product.product-edit.473153-40') }}</span>