976 lines
49 KiB
Vue
976 lines
49 KiB
Vue
<template>
|
||
<div class="modbus-wrap">
|
||
<div style="margin-bottom: 0px">
|
||
<div class="card-title">
|
||
<div style="display: flex; justify-content: flex-start; margin-bottom: 30px">
|
||
<!-- <svg-icon icon-class="modbus" style="font-size: 20px;margin-top: 4px;margin-right: 10px;" /> -->
|
||
<div class="title" style="font-size: 22px">{{ $t('product.product-modbus.562372-0') }}</div>
|
||
<el-tooltip class="item" effect="dark" :content="$t('product.product-modbus.562372-1')" placement="top">
|
||
<i class="el-icon-question" style="margin-left: 10px; margin-top: 5px; font-size: large" />
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group" style="margin-top: -10px">
|
||
<el-button type="primary" plain icon="el-icon-edit" size="mini" @click="setSlave" v-if="!enableSetSlave && productInfo.status == 1">{{ $t('product.product-modbus.562372-2') }}</el-button>
|
||
<el-button type="primary" plain size="mini" @click="saveSlave" v-if="enableSetSlave" v-hasPermi="['modbus:config:edit']">{{ $t('product.product-modbus.562372-3') }}</el-button>
|
||
<el-button type="info" plain size="mini" @click="cancelSlave" v-if="enableSetSlave">{{ $t('product.product-modbus.562372-4') }}</el-button>
|
||
</div>
|
||
<el-form ref="form" :model="form" label-width="180px" :rules="rules" style="margin-left: -150px">
|
||
<el-form-item label="">
|
||
<el-tooltip placement="top">
|
||
<div slot="content" class="test_div">
|
||
{{ $t('product.product-modbus.562372-6') }}
|
||
<br />
|
||
{{ $t('product.product-modbus.562372-7') }}
|
||
<br />
|
||
{{ $t('product.product-modbus.562372-8') }}
|
||
</div>
|
||
<!-- elementui图标库:显示黑色的问号图标 -->
|
||
<i class="el-icon-question" style="color: dodgerblue" />
|
||
<span>{{ $t('product.product-modbus.562372-5') }}</span>
|
||
</el-tooltip>
|
||
<span style="margin-right: 10px">{{ $t('product.product-modbus.562372-5') }}</span>
|
||
<el-radio-group :disabled="!enableSetSlave" v-model="form.statusDeter">
|
||
<el-radio v-for="dict in dict.type.device_status_deter" :key="dict.value" :label="Number(dict.value)">
|
||
{{ dict.label }}
|
||
</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item v-if="form.statusDeter == '1'" label="">
|
||
<span>
|
||
<el-tooltip placement="top">
|
||
<div slot="content" class="test_div">
|
||
{{ $t('product.product-modbus.562372-10') }}
|
||
</div>
|
||
<!-- elementui图标库:显示黑色的问号图标 -->
|
||
<i class="el-icon-question" style="color: dodgerblue" />
|
||
</el-tooltip>
|
||
</span>
|
||
<span style="margin-right: 10px">{{ $t('product.product-modbus.562372-9') }}</span>
|
||
<el-select :disabled="!enableSetSlave" v-model="form.deterTimer" :placeholder="$t('product.product-modbus.562372-11')">
|
||
<el-option v-for="dict in dict.type.iot_modbus_poll_time" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||
</el-select>
|
||
<span style="font-size: 14px; color: darkgrey">{{ $t('product.product-modbus.562372-12') }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="">
|
||
<span style="margin-right: 10px">{{ $t('product.product-modbus.562372-13') }}</span>
|
||
<el-radio-group :disabled="!enableSetSlave" v-model="form.pollType" @change="changePollType">
|
||
<el-radio v-for="dict in dict.type.data_collect_type" :key="dict.value" :label="Number(dict.value)">{{ dict.label }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="" prop="slaveId">
|
||
<span style="margin-right: 10px">{{ $t('product.product-modbus.562372-14') }}</span>
|
||
<el-input :disabled="!enableSetSlave" v-model="form.slaveId" size="small" :label="$t('product.product-modbus.562372-15')" style="width: 230px" type="number"></el-input>
|
||
<span style="font-size: 14px; color: darkgrey">{{ $t('product.product-modbus.562372-16') }}</span>
|
||
</el-form-item>
|
||
</el-form>
|
||
<el-divider></el-divider>
|
||
</div>
|
||
<!-- IO寄存器 -->
|
||
<div class="head">
|
||
<div class="card-title">
|
||
<div style="display: flex; justify-content: flex-start; margin-bottom: 10px">
|
||
<!-- <svg-icon icon-class="modbus" style="font-size: 20px;margin-top: 4px;margin-right: 10px;" /> -->
|
||
<div class="title" style="font-size: 22px">{{ $t('product.product-modbus.562372-17') }}</div>
|
||
<el-tooltip class="item" effect="dark" :content="$t('product.product-modbus.562372-18')" placement="top">
|
||
<i class="el-icon-question" style="margin-left: 10px; margin-top: 5px; font-size: large" />
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
<div class="button-group">
|
||
<el-button type="primary" plain icon="el-icon-edit" size="mini" @click="editIOModbus" v-if="!enableEditIO && productInfo.status == 1">{{ $t('product.product-modbus.562372-19') }}</el-button>
|
||
<el-button type="primary" plain size="mini" @click="submitFormIO" v-if="enableEditIO" v-hasPermi="['modbus:config:edit']">{{ $t('product.product-modbus.562372-22') }}</el-button>
|
||
<el-button type="info" plain size="mini" @click="handleCancelIO" v-if="enableEditIO">{{ $t('product.product-modbus.562372-23') }}</el-button>
|
||
<div class="right-btns">
|
||
<el-button type="primary" plain icon="el-icon-upload" v-if="productInfo.status == 1" size="mini" @click.stop="batchImport('isSelectIo')" v-hasPermi="['modbus:config:import']">
|
||
{{ $t('product.product-modbus.562372-20') }}
|
||
</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']">
|
||
{{ $t('product.product-modbus.562372-21') }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<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">
|
||
<el-select
|
||
filterable
|
||
:disabled="!enableEditIO"
|
||
v-model="scope.row.identifier"
|
||
:placeholder="$t('product.product-modbus.562372-26')"
|
||
size="mini"
|
||
>
|
||
<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="isOptionDisabled(item, 'isSelectIo')">
|
||
<span style="float: left">{{ item.modelName }}</span>
|
||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.identifier }}</span>
|
||
</el-option>
|
||
</el-select>
|
||
</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>
|
||
</el-table-column>
|
||
<el-table-column :label="$t('product.product-modbus.562372-30')" align="center" prop="address">
|
||
<template slot-scope="scope">
|
||
<el-input-number :disabled="!enableEditIO" 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">
|
||
<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-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') }}
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination v-show="total > 0" :total="total" :page.sync="queryParamsIO.pageNum" :limit.sync="queryParamsIO.pageSize" @pagination="getIOList" />
|
||
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-if="enableEditIO">{{ $t('product.product-modbus.562372-36') }}</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddBatch('isSelectIo')" v-if="enableEditIO">{{ $t('product.product-modbus.562372-37') }}</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 数据寄存器 -->
|
||
<div class="head">
|
||
<el-divider></el-divider>
|
||
<div class="card-title">
|
||
<div style="display: flex; justify-content: flex-start; margin-bottom: 10px">
|
||
<!-- <svg-icon icon-class="modbus" style="font-size: 20px;margin-top: 4px;margin-right: 10px;" /> -->
|
||
<div class="title" style="font-size: 22px">{{ $t('product.product-modbus.562372-38') }}</div>
|
||
<el-tooltip class="item" effect="dark" :content="$t('product.product-modbus.562372-39')" placement="top">
|
||
<i class="el-icon-question" style="margin-left: 10px; margin-top: 5px; font-size: large" />
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
<div class="button-group">
|
||
<el-button type="primary" plain icon="el-icon-edit" size="mini" @click="editDataModbus" v-if="!enableEditData && productInfo.status == 1">{{ $t('product.product-modbus.562372-19') }}</el-button>
|
||
<el-button type="primary" plain size="mini" @click="submitFormData" v-if="enableEditData" v-hasPermi="['modbus:config:edit']">{{ $t('product.product-modbus.562372-22') }}</el-button>
|
||
<el-button type="info" plain size="mini" @click="handleCancelData" v-if="enableEditData">{{ $t('product.product-modbus.562372-23') }}</el-button>
|
||
<div class="right-btns">
|
||
<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('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" @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">
|
||
<el-select
|
||
filterable
|
||
:disabled="!enableEditData"
|
||
v-model="scope.row.identifier"
|
||
:placeholder="$t('product.product-modbus.562372-26')"
|
||
size="mini"
|
||
>
|
||
<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="isOptionDisabled(item, 'isSelectData')">
|
||
<!-- 判断是否可选 -->
|
||
<span style="float: left">{{ item.modelName }}</span>
|
||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.identifier }}</span>
|
||
</el-option>
|
||
</el-select>
|
||
</template>
|
||
</el-table-column>
|
||
<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-group>
|
||
</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>
|
||
</el-select>
|
||
</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') }}
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination v-show="dataTotal > 0" :total="dataTotal" :page.sync="queryParamsData.pageNum" :limit.sync="queryParamsData.pageSize" @pagination="getDataList" />
|
||
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddData" v-if="enableEditData">{{ $t('product.product-modbus.562372-44') }}</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAddBatch('isSelectData')" v-if="enableEditData">{{ $t('product.product-modbus.562372-45') }}</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 选择物模型 -->
|
||
<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>
|
||
</template>
|
||
|
||
<script>
|
||
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',
|
||
dicts: ['iot_modbus_data_type', 'iot_yes_no', 'data_collect_type', 'device_status_deter', 'iot_modbus_poll_time'],
|
||
props: {
|
||
product: {
|
||
type: Object,
|
||
default: null,
|
||
},
|
||
},
|
||
components: {
|
||
thingsList,
|
||
importBatch,
|
||
modbusColumnBatch,
|
||
},
|
||
watch: {
|
||
// 获取到父组件传递的productId后,刷新列表
|
||
product: function (newVal, oldVal) {
|
||
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();
|
||
this.getParams();
|
||
this.sendPollType();
|
||
}
|
||
},
|
||
enableEditIO: function (n, old) {
|
||
//设置行是否拖拽
|
||
if (this.sortableIo) this.sortableIo.option('disabled', !n);
|
||
//关闭编辑刷新页面(保存与取消都会走到这里:重拉数据与物模型可选标记,并清空本次编辑的临时状态)
|
||
if (!n) {
|
||
this.getIOList();
|
||
this.getThingsModelList();
|
||
this.ioSelection = [];
|
||
this.releasedIo = [];
|
||
this.delIoIds = [];
|
||
}
|
||
},
|
||
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() {
|
||
return {
|
||
// 遮罩层
|
||
loadingIO: true,
|
||
loadingData: true,
|
||
// 总条数
|
||
total: 0,
|
||
// modbus配置表格数据
|
||
configList: [],
|
||
//io表格是否可以编辑
|
||
enableEditIO: false,
|
||
//子设备配置是否可编辑
|
||
enableSetSlave: false,
|
||
// 查询参数
|
||
queryParamsIO: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
identifier: null,
|
||
address: null,
|
||
isReadonly: null,
|
||
dataType: null,
|
||
quantity: null,
|
||
type: 1,
|
||
productId: 0,
|
||
},
|
||
// 查询参数
|
||
queryParamsData: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
identifier: null,
|
||
address: null,
|
||
isReadonly: null,
|
||
dataType: null,
|
||
quantity: null,
|
||
type: 2,
|
||
productId: 0,
|
||
},
|
||
//产品id
|
||
productId: 0,
|
||
//数据寄存器
|
||
dataModbusList: [],
|
||
//io表格是否可以编辑
|
||
enableEditData: false,
|
||
//数据寄存器总条数
|
||
dataTotal: 0,
|
||
// 数据寄存器遮罩层
|
||
dataLoading: true,
|
||
|
||
//IO表勾选行(列头批量作用域:有勾选=勾选行,否则本页全部行)
|
||
ioSelection: [],
|
||
//数据表勾选行
|
||
dataSelection: [],
|
||
//本次编辑中被删除行占用的物模型标识符(后端标记要保存后才变,期间放行其重新可选)
|
||
releasedIo: [],
|
||
releasedData: [],
|
||
|
||
//可选属性
|
||
thingsModelList: [],
|
||
//thingsModel
|
||
thingsModelParams: {
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
productId: 0,
|
||
isCalculated: 0,
|
||
},
|
||
// 表单参数
|
||
form: {
|
||
statusDeter: 1,
|
||
slaveId: null,
|
||
pollType: 0,
|
||
deterTimer: '300',
|
||
},
|
||
// 表单校验
|
||
rules: {
|
||
slaveId: [{ required: true, message: this.$t('product.product-modbus.562372-46'), trigger: 'blur' }],
|
||
},
|
||
//IO行拖拽对象
|
||
sortableIo: null,
|
||
//ioTable的key值,主要用于拖拽后重绘
|
||
configTableKey: 0,
|
||
//ioTable删除的id
|
||
delIoIds: [],
|
||
//数据寄存器行拖拽对象
|
||
sortableData: null,
|
||
//dataTable的key值,主要用于拖拽后重绘
|
||
dataTableKey: 1000,
|
||
//dataTable删除的id
|
||
delDataIds: [],
|
||
//判断多选物模型是io还是data,isSelectIo是判断是否可选io的,isSelectData是判断数据寄存器的,都是false不可选,true可选
|
||
justiceSelect: 'isSelectData',
|
||
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();
|
||
this.getTaskList(this.productInfo.productId);
|
||
this.sendPollType();
|
||
},
|
||
methods: {
|
||
/** 查询modbusIO寄存器列表 */
|
||
getIOList() {
|
||
this.loadingIO = true;
|
||
listConfig(this.queryParamsIO).then((response) => {
|
||
this.configList = response.rows;
|
||
this.total = response.total;
|
||
this.loadingIO = false;
|
||
});
|
||
},
|
||
//传值
|
||
sendPollType() {
|
||
this.$emit('sendPollType', this.form.pollType);
|
||
},
|
||
handleDataImport(data) {
|
||
this.configList = data;
|
||
},
|
||
/** 查询modbus数据寄存器列表 */
|
||
getDataList() {
|
||
this.loadingData = true;
|
||
listConfig(this.queryParamsData).then((response) => {
|
||
this.dataModbusList = response.rows;
|
||
this.dataTotal = response.total;
|
||
this.loadingData = false;
|
||
});
|
||
this.$nextTick(() => {
|
||
this.$refs.Dataable.bodyWrapper.scrollTop = 0;
|
||
});
|
||
},
|
||
/** 查询产品物模型列表 */
|
||
getThingsModelList() {
|
||
getlListModbus(this.thingsModelParams).then((response) => {
|
||
this.$refs.thingsListRef.modelList = response.rows;
|
||
this.thingsModelList = response.rows;
|
||
});
|
||
},
|
||
// IO表勾选(仅编辑态可勾选)
|
||
ioSelectable() {
|
||
return this.enableEditIO;
|
||
},
|
||
// 数据表勾选(仅编辑态可勾选)
|
||
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));
|
||
},
|
||
|
||
/**
|
||
* 寄存器地址一键生成:从起始地址开始,按各行占用的寄存器个数(读取个数)依次推进。
|
||
* 步长口径 = 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() {
|
||
var d = {
|
||
identifier: '',
|
||
slave: 1,
|
||
address: 1,
|
||
isReadonly: 1,
|
||
type: 1,
|
||
quantity: 1,
|
||
sort: this.configList.length + 1,
|
||
};
|
||
this.configList.push(d);
|
||
setTimeout(() => {
|
||
this.$refs.IOTable.setCurrentRow(d);
|
||
}, 10);
|
||
},
|
||
|
||
/** 新增数据寄存器行操作 */
|
||
handleAddData() {
|
||
var d = {
|
||
identifier: '',
|
||
slave: 1,
|
||
address: 1,
|
||
isReadonly: 1,
|
||
dataType: 'ushort',
|
||
quantity: 1,
|
||
type: 2,
|
||
sort: this.dataModbusList.length + 1,
|
||
};
|
||
this.dataModbusList.push(d);
|
||
setTimeout(() => {
|
||
this.$refs.Dataable.setCurrentRow(d);
|
||
}, 10);
|
||
},
|
||
|
||
handleAddBatch(justiceSelect) {
|
||
this.justiceSelect = justiceSelect;
|
||
this.$refs.thingsListRef.open = true;
|
||
this.$refs.thingsListRef.selectedList = justiceSelect == 'isSelectData' ? this.dataModbusList : this.configList;
|
||
this.$refs.thingsListRef.getList();
|
||
},
|
||
/**点击编辑IO控制器*/
|
||
editIOModbus() {
|
||
this.enableEditIO = !this.enableEditIO;
|
||
},
|
||
/**点击取消IO寄存器编辑按钮*/
|
||
handleCancelIO() {
|
||
this.enableEditIO = !this.enableEditIO;
|
||
},
|
||
/**点击取消数据寄存器编辑按钮*/
|
||
handleCancelData() {
|
||
this.enableEditData = !this.enableEditData;
|
||
},
|
||
/**点击编辑数据寄存器*/
|
||
editDataModbus() {
|
||
this.enableEditData = !this.enableEditData;
|
||
},
|
||
|
||
/** IO寄存器提交按钮 */
|
||
submitFormIO() {
|
||
const configList = [];
|
||
this.configList.forEach((config, index) => {
|
||
if (config.identifier) {
|
||
config.sort = configList.length + 1;
|
||
configList.push(config);
|
||
}
|
||
});
|
||
this.loadingIO = true;
|
||
const data = { productId: this.productId, configList: configList, delIds: this.delIoIds };
|
||
addBatch(data)
|
||
.then((response) => {
|
||
this.$modal.msgSuccess('保存成功');
|
||
this.getIOList();
|
||
this.open = false;
|
||
this.loadingIO = false;
|
||
this.enableEditIO = false;
|
||
})
|
||
.catch((err) => {
|
||
this.loadingIO = false;
|
||
});
|
||
},
|
||
/** 数据寄存器提交按钮 */
|
||
submitFormData() {
|
||
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);
|
||
}
|
||
});
|
||
this.loadingData = true;
|
||
const data = { productId: this.productId, configList: dataModbusList, delIds: this.delDataIds };
|
||
addBatch(data)
|
||
.then((response) => {
|
||
this.$modal.msgSuccess(this.$t('product.product-modbus.562372-52'));
|
||
this.open = false;
|
||
this.enableEditData = false;
|
||
this.loadingData = false;
|
||
})
|
||
.catch((err) => {
|
||
this.loadingData = false;
|
||
});
|
||
},
|
||
/** 删除按钮操作:本地删除 + 收集已保存行 id;释放的物模型占用由 isOptionDisabled 派生判定自动生效 */
|
||
handleDelete(row, index, list, justiceSelect) {
|
||
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);
|
||
},
|
||
|
||
/*获取选择的物模型*/
|
||
getThingsData(ids) {
|
||
const list = this.justiceSelect == 'isSelectData' ? this.dataModbusList : this.configList;
|
||
ids.forEach((id, index) => {
|
||
const findIndex = list.findIndex((item) => item.identifier == id);
|
||
if (findIndex == -1) {
|
||
list.push({
|
||
identifier: id,
|
||
slave: 1,
|
||
address: 1,
|
||
isReadonly: 1,
|
||
dataType: 'ushort',
|
||
quantity: 1,
|
||
type: this.justiceSelect == 'isSelectData' ? 2 : 1,
|
||
sort: list.length + 1,
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
/**
|
||
* io拖拽行
|
||
*/
|
||
rowDropIo() {
|
||
const tbodyIo = this.$refs.IOTable.$el.children[2].children[0].children[1];
|
||
this.sortableIo = new Sortable(tbodyIo, {
|
||
disabled: true,
|
||
onEnd: ({ newIndex, oldIndex, to }) => {
|
||
this.dealDrop(this.configList, newIndex, oldIndex);
|
||
this.configTableKey++;
|
||
this.sortableIo.destroy();
|
||
this.$nextTick(() => {
|
||
this.rowDropIo();
|
||
this.sortableIo.option('disabled', false);
|
||
});
|
||
},
|
||
});
|
||
},
|
||
/**
|
||
* 数据寄存器拖拽
|
||
*/
|
||
rowDropData() {
|
||
const tbodyData = this.$refs.Dataable.$el.children[2].children[0].children[1];
|
||
this.sortableData = new Sortable(tbodyData, {
|
||
disabled: true,
|
||
onEnd: ({ newIndex, oldIndex, to }) => {
|
||
this.dealDrop(this.dataModbusList, newIndex, oldIndex);
|
||
this.dataTableKey++;
|
||
this.sortableData.destroy();
|
||
this.$nextTick(() => {
|
||
this.rowDropData();
|
||
this.sortableData.option('disabled', false);
|
||
});
|
||
},
|
||
});
|
||
},
|
||
//处理拖拽后的数据
|
||
dealDrop(list, newIndex, oldIndex) {
|
||
if (oldIndex == newIndex) return;
|
||
//找到选中那列,移除出数组
|
||
const curRow = list.splice(oldIndex, 1)[0];
|
||
//将选择中那列查到新位置
|
||
list.splice(newIndex, 0, curRow);
|
||
},
|
||
/**批量导入 */
|
||
batchImport(justiceSelect) {
|
||
this.justiceSelect = justiceSelect;
|
||
this.$refs.importBatchRef.upload.importDeviceDialog = true;
|
||
},
|
||
/**导出 */
|
||
exportModbus(justiceSelect) {
|
||
const type = justiceSelect == 'isSelectData' ? 2 : 1;
|
||
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参数配置*/
|
||
getParams() {
|
||
const params = { productId: this.productId };
|
||
getByProductId(params).then((response) => {
|
||
if (response.data) {
|
||
this.form = response.data;
|
||
this.sendPollType();
|
||
}
|
||
});
|
||
},
|
||
/**编辑modbus配置参数*/
|
||
setSlave() {
|
||
this.enableSetSlave = !this.enableSetSlave;
|
||
},
|
||
|
||
/** 保存modbus配置参数 */
|
||
saveSlave() {
|
||
this.$refs['form'].validate((valid) => {
|
||
if (valid) {
|
||
this.enableSetSlave = !this.enableSetSlave;
|
||
this.form.productId = this.productId;
|
||
addOrUpdate(this.form).then((response) => {
|
||
this.$modal.msgSuccess(this.$t('product.product-modbus.562372-53'));
|
||
});
|
||
}
|
||
});
|
||
this.sendPollType();
|
||
},
|
||
//改变采集方式
|
||
changePollType() {
|
||
this.$nextTick(() => {
|
||
this.getTaskList(this.productInfo.productId);
|
||
});
|
||
setTimeout(() => {
|
||
if (this.form.pollType === 1) {
|
||
if (this.jobTotal > 0) {
|
||
this.$confirm(this.$t('product.product-modbus.562372-312'), 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(() => {})
|
||
.catch(() => {
|
||
this.form.pollType = 0;
|
||
this.$message({
|
||
type: 'info',
|
||
message: this.$t('product.product-modbus.562372-316'),
|
||
});
|
||
});
|
||
}
|
||
}
|
||
}, 500);
|
||
},
|
||
/** 查询轮训任务列列表 */
|
||
getTaskList(productId) {
|
||
const params = {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
productId: productId,
|
||
};
|
||
listProductJob(params).then((response) => {
|
||
this.jobTotal = response.total;
|
||
});
|
||
},
|
||
/**取消*/
|
||
cancelSlave() {
|
||
this.enableSetSlave = !this.enableSetSlave;
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.el-input-number.is-disabled .el-input__inner {
|
||
color: #111315;
|
||
}
|
||
|
||
.el-select .el-input.is-disabled .el-input__inner {
|
||
color: #0c0d0e;
|
||
}
|
||
|
||
.el-radio-button__orig-radio:disabled:checked + .el-radio-button__inner {
|
||
background-color: #409eff !important;
|
||
}
|
||
|
||
.head {
|
||
.head-title {
|
||
margin-bottom: 12px;
|
||
|
||
.title {
|
||
margin-top: 16px;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.tips {
|
||
color: grey;
|
||
line-height: 36px;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.button-group {
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
|
||
.right-btns {
|
||
margin-left: auto;
|
||
}
|
||
}
|
||
}
|
||
|
||
.test_div {
|
||
width: 300px;
|
||
padding: 5px;
|
||
}
|
||
</style>
|