modbus设置:操作便捷化
This commit is contained in:
parent
4cc487c00f
commit
bfd7a74831
@ -426,11 +426,6 @@
|
||||
"product.product-modbus.562372-44": "Add a single data register",
|
||||
"product.product-modbus.562372-45": "Add multiple data registers",
|
||||
"product.product-modbus.562372-46": "The default address cannot be empty",
|
||||
"product.product-modbus.562372-47": "Register address cannot be empty",
|
||||
"product.product-modbus.562372-48": "Read only (0- No, 1- Yes) cannot be empty",
|
||||
"product.product-modbus.562372-49": "Modbus data type cannot be empty",
|
||||
"product.product-modbus.562372-50": "The number of reads cannot be empty",
|
||||
"product.product-modbus.562372-51": "Register type 1-IO register 2-data register cannot be empty",
|
||||
"product.product-modbus.562372-52": "Save successful",
|
||||
"product.product-modbus.562372-53": "Modified successfully",
|
||||
"product.product-modbus.562372-311": "Positional offset",
|
||||
@ -551,5 +546,21 @@
|
||||
"product.product-things-model.142341-200": "Click symbols to build the formula",
|
||||
"product.product-things-model.142341-201": "Reference product model identifiers",
|
||||
"product.product-things-model.142341-202": "No available identifiers",
|
||||
"product.product-things-model.142341-203": "Add Calculated Model"
|
||||
"product.product-things-model.142341-203": "Add Calculated Model",
|
||||
"product.product-modbus.562372-320": "Batch",
|
||||
"product.product-modbus.562372-321": "Scope: all {n} rows on this page",
|
||||
"product.product-modbus.562372-323": "{n} rows updated",
|
||||
"product.product-modbus.562372-325": "skipped {m} rows (data type mismatch)",
|
||||
"product.product-modbus.562372-326": "Batch delete",
|
||||
"product.product-modbus.562372-327": "Select rows to delete first",
|
||||
"product.product-modbus.562372-328": "The {n} selected rows ({m} already saved) will be removed locally and take effect after you click Save. Continue?",
|
||||
"product.product-modbus.562372-329": "1 (16-bit)",
|
||||
"product.product-modbus.562372-330": "2 (32-bit)",
|
||||
"product.product-modbus.562372-331": "Click \"Edit registers\" to enter edit mode first",
|
||||
"product.product-modbus.562372-333": "Scope: {n} selected rows",
|
||||
"product.product-modbus.562372-334": "Advances by read count · Enter or blur to generate",
|
||||
"product.product-modbus.562372-335": "Generate",
|
||||
"product.product-modbus.562372-336": "Generated {n} addresses: {from} -> {to}",
|
||||
"product.product-modbus.562372-337": "{m} duplicate slave + register address pairs on this page",
|
||||
"product.product-modbus.562372-338": "Product is published; click \"Unpublish\" first to edit registers"
|
||||
}
|
||||
|
||||
@ -426,11 +426,6 @@
|
||||
"product.product-modbus.562372-44": "添加单个数据寄存器",
|
||||
"product.product-modbus.562372-45": "添加多个数据寄存器",
|
||||
"product.product-modbus.562372-46": "默认地址不能为空",
|
||||
"product.product-modbus.562372-47": "寄存器地址不能为空",
|
||||
"product.product-modbus.562372-48": "是否只读(0-否,1-是)不能为空",
|
||||
"product.product-modbus.562372-49": "modbus数据类型不能为空",
|
||||
"product.product-modbus.562372-50": "读取个数不能为空",
|
||||
"product.product-modbus.562372-51": "寄存器类型 1-IO寄存器 2-数据寄存器不能为空",
|
||||
"product.product-modbus.562372-52": "保存成功",
|
||||
"product.product-modbus.562372-53": "修改成功",
|
||||
"product.product-modbus.562372-311": "位偏移量",
|
||||
@ -551,5 +546,21 @@
|
||||
"product.product-things-model.142341-200": "点击符号快速拼接",
|
||||
"product.product-things-model.142341-201": "引用产品物模型标识",
|
||||
"product.product-things-model.142341-202": "暂无可用标识",
|
||||
"product.product-things-model.142341-203": "新增计算模型"
|
||||
"product.product-things-model.142341-203": "新增计算模型",
|
||||
"product.product-modbus.562372-320": "批量",
|
||||
"product.product-modbus.562372-321": "作用:本页 {n} 行",
|
||||
"product.product-modbus.562372-323": "已应用 {n} 行",
|
||||
"product.product-modbus.562372-325": "跳过 {m} 行(数据类型不匹配)",
|
||||
"product.product-modbus.562372-326": "批量删除",
|
||||
"product.product-modbus.562372-327": "请先勾选要删除的行",
|
||||
"product.product-modbus.562372-328": "将删除勾选的 {n} 行(含已保存 {m} 行),确认后需点击保存生效",
|
||||
"product.product-modbus.562372-329": "1(16位)",
|
||||
"product.product-modbus.562372-330": "2(32位)",
|
||||
"product.product-modbus.562372-331": "请先点击「编辑寄存器」进入编辑状态",
|
||||
"product.product-modbus.562372-333": "作用:已勾选 {n} 行",
|
||||
"product.product-modbus.562372-334": "按读取个数递增 · 回车或失焦即生成",
|
||||
"product.product-modbus.562372-335": "一键生成",
|
||||
"product.product-modbus.562372-336": "已生成 {n} 行地址:{from} → {to}",
|
||||
"product.product-modbus.562372-337": "本页有 {m} 处「从机地址 + 寄存器地址」重复,请检查",
|
||||
"product.product-modbus.562372-338": "产品当前为已发布状态,请先点击「取消发布」后再编辑寄存器"
|
||||
}
|
||||
|
||||
254
yack-web/src/views/iot/product/components/modbusColumnBatch.vue
Normal file
254
yack-web/src/views/iot/product/components/modbusColumnBatch.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<el-popover placement="bottom-start" width="240" trigger="click" :disabled="disabled" v-model="visible">
|
||||
<span slot="reference" class="modbus-col-batch-header" :class="{ 'is-disabled': disabled }" @click.stop="handleTriggerClick">
|
||||
<span class="modbus-col-batch-label">{{ label }}</span>
|
||||
<span class="modbus-col-batch-badge" :class="{ 'is-magic': column === 'addressAuto' }" :title="column === 'addressAuto' ? $t('product.product-modbus.562372-335') : ''">
|
||||
<i v-if="column === 'addressAuto'" class="el-icon-magic-stick"></i>
|
||||
<template v-else>{{ $t('product.product-modbus.562372-320') }}</template>
|
||||
</span>
|
||||
</span>
|
||||
<div class="modbus-col-batch-panel">
|
||||
<div class="modbus-col-batch-scope">{{ scopeText }}</div>
|
||||
|
||||
<!-- 从机地址:数字回车 / 步进变化即生效 -->
|
||||
<div v-if="column === 'slave'" class="modbus-col-batch-field">
|
||||
<el-input-number v-model="slaveValue" size="mini" :min="1" :max="256" :label="$t('product.product-modbus.562372-29')" @change="handleInstantApply" @keyup.enter.native="handleNumberEnter"></el-input-number>
|
||||
</div>
|
||||
|
||||
<!-- 是否只读(编码红线:0=只读、1=读写) -->
|
||||
<div v-else-if="column === 'isReadonly'" class="modbus-col-batch-field">
|
||||
<el-radio-group v-model="readonlyValue" @change="handleInstantApply">
|
||||
<el-radio :label="0">{{ $t('product.product-modbus.562372-32') }}</el-radio>
|
||||
<el-radio :label="1">{{ $t('product.product-modbus.562372-33') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- 位偏移量(IO 寄存器):数字回车 / 步进变化即生效 -->
|
||||
<div v-else-if="column === 'bitOrder'" class="modbus-col-batch-field">
|
||||
<el-input-number v-model="bitOrderValue" size="mini" :min="0" :max="15" :label="$t('product.product-modbus.562372-311')" @change="handleInstantApply" @keyup.enter.native="handleNumberEnter"></el-input-number>
|
||||
</div>
|
||||
|
||||
<!-- 数据类型:选中即写入并联动读取个数,32 位类型在选项上标出 · 2 -->
|
||||
<div v-else-if="column === 'dataType'" class="modbus-col-batch-field">
|
||||
<el-select v-model="dataTypeValue" size="mini" :placeholder="$t('product.product-modbus.562372-42')" @change="handleInstantApply">
|
||||
<el-option v-for="dict in dict.type.iot_modbus_data_type" :key="dict.value" :label="dictOptionLabel(dict)" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- 读取个数:选中即写入,数据类型不匹配的行跳过 -->
|
||||
<div v-else-if="column === 'quantity'" class="modbus-col-batch-field">
|
||||
<el-radio-group v-model="quantityValue" @change="handleInstantApply">
|
||||
<el-radio :label="1">{{ $t('product.product-modbus.562372-329') }}</el-radio>
|
||||
<el-radio :label="2">{{ $t('product.product-modbus.562372-330') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="modbus-col-batch-tip" v-if="quantityValue && quantitySkipCount > 0">
|
||||
{{ $t('product.product-modbus.562372-325', { m: quantitySkipCount }) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 寄存器地址一键生成:填起始地址,回车(或改完失焦)即按各行读取个数依次生成 -->
|
||||
<div v-else-if="column === 'addressAuto'" class="modbus-col-batch-field">
|
||||
<el-input
|
||||
v-model.number="startAddressValue"
|
||||
size="mini"
|
||||
:placeholder="$t('product.product-modbus.562372-30')"
|
||||
@change="handleGenerateAddress"
|
||||
@keyup.enter.native="handleGenerateAddress"
|
||||
></el-input>
|
||||
<div class="modbus-col-batch-tip is-hint">{{ $t('product.product-modbus.562372-334') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deriveQuantity } from '../modbus-derive';
|
||||
|
||||
/**
|
||||
* 列头批量面板:一列一个入口,谁的值谁负责。
|
||||
* 交互约定(2026-10-06 简化):
|
||||
* - 整个列头即入口(列名 + 「批量」角标),点哪都能开;
|
||||
* - **选择即生效**:下拉/单选选中即写入并收起;数字回车或步进变化即写入(无「应用」按钮);
|
||||
* - 作用域由父组件通过 scopeRows + scopeMode 传入(有勾选行→勾选行,否则本页全部行)。
|
||||
*/
|
||||
export default {
|
||||
name: 'modbus-column-batch',
|
||||
dicts: ['iot_modbus_data_type'],
|
||||
props: {
|
||||
// 列头显示名(父组件传入 i18n 文案)
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 列标识:slave / isReadonly / bitOrder / dataType / quantity
|
||||
column: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
// 仅编辑态可操作
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 禁用时的提示文案(由父组件按真实原因给出:产品已发布 / 未进入编辑态)
|
||||
disabledTip: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 作用行:勾选行优先,否则本页全部行(父组件算好传入)
|
||||
scopeRows: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 作用域来源:all=本页全部行,selected=勾选行
|
||||
scopeMode: {
|
||||
type: String,
|
||||
default: 'all',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
// 本次打开面板是否已提交(防止 input-number 的 change 与 keyup.enter 双触发)
|
||||
applied: false,
|
||||
// 初值与"混合态"一致用 undefined(el-input-number 会把 null 解析成 0)
|
||||
slaveValue: undefined,
|
||||
readonlyValue: undefined,
|
||||
bitOrderValue: undefined,
|
||||
dataTypeValue: undefined,
|
||||
quantityValue: undefined,
|
||||
startAddressValue: undefined,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
scopeText() {
|
||||
const key = this.scopeMode === 'selected' ? 'product.product-modbus.562372-333' : 'product.product-modbus.562372-321';
|
||||
return this.$t(key, { n: this.scopeRows.length });
|
||||
},
|
||||
// 数据类型非空且派生读取个数与所选值不符的行数(这些行将跳过,不写出自相矛盾的行)
|
||||
quantitySkipCount() {
|
||||
if (this.column !== 'quantity' || !this.quantityValue) return 0;
|
||||
return this.scopeRows.filter((row) => row.dataType && deriveQuantity(row.dataType) !== this.quantityValue).length;
|
||||
},
|
||||
// 一键生成需要明确的起始地址(0 是合法值,不能用真值判断)
|
||||
canGenerate() {
|
||||
const value = this.startAddressValue;
|
||||
if (value === undefined || value === null || value === '') return false;
|
||||
const num = Number(value);
|
||||
return !isNaN(num) && num >= 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 打开时预填作用行的公共值:全部一致就显示该值,不一致则留空(提示值为混合态)
|
||||
visible(val) {
|
||||
if (val) this.syncFromScope();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncFromScope() {
|
||||
this.applied = false;
|
||||
const pick = (field) => {
|
||||
const rows = this.scopeRows;
|
||||
if (!rows.length) return undefined;
|
||||
const first = rows[0][field];
|
||||
return rows.every((row) => row[field] === first) ? first : undefined;
|
||||
};
|
||||
this.slaveValue = pick('slave');
|
||||
this.readonlyValue = pick('isReadonly');
|
||||
this.bitOrderValue = pick('bitOrder');
|
||||
this.dataTypeValue = pick('dataType');
|
||||
this.quantityValue = pick('quantity');
|
||||
// 起始地址预填作用域首行地址(重排时通常保留起点)
|
||||
this.startAddressValue = this.scopeRows.length ? this.scopeRows[0].address : undefined;
|
||||
},
|
||||
// 32 位类型在选项后标出占用的寄存器个数,选之前就知道会写 2
|
||||
dictOptionLabel(dict) {
|
||||
return deriveQuantity(dict.value) === 2 ? `${dict.label} · 2` : dict.label;
|
||||
},
|
||||
handleTriggerClick() {
|
||||
if (this.disabled) {
|
||||
this.$modal.msgWarning(this.disabledTip || this.$t('product.product-modbus.562372-331'));
|
||||
}
|
||||
},
|
||||
// 数字输入框回车:失焦以触发 el-input-number 的提交(它自身不响应回车),值有变化才会 emit change
|
||||
handleNumberEnter(event) {
|
||||
if (event && event.target && event.target.blur) event.target.blur();
|
||||
},
|
||||
// 选择即生效:值变化即提交并收起面板
|
||||
handleInstantApply(value) {
|
||||
if (this.applied) return;
|
||||
if (value === null || value === undefined || value === '') return;
|
||||
this.applied = true;
|
||||
this.$emit('apply', { column: this.column, value });
|
||||
this.visible = false;
|
||||
},
|
||||
// 地址一键生成:起始地址回车、或改完失焦即生成(无需按钮;同一面板只提交一次)
|
||||
// 注意这里用 el-input 而非 el-input-number:后者的 v-model 只在失焦/步进时提交,回车取到的会是旧值
|
||||
handleGenerateAddress() {
|
||||
if (this.applied || !this.canGenerate) return;
|
||||
this.applied = true;
|
||||
this.$emit('apply', { column: this.column, value: Number(this.startAddressValue) });
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.modbus-col-batch-header {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.modbus-col-batch-label {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.modbus-col-batch-badge {
|
||||
margin-left: 4px;
|
||||
font-size: 11px;
|
||||
color: #c0c4cc;
|
||||
line-height: 1;
|
||||
|
||||
&.is-magic {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .modbus-col-batch-badge {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: not-allowed;
|
||||
|
||||
.modbus-col-batch-badge {
|
||||
color: #e4e7ed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modbus-col-batch-panel {
|
||||
.modbus-col-batch-scope {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.modbus-col-batch-field {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.modbus-col-batch-tip {
|
||||
font-size: 12px;
|
||||
color: #e6a23c;
|
||||
line-height: 18px;
|
||||
margin-top: 6px;
|
||||
|
||||
&.is-hint {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
yack-web/src/views/iot/product/modbus-derive.js
Normal file
44
yack-web/src/views/iot/product/modbus-derive.js
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* modbus 配置派生规则(唯一口径,页面与批量面板共用)
|
||||
*
|
||||
* 红线:`读取个数(quantity)` 是 `数据类型(dataType)` 的派生量,不是独立字段。
|
||||
* 后端 ModbusProtocol#handleRead0304 完全按 quantity 分支解析:
|
||||
* quantity==1 → 16 位(CalculateUtils.handleToUnSign16)
|
||||
* quantity==2 → 32 位(CalculateUtils.parseValue)
|
||||
* 其它值 → log.warn 直接丢弃
|
||||
* 字典 iot_modbus_data_type(sql/yack-init.sql:2503-2511):
|
||||
* bit=1 位;ushort/short=16 位 1 个寄存器;long/ulong/float 的 ABCD/CDAB 变体=32 位 2 个寄存器。
|
||||
*/
|
||||
|
||||
/** 32 位数据类型集合(占 2 个寄存器) */
|
||||
export const MODBUS_32BIT_TYPES = ['long-ABCD', 'long-CDAB', 'ulong-ABCD', 'ulong-CDAB', 'float-ABCD', 'float-CDAB'];
|
||||
|
||||
/** 16 位数据类型集合(占 1 个寄存器) */
|
||||
export const MODBUS_16BIT_TYPES = ['ushort', 'short'];
|
||||
|
||||
/**
|
||||
* 由数据类型推导读取个数
|
||||
* @param {string} dataType 字典 iot_modbus_data_type 的值
|
||||
* @returns {number|null} 1 | 2;dataType 为空或未知类型返回 null(不派生,避免把 data_type 为空的历史行改坏)
|
||||
*/
|
||||
export function deriveQuantity(dataType) {
|
||||
if (!dataType) return null;
|
||||
if (dataType === 'bit') return 1;
|
||||
if (MODBUS_16BIT_TYPES.indexOf(dataType) !== -1) return 1;
|
||||
if (MODBUS_32BIT_TYPES.indexOf(dataType) !== -1) return 2;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单行占用的寄存器个数(一 key 生成地址序列时的步长)
|
||||
* dataType 非空 → 以派生值为准(权威);dataType 为空的历史行 → 回退行内 quantity;再缺省 1
|
||||
* @param {object} row modbus 配置行
|
||||
* @returns {number} 1 | 2 | 行内 quantity
|
||||
*/
|
||||
export function resolveQuantity(row) {
|
||||
if (!row) return 1;
|
||||
const derived = deriveQuantity(row.dataType);
|
||||
if (derived !== null) return derived;
|
||||
const quantity = Number(row.quantity);
|
||||
return quantity > 0 ? quantity : 1;
|
||||
}
|
||||
@ -93,7 +93,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table style="margin-bottom: 10px" v-loading="loadingIO" :data="configList" :key="configTableKey" data-key="id" ref="IOTable">
|
||||
<el-table style="margin-bottom: 10px" v-loading="loadingIO" :data="configList" :key="configTableKey" data-key="id" ref="IOTable" @selection-change="handleIoSelectionChange">
|
||||
<el-table-column type="selection" width="45" align="center" :selectable="ioSelectable" />
|
||||
<el-table-column :label="$t('product.product-modbus.562372-24')" align="center" prop="sort"></el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-25')" align="center" prop="identifier">
|
||||
<template slot-scope="scope">
|
||||
@ -102,15 +103,13 @@
|
||||
:disabled="!enableEditIO"
|
||||
v-model="scope.row.identifier"
|
||||
:placeholder="$t('product.product-modbus.562372-26')"
|
||||
:ref="`selectIo${scope.$index}`"
|
||||
size="mini"
|
||||
@change="(e) => updateSelectThingsModel({ newVal: e, oldVal: $refs[`selectIo${scope.$index}`].value, justiceSelect: 'isSelectIo' })"
|
||||
>
|
||||
<el-option key="0" label="" value="" disabled style="width: 300px">
|
||||
<span style="float: left">{{ $t('product.product-modbus.562372-27') }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ $t('product.product-modbus.562372-28') }}</span>
|
||||
</el-option>
|
||||
<el-option v-for="item in thingsModelList" :key="item.identifier" :label="`${item.modelName} (${item.identifier})`" :value="item.identifier" style="width: 300px" :disabled="!item.isSelectIo">
|
||||
<el-option v-for="item in thingsModelList" :key="item.identifier" :label="`${item.modelName} (${item.identifier})`" :value="item.identifier" style="width: 300px" :disabled="isOptionDisabled(item, 'isSelectIo')">
|
||||
<span style="float: left">{{ item.modelName }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.identifier }}</span>
|
||||
</el-option>
|
||||
@ -118,6 +117,9 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-29')" align="center" prop="slave">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-29')" column="slave" :disabled="!enableEditIO" :scope-rows="ioScopeRows" :scope-mode="ioScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('io', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input-number :disabled="!enableEditIO" v-model="scope.row.slave" size="small" :min="1" :max="256" :label="$t('product.product-modbus.562372-29')"></el-input-number>
|
||||
</template>
|
||||
@ -128,19 +130,31 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-31')" align="center" prop="isReadonly">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-31')" column="isReadonly" :disabled="!enableEditIO" :scope-rows="ioScopeRows" :scope-mode="ioScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('io', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-radio-group :disabled="!enableEditIO" size="mini" v-model="scope.row.isReadonly">
|
||||
<el-radio-button label="0">{{ $t('product.product-modbus.562372-32') }}</el-radio-button>
|
||||
<el-radio-button label="1">{{ $t('product.product-modbus.562372-33') }}</el-radio-button>
|
||||
<el-radio-button :label="0">{{ $t('product.product-modbus.562372-32') }}</el-radio-button>
|
||||
<el-radio-button :label="1">{{ $t('product.product-modbus.562372-33') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-311')" align="center" prop="bitOrder">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-311')" column="bitOrder" :disabled="!enableEditIO" :scope-rows="ioScopeRows" :scope-mode="ioScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('io', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input-number :disabled="!enableEditIO" v-model="scope.row.bitOrder" size="small" :min="0" :max="15" :label="$t('product.product-modbus.562372-311')"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-34')" align="center" class-name="small-padding fixed-width">
|
||||
<template slot="header">
|
||||
<span>{{ $t('product.product-modbus.562372-34') }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" :disabled="!enableEditIO" @click="handleBatchDelete('io')" v-hasPermi="['iot:config:remove']">
|
||||
{{ $t('product.product-modbus.562372-326') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="enableEditIO" size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row, scope.$index, configList, 'isSelectIo')" v-hasPermi="['iot:config:remove']">
|
||||
{{ $t('product.product-modbus.562372-35') }}
|
||||
@ -180,14 +194,15 @@
|
||||
<el-button type="primary" plain icon="el-icon-upload" v-if="productInfo.status == 1" size="mini" @click.stop="batchImport('isSelectData')" v-hasPermi="['modbus:config:import']">
|
||||
{{ $t('product.product-modbus.562372-40') }}
|
||||
</el-button>
|
||||
<el-button plain icon="el-icon-download" v-if="productInfo.status == 1" size="mini" @click.stop="exportModbus('isSelectIo')" v-hasPermi="['modbus:config:export']">
|
||||
<el-button plain icon="el-icon-download" v-if="productInfo.status == 1" size="mini" @click.stop="exportModbus('isSelectData')" v-hasPermi="['modbus:config:export']">
|
||||
{{ $t('product.product-modbus.562372-41') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table style="margin-bottom: 10px" v-loading="loadingData" :data="dataModbusList" :key="dataTableKey" ref="Dataable">
|
||||
<el-table style="margin-bottom: 10px" v-loading="loadingData" :data="dataModbusList" :key="dataTableKey" ref="Dataable" @selection-change="handleDataSelectionChange">
|
||||
<el-table-column type="selection" width="45" align="center" :selectable="dataSelectable" />
|
||||
<el-table-column :label="$t('product.product-modbus.562372-24')" align="center" prop="sort"></el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-25')" align="center" prop="identifier" width="250px">
|
||||
<template slot-scope="scope">
|
||||
@ -197,14 +212,12 @@
|
||||
v-model="scope.row.identifier"
|
||||
:placeholder="$t('product.product-modbus.562372-26')"
|
||||
size="mini"
|
||||
:ref="`selectData${scope.$index}`"
|
||||
@change="(e) => updateSelectThingsModel({ newVal: e, oldVal: $refs[`selectData${scope.$index}`].value, justiceSelect: 'isSelectData' })"
|
||||
>
|
||||
<el-option key="0" label="" value="" disabled style="width: 300px">
|
||||
<span style="float: left">{{ $t('product.product-modbus.562372-27') }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ $t('product.product-modbus.562372-28') }}</span>
|
||||
</el-option>
|
||||
<el-option v-for="item in thingsModelList" :key="item.identifier" :label="`${item.modelName} (${item.identifier})`" :value="item.identifier" style="width: 300px" :disabled="!item.isSelectData">
|
||||
<el-option v-for="item in thingsModelList" :key="item.identifier" :label="`${item.modelName} (${item.identifier})`" :value="item.identifier" style="width: 300px" :disabled="isOptionDisabled(item, 'isSelectData')">
|
||||
<!-- 判断是否可选 -->
|
||||
<span style="float: left">{{ item.modelName }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.identifier }}</span>
|
||||
@ -212,26 +225,37 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-29')" align="center" prop="identifier" width="170px">
|
||||
<el-table-column :label="$t('product.product-modbus.562372-29')" align="center" prop="slave" width="170px">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-29')" column="slave" :disabled="!enableEditData" :scope-rows="dataScopeRows" :scope-mode="dataScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('data', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input-number :disabled="!enableEditData" v-model="scope.row.slave" size="small" :min="1" :max="256" :label="$t('product.product-modbus.562372-29')"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-30')" align="center" prop="address" width="170px">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-30')" column="addressAuto" :disabled="!enableEditData" :scope-rows="dataScopeRows" :scope-mode="dataScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('data', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input-number :disabled="!enableEditData" v-model="scope.row.address" size="small" :min="0" :max="400000" :label="$t('product.product-modbus.562372-30')"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-31')" align="center" prop="isReadonly" width="170px">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-31')" column="isReadonly" :disabled="!enableEditData" :scope-rows="dataScopeRows" :scope-mode="dataScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('data', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-radio-group :disabled="!enableEditData" size="mini" v-model="scope.row.isReadonly">
|
||||
<el-radio-button label="0">{{ $t('product.product-modbus.562372-32') }}</el-radio-button>
|
||||
<el-radio-button label="1">{{ $t('product.product-modbus.562372-33') }}</el-radio-button>
|
||||
<el-radio-button :label="0">{{ $t('product.product-modbus.562372-32') }}</el-radio-button>
|
||||
<el-radio-button :label="1">{{ $t('product.product-modbus.562372-33') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<!-- <el-tag v-show="!enableEditData">{{scope.row.isReadonly=='0'?'只读':'读写'}}</el-tag> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-42')" align="center" prop="dataType" width="200px">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-42')" column="dataType" :disabled="!enableEditData" :scope-rows="dataScopeRows" :scope-mode="dataScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('data', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-select :disabled="!enableEditData" size="mini" v-model="scope.row.dataType" :placeholder="$t('product.product-modbus.562372-42')" style="display: inline-block; padding-right: 10px">
|
||||
<el-option v-for="dict in dict.type.iot_modbus_data_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
@ -239,11 +263,20 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-43')" align="center" prop="quantity" width="170px">
|
||||
<template slot="header">
|
||||
<modbus-column-batch :label="$t('product.product-modbus.562372-43')" column="quantity" :disabled="!enableEditData" :scope-rows="dataScopeRows" :scope-mode="dataScopeMode" :disabled-tip="editDisabledTip" @apply="(p) => onColumnApply('data', p)" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input-number :disabled="!enableEditData" v-model="scope.row.quantity" size="small" :min="1" :max="256" :label="$t('product.product-modbus.562372-43')"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('product.product-modbus.562372-34')" align="center" class-name="small-padding fixed-width">
|
||||
<template slot="header">
|
||||
<span>{{ $t('product.product-modbus.562372-34') }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" :disabled="!enableEditData" @click="handleBatchDelete('data')" v-hasPermi="['iot:config:remove']">
|
||||
{{ $t('product.product-modbus.562372-326') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="enableEditData" size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row, scope.$index, dataModbusList, 'isSelectData')" v-hasPermi="['iot:config:remove']">
|
||||
{{ $t('product.product-modbus.562372-35') }}
|
||||
@ -264,7 +297,7 @@
|
||||
</el-row>
|
||||
|
||||
<!-- 选择物模型 -->
|
||||
<things-list ref="thingsListRef" :productId="productId" @productEvent="getThingsData($event)" :justiceSelect="justiceSelect" />
|
||||
<things-list ref="thingsListRef" :productId="productId" @productEvent="getThingsData($event)" :justiceSelect="justiceSelect" :judge-disabled="isOptionDisabled" />
|
||||
<!-- 批量导入 -->
|
||||
<import-batch ref="importBatchRef" :productId="productId" @data-imported="handleDataImport" :justiceSelect="justiceSelect" />
|
||||
</div>
|
||||
@ -275,12 +308,14 @@ import { listConfig, addBatch } from '@/api/iot/modbusConfig';
|
||||
import { getlListModbus } from '@/api/iot/model';
|
||||
import thingsList from './things-model-list';
|
||||
import importBatch from './components/batchImportModbus';
|
||||
import modbusColumnBatch from './components/modbusColumnBatch';
|
||||
import { deriveQuantity, resolveQuantity } from './modbus-derive';
|
||||
import Sortable from 'sortablejs';
|
||||
import { addOrUpdate, getByProductId } from '@/api/iot/params';
|
||||
import { listProductJob } from '@/api/iot/modbusJob';
|
||||
|
||||
export default {
|
||||
name: 'product-modbus-copy',
|
||||
name: 'product-modbus',
|
||||
dicts: ['iot_modbus_data_type', 'iot_yes_no', 'data_collect_type', 'device_status_deter', 'iot_modbus_poll_time'],
|
||||
props: {
|
||||
product: {
|
||||
@ -291,16 +326,16 @@ export default {
|
||||
components: {
|
||||
thingsList,
|
||||
importBatch,
|
||||
modbusColumnBatch,
|
||||
},
|
||||
watch: {
|
||||
// 获取到父组件传递的productId后,刷新列表
|
||||
product: function (newVal, oldVal) {
|
||||
this.productInfo = newVal;
|
||||
if (this.productInfo && this.productInfo.productId != 0) {
|
||||
this.thingsModelParams.productId = this.productInfo.productId;
|
||||
this.queryParamsIO.productId = this.productInfo.productId;
|
||||
this.queryParamsData.productId = this.productInfo.productId;
|
||||
this.productId = this.productInfo.productId;
|
||||
if (newVal && newVal.productId != 0) {
|
||||
this.thingsModelParams.productId = newVal.productId;
|
||||
this.queryParamsIO.productId = newVal.productId;
|
||||
this.queryParamsData.productId = newVal.productId;
|
||||
this.productId = newVal.productId;
|
||||
this.getIOList();
|
||||
this.getDataList();
|
||||
this.getThingsModelList();
|
||||
@ -311,25 +346,26 @@ export default {
|
||||
enableEditIO: function (n, old) {
|
||||
//设置行是否拖拽
|
||||
if (this.sortableIo) this.sortableIo.option('disabled', !n);
|
||||
//关闭编辑刷新页面
|
||||
//关闭编辑刷新页面(保存与取消都会走到这里:重拉数据与物模型可选标记,并清空本次编辑的临时状态)
|
||||
if (!n) {
|
||||
this.getIOList();
|
||||
this.getThingsModelList();
|
||||
}
|
||||
//清空删除数组
|
||||
this.ioSelection = [];
|
||||
this.releasedIo = [];
|
||||
this.delIoIds = [];
|
||||
this.getThingsModelList();
|
||||
}
|
||||
},
|
||||
enableEditData: function (n, old) {
|
||||
//设置行是否拖拽
|
||||
if (this.sortableData) this.sortableData.option('disabled', !n);
|
||||
//关闭编辑刷新页面
|
||||
//关闭编辑刷新页面(保存与取消都会走到这里:重拉数据与物模型可选标记,并清空本次编辑的临时状态)
|
||||
if (!n) {
|
||||
this.getDataList();
|
||||
this.getThingsModelList();
|
||||
}
|
||||
//清空删除数组
|
||||
this.dataSelection = [];
|
||||
this.releasedData = [];
|
||||
this.delDataIds = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@ -337,14 +373,6 @@ export default {
|
||||
// 遮罩层
|
||||
loadingIO: true,
|
||||
loadingData: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// modbus配置表格数据
|
||||
@ -353,10 +381,6 @@ export default {
|
||||
enableEditIO: false,
|
||||
//子设备配置是否可编辑
|
||||
enableSetSlave: false,
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParamsIO: {
|
||||
pageNum: 1,
|
||||
@ -392,10 +416,16 @@ export default {
|
||||
// 数据寄存器遮罩层
|
||||
dataLoading: true,
|
||||
|
||||
//IO表勾选行(列头批量作用域:有勾选=勾选行,否则本页全部行)
|
||||
ioSelection: [],
|
||||
//数据表勾选行
|
||||
dataSelection: [],
|
||||
//本次编辑中被删除行占用的物模型标识符(后端标记要保存后才变,期间放行其重新可选)
|
||||
releasedIo: [],
|
||||
releasedData: [],
|
||||
|
||||
//可选属性
|
||||
thingsModelList: [],
|
||||
//物模型遮罩层
|
||||
thingsLoading: true,
|
||||
//thingsModel
|
||||
thingsModelParams: {
|
||||
pageNum: 1,
|
||||
@ -403,7 +433,6 @@ export default {
|
||||
productId: 0,
|
||||
isCalculated: 0,
|
||||
},
|
||||
thingsTotal: 0,
|
||||
// 表单参数
|
||||
form: {
|
||||
statusDeter: 1,
|
||||
@ -411,15 +440,9 @@ export default {
|
||||
pollType: 0,
|
||||
deterTimer: '300',
|
||||
},
|
||||
productInfo: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
slaveId: [{ required: true, message: this.$t('product.product-modbus.562372-46'), trigger: 'blur' }],
|
||||
address: [{ required: true, message: this.$t('product.product-modbus.562372-47'), trigger: 'blur' }],
|
||||
isReadonly: [{ required: true, message: this.$t('product.product-modbus.562372-48'), trigger: 'blur' }],
|
||||
dataType: [{ required: true, message: this.$t('product.product-modbus.562372-49'), trigger: 'change' }],
|
||||
quantity: [{ required: true, message: this.$t('product.product-modbus.562372-50'), trigger: 'blur' }],
|
||||
type: [{ required: true, message: this.$t('product.product-modbus.562372-51'), trigger: 'change' }],
|
||||
},
|
||||
//IO行拖拽对象
|
||||
sortableIo: null,
|
||||
@ -438,6 +461,35 @@ export default {
|
||||
jobTotal: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
//直接读 prop,不再维护副本(副本只在 watch 触发时同步,一旦脱节:
|
||||
//productInfo 停留为 {} → status==1 判定失败 → 「编辑寄存器」按钮不渲染,而批量入口仍可见,用户只会得到"请先点击编辑寄存器"的无效提示)
|
||||
productInfo() {
|
||||
return this.product || {};
|
||||
},
|
||||
//批量入口禁用原因(提示要指向用户当下真正该做的动作)
|
||||
editDisabledTip() {
|
||||
if (this.productInfo.status != 1) return this.$t('product.product-modbus.562372-338');
|
||||
return this.$t('product.product-modbus.562372-331');
|
||||
},
|
||||
//列头批量作用域:有勾选行用勾选行,否则本页全部行(不再问第二遍)
|
||||
//一律按表格行序返回:el-table 的 selection 是"勾选先后"顺序,地址生成必须按行序
|
||||
ioScopeRows() {
|
||||
const rows = this.ioSelection.length ? this.ioSelection : this.configList;
|
||||
return this.sortByTableOrder(rows, this.configList);
|
||||
},
|
||||
dataScopeRows() {
|
||||
const rows = this.dataSelection.length ? this.dataSelection : this.dataModbusList;
|
||||
return this.sortByTableOrder(rows, this.dataModbusList);
|
||||
},
|
||||
//作用域来源(仅用于面板文案:作用:本页 N 行 / 已勾选 N 行)
|
||||
ioScopeMode() {
|
||||
return this.ioSelection.length ? 'selected' : 'all';
|
||||
},
|
||||
dataScopeMode() {
|
||||
return this.dataSelection.length ? 'selected' : 'all';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.rowDropIo();
|
||||
this.rowDropData();
|
||||
@ -475,35 +527,143 @@ export default {
|
||||
},
|
||||
/** 查询产品物模型列表 */
|
||||
getThingsModelList() {
|
||||
this.thingsLoading = true;
|
||||
getlListModbus(this.thingsModelParams).then((response) => {
|
||||
this.$refs.thingsListRef.modelList = response.rows;
|
||||
this.thingsModelList = response.rows;
|
||||
this.thingsTotal = response.total;
|
||||
this.thingsLoading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
// IO表勾选(仅编辑态可勾选)
|
||||
ioSelectable() {
|
||||
return this.enableEditIO;
|
||||
},
|
||||
// 表单重置
|
||||
reset() {
|
||||
this.form = {
|
||||
statusDeter: 1,
|
||||
slaveId: null,
|
||||
// pollType: '0',
|
||||
deterTime: '300',
|
||||
};
|
||||
this.resetForm('form');
|
||||
// 数据表勾选(仅编辑态可勾选)
|
||||
dataSelectable() {
|
||||
return this.enableEditData;
|
||||
},
|
||||
handleIoSelectionChange(selection) {
|
||||
this.ioSelection = selection;
|
||||
},
|
||||
handleDataSelectionChange(selection) {
|
||||
this.dataSelection = selection;
|
||||
},
|
||||
// 按表格行序重排(传入的已是整个列表时直接返回,不产生新数组)
|
||||
sortByTableOrder(rows, list) {
|
||||
if (rows === list) return rows;
|
||||
return rows.slice().sort((a, b) => list.indexOf(a) - list.indexOf(b));
|
||||
},
|
||||
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map((item) => item.id);
|
||||
this.single = selection.length !== 1;
|
||||
this.multiple = !selection.length;
|
||||
/**
|
||||
* 寄存器地址一键生成:从起始地址开始,按各行占用的寄存器个数(读取个数)依次推进。
|
||||
* 步长口径 = modbus-derive#resolveQuantity:dataType 非空以派生值为准,dataType 为空的历史行回退行内 quantity,再缺省 1。
|
||||
* 仅数据寄存器使用(IO 寄存器同地址按 bitOrder 取位,地址不递增)。
|
||||
*/
|
||||
applyAddressAuto(table, rows, startAddress) {
|
||||
if (!rows.length) return;
|
||||
const list = table === 'io' ? this.configList : this.dataModbusList;
|
||||
const from = Number(startAddress);
|
||||
let cursor = from;
|
||||
let last = from;
|
||||
rows.forEach((row) => {
|
||||
row.address = cursor;
|
||||
last = cursor;
|
||||
cursor += resolveQuantity(row);
|
||||
});
|
||||
this.$modal.msgSuccess(this.$t('product.product-modbus.562372-336', { n: rows.length, from, to: last }));
|
||||
// 冲突检测:同从机地址 + 同寄存器地址会被 ModbusProtocol 分组解析互相覆盖,本页内先提示
|
||||
const seen = Object.create(null);
|
||||
let duplicate = 0;
|
||||
list.forEach((row) => {
|
||||
const key = `${row.slave}-${row.address}`;
|
||||
if (seen[key]) duplicate++;
|
||||
else seen[key] = true;
|
||||
});
|
||||
if (duplicate > 0) {
|
||||
this.$modal.msgWarning(this.$t('product.product-modbus.562372-337', { m: duplicate }));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 物模型「可否选中」派生判定(全表统一口径,不再增量改写 thingsModelList):
|
||||
* ① 本次编辑刚删除释放的标识符放行(后端标记要保存后才变)
|
||||
* ② 当前表内存中已占用的标识符禁用(覆盖未保存的新增行)
|
||||
* ③ 后端回传的 isSelectIo/isSelectData 兜底
|
||||
*/
|
||||
isOptionDisabled(item, justiceSelect) {
|
||||
const released = justiceSelect === 'isSelectIo' ? this.releasedIo : this.releasedData;
|
||||
if (released.indexOf(item.identifier) !== -1) return false;
|
||||
const list = justiceSelect === 'isSelectIo' ? this.configList : this.dataModbusList;
|
||||
if (list.some((row) => row.identifier && row.identifier === item.identifier)) return true;
|
||||
return !item[justiceSelect];
|
||||
},
|
||||
|
||||
/**
|
||||
* 列头批量应用:一列一个入口,谁的值谁负责。
|
||||
* - dataType:写入后联动 quantity = deriveQuantity(dataType)(字典每种类型位宽唯一)
|
||||
* - quantity:仅允许 1/2;dataType 非空且派生值不符的行跳过(避免写出 ModbusProtocol 会丢弃的矛盾行)
|
||||
* - addressAuto:一键生成地址序列(起始地址 + 按各行读取个数递增)
|
||||
* - 其余列直接写
|
||||
*/
|
||||
onColumnApply(table, { column, value }) {
|
||||
const rows = table === 'io' ? this.ioScopeRows : this.dataScopeRows;
|
||||
if (column === 'addressAuto') {
|
||||
this.applyAddressAuto(table, rows, value);
|
||||
return;
|
||||
}
|
||||
let changed = 0;
|
||||
let skipped = 0;
|
||||
rows.forEach((row) => {
|
||||
if (column === 'dataType') {
|
||||
row.dataType = value;
|
||||
const quantity = deriveQuantity(value);
|
||||
if (quantity !== null) row.quantity = quantity;
|
||||
changed++;
|
||||
} else if (column === 'quantity') {
|
||||
if (row.dataType && deriveQuantity(row.dataType) !== value) {
|
||||
skipped++;
|
||||
return;
|
||||
}
|
||||
row.quantity = value;
|
||||
changed++;
|
||||
} else {
|
||||
row[column] = value;
|
||||
changed++;
|
||||
}
|
||||
});
|
||||
if (changed > 0) {
|
||||
this.$modal.msgSuccess(this.$t('product.product-modbus.562372-323', { n: changed }));
|
||||
}
|
||||
if (skipped > 0) {
|
||||
this.$modal.msgWarning(this.$t('product.product-modbus.562372-325', { m: skipped }));
|
||||
}
|
||||
},
|
||||
|
||||
/** 操作列头批量删除:必须先勾选行(删除高危,不做"本页全部"默认),本地删除、保存驱动提交 */
|
||||
handleBatchDelete(table) {
|
||||
const selection = table === 'io' ? this.ioSelection : this.dataSelection;
|
||||
if (!selection.length) {
|
||||
this.$modal.msgWarning(this.$t('product.product-modbus.562372-327'));
|
||||
return;
|
||||
}
|
||||
const savedCount = selection.filter((row) => row.id).length;
|
||||
this.$confirm(this.$t('product.product-modbus.562372-328', { n: selection.length, m: savedCount }), this.$t('product.product-modbus.562372-313'), {
|
||||
confirmButtonText: this.$t('product.product-modbus.562372-314'),
|
||||
cancelButtonText: this.$t('product.product-modbus.562372-315'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
const list = table === 'io' ? this.configList : this.dataModbusList;
|
||||
const delIds = table === 'io' ? this.delIoIds : this.delDataIds;
|
||||
const released = table === 'io' ? this.releasedIo : this.releasedData;
|
||||
selection.forEach((row) => {
|
||||
const index = list.indexOf(row);
|
||||
if (index !== -1) list.splice(index, 1);
|
||||
if (row.id) delIds.push(row.id);
|
||||
if (row.identifier) released.push(row.identifier);
|
||||
});
|
||||
if (table === 'io') this.ioSelection = [];
|
||||
else this.dataSelection = [];
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
/** 新增IO寄存器行操作 */
|
||||
handleAdd() {
|
||||
@ -591,6 +751,11 @@ export default {
|
||||
const dataModbusList = [];
|
||||
this.dataModbusList.forEach((dataModbus, index) => {
|
||||
if (dataModbus.identifier) {
|
||||
//读取个数是数据类型的派生量:提交前回算,但仅当 dataType 非空(历史行 data_type 可空,勿改坏)
|
||||
if (dataModbus.dataType) {
|
||||
const quantity = deriveQuantity(dataModbus.dataType);
|
||||
if (quantity !== null) dataModbus.quantity = quantity;
|
||||
}
|
||||
dataModbus.sort = dataModbusList.length + 1;
|
||||
dataModbusList.push(dataModbus);
|
||||
}
|
||||
@ -608,15 +773,13 @@ export default {
|
||||
this.loadingData = false;
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
/** 删除按钮操作:本地删除 + 收集已保存行 id;释放的物模型占用由 isOptionDisabled 派生判定自动生效 */
|
||||
handleDelete(row, index, list, justiceSelect) {
|
||||
const item = list.splice(index, 1)[0];
|
||||
//如果是删除数据寄存器,并且有id就存id
|
||||
if (justiceSelect == 'isSelectData' && row.id) this.delDataIds.push(row.id);
|
||||
//如果是删除io,并且有id就存id
|
||||
if (justiceSelect == 'isSelectIo' && row.id) this.delIoIds.push(row.id);
|
||||
//处理选项是否可选
|
||||
this.updateSelectThingsModel({ justiceSelect, oldVal: item.identifier });
|
||||
list.splice(index, 1);
|
||||
const delIds = justiceSelect === 'isSelectIo' ? this.delIoIds : this.delDataIds;
|
||||
const released = justiceSelect === 'isSelectIo' ? this.releasedIo : this.releasedData;
|
||||
if (row.id) delIds.push(row.id);
|
||||
if (row.identifier) released.push(row.identifier);
|
||||
},
|
||||
|
||||
/*获取选择的物模型*/
|
||||
@ -635,7 +798,6 @@ export default {
|
||||
type: this.justiceSelect == 'isSelectData' ? 2 : 1,
|
||||
sort: list.length + 1,
|
||||
});
|
||||
this.updateSelectThingsModel({ justiceSelect: this.justiceSelect, newVal: id });
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -684,16 +846,6 @@ export default {
|
||||
//将选择中那列查到新位置
|
||||
list.splice(newIndex, 0, curRow);
|
||||
},
|
||||
/**
|
||||
* 更新物模型是否可选
|
||||
*/
|
||||
updateSelectThingsModel({ oldVal, newVal, justiceSelect }) {
|
||||
//直接改前后值
|
||||
const oldIndex = oldVal ? this.thingsModelList.findIndex((thingsModel) => thingsModel.identifier == oldVal) : -1;
|
||||
const newIndex = newVal ? this.thingsModelList.findIndex((thingsModel) => thingsModel.identifier == newVal) : -1;
|
||||
if (oldIndex != -1) this.thingsModelList[oldIndex][justiceSelect] = true;
|
||||
if (newIndex != -1) this.thingsModelList[newIndex][justiceSelect] = false;
|
||||
},
|
||||
/**批量导入 */
|
||||
batchImport(justiceSelect) {
|
||||
this.justiceSelect = justiceSelect;
|
||||
@ -702,7 +854,7 @@ export default {
|
||||
/**导出 */
|
||||
exportModbus(justiceSelect) {
|
||||
const type = justiceSelect == 'isSelectData' ? 2 : 1;
|
||||
const name = justiceSelect == 'isSelectData' ? this.$t('product.product-modbus.562372-17') : this.$t('product.product-modbus.562372-38');
|
||||
const name = justiceSelect == 'isSelectData' ? this.$t('product.product-modbus.562372-38') : this.$t('product.product-modbus.562372-17');
|
||||
this.download('/modbus/config/exportModbus?type=' + type, {}, `${name}_${new Date().getTime()}.xlsx`);
|
||||
},
|
||||
/**获取modbus参数配置*/
|
||||
@ -760,16 +912,13 @@ export default {
|
||||
},
|
||||
/** 查询轮训任务列列表 */
|
||||
getTaskList(productId) {
|
||||
this.loading = true;
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
productId: productId,
|
||||
};
|
||||
listProductJob(params).then((response) => {
|
||||
this.jobList = response.rows;
|
||||
this.jobTotal = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/**取消*/
|
||||
|
||||
@ -46,6 +46,11 @@ export default {
|
||||
justiceSelect: {
|
||||
type: String,
|
||||
default: 'isSelectData'
|
||||
},
|
||||
// 父组件的「可否选中」派生判定(表内内存占用 + 刚删除释放 + 后端标记),与本页下拉同口径
|
||||
judgeDisabled: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -92,10 +97,7 @@ export default {
|
||||
this.selectedList.forEach(selected => {
|
||||
const findIndex = this.modelList.findIndex(model => model.identifier == selected.identifier)
|
||||
if (findIndex != -1) {
|
||||
const model = this.modelList[findIndex]
|
||||
model.isSelectData = false
|
||||
model.isSelectIo = false
|
||||
this.$refs.thingsModelTable.toggleRowSelection(model, true)
|
||||
this.$refs.thingsModelTable.toggleRowSelection(this.modelList[findIndex], true)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -132,8 +134,11 @@ export default {
|
||||
closeDialog() {
|
||||
this.open = false;
|
||||
},
|
||||
//是否可选
|
||||
//是否可选:优先走父组件派生判定(内存占用/刚删除释放/后端标记三合一),无判定时退回后端标记
|
||||
selectable(item) {
|
||||
if (this.judgeDisabled) {
|
||||
return !this.judgeDisabled(item, this.justiceSelect)
|
||||
}
|
||||
return item[this.justiceSelect]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user