Преглед на файлове

调试模板管理的新增的删除

yanglzh преди 2 години
родител
ревизия
ddfc78bdc3

+ 13 - 1
src/api/device/modbus.ts

@@ -2,6 +2,7 @@ import { get, post } from '/@/utils/request_modbus';
 
 export default {
   getDict: (params: object) => get('/dict', params),
+  getDataId: () => get('/dict/getdataid'),
   channel: {
     getList: (params: object) => get('/device', params),
     addDevice: (data: object) => post('/device/add', data),
@@ -9,6 +10,18 @@ export default {
     editDevice: (data: object) => post('/device/edit', data),
     downloadLog: (number: string) => get('/debug/export_message', { number }),
   },
+  data: {
+    getList: (params: object) => get('/device_template', params),
+    addDeviceTemplate: (data: object) => post('/device_template/add', data),
+    editDeviceTemplate: (data: object) => post('/device_template/edit', data),
+    deleteDeviceTemplate: (data: object) => post('/device_template/delete', data),
+  },
+  area: {
+    getList: (params: object) => get('/data_area', params),
+    addDataArea: (data: object) => post('/data_area/add', data),
+    editDataArea: (data: object) => post('/data_area/edit', data),
+    deleteDataArea: (data: object) => post('/data_area/delete', data),
+  },
   task: {
     getList: (params: object) => get('/device_job', params),
     addDeviceJob: (data: object) => post('/device_job/add', data),
@@ -17,7 +30,6 @@ export default {
   },
   template: {
     getList: (params: object) => get('/template', params),
-    getDataId: (params: object) => get('/dict/getdataid', params),
     addTemplate: (data: object) => post('/template/add', data),
     deleteTemplate: (data: object) => post('/template/delete', data),
     editTemplate: (data: object) => post('/template/edit', data),

+ 143 - 0
src/views/iot/device/template/component/dataAreaDialog.vue

@@ -0,0 +1,143 @@
+<template>
+	<div class="component-container">
+		<div class="filter-container">
+			<el-button class="filter-item" type="primary" icon="el-icon-circle-plus-outline" @click="handleAdd"> 添加数据区 </el-button>
+		</div>
+		<el-table :key="tableKey" v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
+			<el-table-column label="名称" prop="name" align="center">
+				<template slot-scope="{ row }">
+					<el-input v-model="row.name" placeholder="请输入" />
+				</template>
+			</el-table-column>
+			<el-table-column label="起始地址" prop="address" align="center">
+				<template slot-scope="{ row }">
+					<el-input-number v-model="row.address" controls-position="right" :min="0" style="width: 100%" />
+				</template>
+			</el-table-column>
+			<el-table-column label="长度" prop="length" align="center">
+				<template slot-scope="{ row }">
+					<el-input-number v-model="row.length" controls-position="right" :min="0" style="width: 100%" />
+				</template>
+			</el-table-column>
+			<el-table-column label="寄存器区" prop="regArea" align="center">
+				<template slot-scope="{ row }">
+					<el-select v-model="row.regArea">
+						<el-option label="HOLDING" value="holding" />
+						<el-option label="INPUT" value="input" />
+						<el-option label="DISCRETE" value="discrete" />
+						<el-option label="COIL" value="coil" />
+					</el-select>
+				</template>
+			</el-table-column>
+			<el-table-column label="读取属性" prop="" align="center">
+				<template slot-scope="{ row }">
+					<span v-if="row.regArea === 'input' || row.regArea === 'discrete'">只读</span>
+					<span v-else-if="row.regArea === 'holding' || row.regArea === 'coil'">读写</span>
+				</template>
+			</el-table-column>
+			<el-table-column label="备注" prop="remarks" align="center">
+				<template slot-scope="{ row }">
+					<el-input v-model="row.remarks" placeholder="请输入" />
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" align="center" width="150px">
+				<template slot-scope="{ row, $index }">
+					<el-button type="primary" size="mini" @click="handleSubmit(row, $index)"> 保存 </el-button>
+					<el-button type="danger" size="mini" @click="handleDelete(row, $index)"> 删除 </el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+	</div>
+</template>
+
+<script lang="ts">
+import api from '/@/api/device/modbus';
+import { ElMessage } from 'element-plus';
+
+export default {
+	props: {
+		templateNumber: String,
+	},
+	data() {
+		return {
+			tableKey: 0,
+			list: [] as any[],
+			total: 0,
+			listLoading: false,
+			listQuery: {
+				page: 1,
+				size: 20,
+			},
+		};
+	},
+
+	mounted() {
+		this.getList();
+	},
+
+	methods: {
+		getList() {
+			this.listLoading = true;
+			api.template
+				.getList({ template_number: this.templateNumber })
+				.then((res: any) => {
+					this.list = res.list || [];
+					this.total = res.Total;
+				})
+				.finally(() => {
+					this.listLoading = false;
+				});
+		},
+		//
+		handleAdd() {
+			this.list.push({
+				name: '',
+				address: '',
+				length: '',
+				regArea: '',
+				remarks: '',
+				templateNumber: this.templateNumber,
+			});
+		},
+		handleDelete(row: any, index: number) {
+			this.$confirm('是否确认删除数据区名称为"' + row.name + '"的数据项?', '警告', {
+				confirmButtonText: '确定',
+				cancelButtonText: '取消',
+				type: 'warning',
+			}).then(() => {
+				if (row.taId) {
+					api.area.deleteDataArea({ ta_id: row.taId }).then(() => {
+						this.getList();
+						ElMessage.success('删除成功');
+					});
+				} else {
+					this.list.splice(index, 1);
+				}
+			});
+		},
+		handleSubmit(row: any, index: number) {
+			if (row.taId) {
+				api.area.editDataArea(row).then(() => {
+					this.getList();
+					ElMessage.success('操作成功!');
+				});
+			} else {
+				api.area.addDataArea(row).then(() => {
+					this.getList();
+					ElMessage.success('操作成功!');
+				});
+			}
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.filter-container {
+	margin-bottom: 10px;
+}
+
+.filter-item {
+	margin-right: 10px;
+}
+</style>

+ 343 - 0
src/views/iot/device/template/component/deviceTemplateDialog.vue

@@ -0,0 +1,343 @@
+<template>
+	<div class="component-container">
+		<div class="filter-container">
+			<el-button class="filter-item" type="primary" icon="el-icon-circle-plus-outline" @click="openDialog('create')"> 添加变量列表 </el-button>
+		</div>
+		<el-table :key="tableKey" v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
+			<el-table-column label="序号" type="index" width="50" />
+			<!-- <el-table-column label="监控设备" prop="templateNumber" align="center" /> -->
+			<el-table-column label="变量名" prop="title" align="center" />
+			<el-table-column label="变量编码" prop="dataAttribName" align="center" />
+			<el-table-column label="数据地址" prop="dataAddressTitle" align="center" />
+			<el-table-column label="系数" prop="dataCoef" align="center" />
+			<el-table-column label="存盘周期" prop="saveCycle" align="center" />
+			<el-table-column label="操作" align="center" width="200">
+				<template slot-scope="{ row, $index }">
+					<el-button type="primary" size="mini" @click="handleUpdate(row)"> 修改 </el-button>
+					<el-button v-if="row.status != 'deleted'" size="mini" type="danger" @click="handleDelete(row, $index)"> 删除 </el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+
+		<pagination
+			v-show="total > 0"
+			:total="total"
+			:page.sync="listQuery.page"
+			:limit.sync="listQuery.size"
+			@pagination="getList"
+			style="padding: 10px 20px 20px !important"
+		/>
+
+		<el-dialog
+			:title="textMap[dialogStatus]"
+			:visible.sync="dialogVisible"
+			width="850px"
+			:before-close="clsoeDialog"
+			close="var-dialog"
+			append-to-body
+			:close-on-click-modal="false"
+		>
+			<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="80px">
+				<el-row>
+					<el-col :span="8">
+						<el-form-item label="变量名称" prop="title">
+							<el-input v-model="temp.title" placeholder="请输入变量名称" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="变量编码" prop="dataAttribName">
+							<el-input v-model="temp.dataAttribName" placeholder="请输入变量编码" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="单位" prop="unit">
+							<el-input v-model="temp.unit" placeholder="请输入单位" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<div class="label">数据来源</div>
+				<el-row>
+					<el-col :span="8" v-if="mode === 1">
+						<el-form-item label="数据区" prop="area">
+							<el-select v-model="temp.area" placeholder="请选择数据区" filterable class="filter-item" @change="handleAreaChange" style="width: 100%">
+								<el-option v-for="(item, index) in dataAreaOptions" :key="index" :label="item.name" :value="item.name" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8" v-if="mode === 1">
+						<el-form-item label="数据地址" prop="dataAddress">
+							<el-select
+								v-model="temp.dataAddress"
+								placeholder="请选择数据地址"
+								filterable
+								class="filter-item"
+								@change="handleSelectChange"
+								style="width: 100%"
+							>
+								<el-option v-for="(item, index) in dataAddressOptions" :key="index" :label="item.title" :value="item.value" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="8" v-if="mode === 0">
+						<el-form-item label="数据地址" prop="dataAddress">
+							<el-input v-model="temp.dataAddress" placeholder="请输入数据地址" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="数据类型" prop="dataType">
+							<el-select v-model="temp.dataType" placeholder="请选择数据类型" filterable class="filter-item" style="width: 100%">
+								<el-option v-for="(item, index) in dataTypeOptions" :key="index" :label="item.title" :value="item.value" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<div class="label">数据处理</div>
+				<el-row>
+					<el-col :span="8">
+						<el-form-item label="初始值" prop="dataInitial">
+							<el-input v-model="temp.dataInitial" placeholder="请输入" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="基值" prop="dataBaseline">
+							<el-input v-model="temp.dataBaseline" placeholder="请输入" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="8">
+						<el-form-item label="系数" prop="dataCoef">
+							<el-input v-model="temp.dataCoef" placeholder="请输入" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+				<el-row>
+					<el-col :span="8">
+						<el-form-item label="存盘周期" prop="saveCycle">
+							<el-input v-model="temp.saveCycle" placeholder="请输入" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<div slot="footer" class="dialog-footer">
+				<el-button @click="clsoeDialog"> 取 消 </el-button>
+				<el-button type="primary" @click="dialogStatus === 'create' ? createData() : updateData()"> 保 存 </el-button>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { ElMessage } from 'element-plus';
+import api from '/@/api/device/modbus';
+
+export default {
+	props: {
+		templateNumber: '',
+		mode: '',
+	},
+	data() {
+		return {
+			tableKey: 0,
+			list: null,
+			total: 0,
+			listLoading: false,
+			listQuery: {
+				page: 1,
+				size: 20,
+			},
+			temp: {
+				title: '',
+				unit: '',
+				dataAttribName: '',
+				area: '',
+				dataAddress: '',
+				dataAddressTitle: '',
+				dataType: '',
+				dataInitial: 0,
+				dataBaseline: 0,
+				dataCoef: 1,
+				saveCycle: '',
+			},
+			rules: {
+				title: [{ required: true, message: '变量名称', trigger: 'blur' }],
+				dataAttribName: [{ required: true, message: '请输入变量编码', trigger: 'blur' }],
+				area: [{ required: true, message: '请选择数据区', trigger: 'change' }],
+				dataAddress: [{ required: true, message: '请选择数据地址', trigger: 'change' }],
+				dataType: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
+			},
+			dialogVisible: false,
+			dialogStatus: '',
+			textMap: {
+				update: '变量详情',
+				create: '添加变量',
+			},
+			dataAreaOptions: [], // 数据区
+			dataAddressOptions: [], // 数据字典
+			dataTypeOptions: [], // 数据类型
+			normalizer(node: any) {
+				return {
+					id: node.value,
+					label: node.title,
+					children: node.children,
+				};
+			},
+		};
+	},
+
+	mounted() {
+		this.listQuery.template_number = this.templateNumber;
+		this.getDict();
+		this.getList();
+		this.getDataAreaList();
+	},
+
+	methods: {
+		// 获取字典数据
+		getDict() {
+			api.getDict({ code: 'dataType' }).then((res) => {
+				this.dataTypeOptions = res.data.list || [];
+			});
+		},
+		getList() {
+			this.listLoading = true;
+			api.template.getList(this.listQuery)
+				.then((response) => {
+					this.list = response.data.list || [];
+					this.total = response.data.Total;
+				})
+				.finally(() => {
+					this.listLoading = false;
+				});
+		},
+		getDataAreaList() {
+			getDataAreaList({ template_number: this.templateNumber })
+				.then((response) => {
+					this.dataAreaOptions = response.data.list || [];
+				})
+				.finally(() => {});
+		},
+		handleFilter() {
+			// this.listQuery.page = 1
+			this.getList();
+		},
+		handleDelete(row, index) {
+			this.$confirm('是否确认删除变量名称为"' + row.title + '"的数据项?', '警告', {
+				confirmButtonText: '确定',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(function () {
+					return deleteDeviceTemplate({ dt_id: row.dtId });
+				})
+				.then(() => {
+					this.getList();
+					this.msgSuccess('删除成功');
+				})
+				.catch(function () {});
+		},
+		openDialog(dialogStatus, row) {
+			this.dialogStatus = dialogStatus;
+			if (row) {
+				this.temp = Object.assign({}, row);
+				this.handleAreaChange(this.temp.area);
+			}
+			this.temp.templateNumber = this.templateNumber;
+			this.dialogVisible = true;
+			this.$nextTick(() => {
+				this.$refs['dataForm'].clearValidate();
+			});
+		},
+		clsoeDialog() {
+			this.dialogVisible = false;
+			// this.$refs.dataForm.resetFields()
+			this.temp = {
+				title: '',
+				unit: '',
+				dataAttribName: '',
+				area: '',
+				dataAddress: '',
+				dataAddressTitle: '',
+				dataType: '',
+				dataInitial: 0,
+				dataBaseline: 0,
+				dataCoef: 1,
+				saveCycle: '',
+			};
+		},
+		createData() {
+			this.$refs['dataForm'].validate((valid) => {
+				if (valid) {
+					this.handleSelectChange(this.temp.dataAddress);
+					const tempData = Object.assign({}, this.temp);
+					api.data.addDeviceTemplate(tempData).then(() => {
+						this.handleFilter();
+						this.clsoeDialog();
+						ElMessage.success('添加成功!');
+					});
+				}
+			});
+		},
+		handleUpdate(row) {
+			this.openDialog('update', row);
+		},
+		updateData() {
+			this.$refs['dataForm'].validate((valid) => {
+				if (valid) {
+					this.handleSelectChange(this.temp.dataAddress);
+					const tempData = Object.assign({}, this.temp);
+					api.data.editDeviceTemplate(tempData).then(() => {
+						this.handleFilter();
+						this.clsoeDialog();
+						ElMessage.success('操作成功!');
+					});
+				}
+			});
+		},
+		handleAreaChange(value) {
+			let item = this.dataAreaOptions.find((item) => item.name === value);
+			let arr = [];
+			if (!item) {
+				this.dataAddressOptions = arr;
+				return;
+			}
+			let len = item.length || 0;
+			for (let i = 0; i <= len; i++) {
+				arr.push({ title: `${item.name}[${i}]`, value: i });
+			}
+			this.dataAddressOptions = arr;
+		},
+		handleSelectChange(value) {
+			let item = this.dataAddressOptions.find((item) => item.value === value);
+			if (item) {
+				this.temp.dataAddressTitle = item.title;
+			} else {
+				this.temp.dataAddressTitle = this.temp.dataAddress;
+			}
+		},
+	},
+};
+</script>
+
+<style lang="scss" scoped>
+.filter-container {
+	margin-bottom: 10px;
+}
+
+.filter-item {
+	margin-right: 10px;
+}
+
+.label {
+	font-size: 16px;
+	border-bottom: 1px solid #ddd;
+	color: #666;
+	font-weight: bold;
+	padding: 5px 0;
+	margin: 10px;
+}
+
+::v-deep {
+	.el-form-item__label {
+		text-align: right;
+	}
+}
+</style>

+ 135 - 178
src/views/iot/device/template/component/edit.vue

@@ -1,190 +1,147 @@
 <template>
-	<div class="system-edit-dic-container">
-		<el-dialog :title="(ruleForm.id!==0?'修改':'添加')+'设备'" v-model="isShowDialog" width="769px">
-			<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="110px">
-       <el-form-item label="设备标识" prop="key">
-          <el-input v-model="ruleForm.key" placeholder="请输入设备标识" :disabled="ruleForm.id" />
-        </el-form-item>
-        <el-form-item label="设备名称" prop="name">
-          <el-input v-model="ruleForm.name" placeholder="请输入设备名称" />
-        </el-form-item>
-
-        
-
-           <el-form-item label="所属产品" prop="productId">
-       
-                <el-select v-model="ruleForm.productId" placeholder="请选择所属产品" class="w100">
-              <el-option
-                v-for="item in productData"
-                :key="item.id"
-                :label="item.name"
-                :value="item.id"
-              />
-            </el-select>
-            </el-form-item> 
-
-         <el-form-item label="所属部门" prop="deptId">
-              <el-cascader :options="deptData" :props="{ checkStrictly: true,emitPath: false, value: 'deptId', label: 'deptName' }" placeholder="请选择所属部门" clearable class="w100" v-model="ruleForm.deptId">
-                <template #default="{ node, data }">
-                  <span>{{ data.deptName }}</span>
-                  <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-                </template>
-              </el-cascader>
-            </el-form-item> 
-
-           
-        
-        <el-form-item label="设备证书" prop="certificate">
-          <el-input v-model="ruleForm.certificate" placeholder="请输入设备证书" />
-        </el-form-item>
-
-        <el-form-item label="设备秘钥" prop="secureKey">
-          <el-input v-model="ruleForm.secureKey" placeholder="请输入设备秘钥" />
-        </el-form-item>
-
-         <el-form-item label="固件版本号" prop="version">
-          <el-input v-model="ruleForm.version" placeholder="请输入固件版本号" />
-        </el-form-item>
-      
-  
-        <el-form-item label="备注" prop="desc">
-          <el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入内容"></el-input>
-        </el-form-item>
+	<el-dialog
+		:title="textMap[dialogStatus]"
+		v-model="dialogVisible"
+		:width="dialogWidth"
+		:before-close="clsoeDialog"
+		:close-on-click-modal="false"
+	>
+		<div class="wrapper">
+			<el-form class="form" ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="120px" style="width: 400px">
+				<el-form-item label="模板名称" prop="title">
+					<el-input v-model="temp.title" placeholder="请输入模板名称" />
+				</el-form-item>
+				<el-form-item label="读取模式" prop="mode">
+					<el-select v-model="temp.mode" placeholder="请选择模式" class="filter-item" style="width: 100%" @change="handleModeChange">
+						<!-- <el-option label="顺序读取" :value="0" /> -->
+						<el-option label="批量读取" :value="1" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="备注" prop="remarks">
+					<el-input v-model="temp.remarks" placeholder="请输入备注" />
+				</el-form-item>
 			</el-form>
-			<template #footer>
-				<span class="dialog-footer">
-					<el-button @click="onCancel" size="default">取 消</el-button>
-					<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.id!==0?'修 改':'添 加'}}</el-button>
-				</span>
-			</template>
-		</el-dialog>
-	</div>
+			<el-tabs v-model="activeName" v-if="dialogStatus === 'update'" :key="tabsKey">
+				<el-tab-pane label="数据区" name="1" v-if="temp.mode !== 0">
+					<DataAreaDialog :templateNumber="temp.number" ref="dataAreaDialog" v-if="activeName === '1'" />
+				</el-tab-pane>
+				<el-tab-pane label="变量列表" name="2">
+					<DeviceTemplateDialog :templateNumber="temp.number" :mode="temp.mode" ref="deviceTemplateDialog" v-if="activeName === '2'" />
+				</el-tab-pane>
+			</el-tabs>
+			<div slot="footer" class="dialog-footer">
+				<el-button @click="clsoeDialog"> 取 消 </el-button>
+				<el-button type="primary" @click="dialogStatus === 'create' ? createData() : updateData()"> 保 存 </el-button>
+			</div>
+		</div>
+	</el-dialog>
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent,ref, unref } from 'vue';
-import api from '/@/api/device';
-import {ElMessage} from "element-plus";
-interface RuleFormState {
-  id:number;
-  name:string;
-  certificate:string;
-  secureKey:string;
-  version:string;
-  productId:number;
-  deptId:number;
-  desc:string;
-}
-interface DicState {
-	isShowDialog: boolean;
-	ruleForm: RuleFormState;
-  rules:{}
-}
+import api from '/@/api/device/modbus';
+import DataAreaDialog from './dataAreaDialog.vue';
+import DeviceTemplateDialog from './deviceTemplateDialog.vue';
+import { ElMessage } from 'element-plus';
 
-export default defineComponent({
-	name: 'deviceEditPro',
-	setup(prop,{emit}) {
-    const formRef = ref<HTMLElement | null>(null);
-		const state = reactive<DicState>({
-			isShowDialog: false,
-      productData: [], // 分类数据
-      deptData: [], // 
-			ruleForm: {
-        id:0,
-        name:'',
-        productId:'',
-        deptId:0,
-        certificate:'',
-        secureKey:'',
-        version:'',
-        desc:''
+export default {
+	components: {
+		DataAreaDialog,
+		DeviceTemplateDialog,
+	},
+	data() {
+		return {
+			temp: {
+				title: '',
+				mode: 1,
+				remarks: '',
+				number: '',
 			},
-      rules: {
-        name: [
-          { required: true, message: "设备名称不能为空", trigger: "blur" }
-        ],
-        key: [
-          { required: true, message: "设备标识不能为空", trigger: "blur" }
-        ],
-        productId: [{ required: true, message: '所属产品不能为空', trigger: 'blur' }],
-        deptId: [{ required: true, message: '所属部门不能为空', trigger: 'blur' }],
-       
-      }
-		});
-		// 打开弹窗
-		const openDialog = (row: RuleFormState|null) => {
-      resetForm();
-
-        api.product.getLists({ status: 1 }).then((res: any) => {
-          state.productData = res.product || [];
-        });
-        api.dept.getList({ status: -1 }).then((res: any) => {
-          state.deptData = res || [];
-        });
-
-
-      if (row){
-        // api.dict.getType(row.id).then((res:any)=>{
-        //   state.ruleForm = res.data.dictType
-        // })
-        state.ruleForm = row;
-      }
-			state.isShowDialog = true;
-		};
-    const resetForm = ()=>{
-      state.ruleForm = {
-        id:0,
-        name:'',
-        productId:'',
-        deptId:0,
-        certificate:'',
-        secureKey:'',
-        version:'',
-        desc:''
-      }
-    };
-		// 关闭弹窗
-		const closeDialog = () => {
-			state.isShowDialog = false;
-		};
-		// 取消
-		const onCancel = () => {
-			closeDialog();
-		};
-		// 新增
-		const onSubmit = () => {
-      const formWrap = unref(formRef) as any;
-      if (!formWrap) return;
-      formWrap.validate((valid: boolean) => {
-        if (valid) {
-          if(state.ruleForm.id!==0){
-            //修改
-            api.instance.edit(state.ruleForm).then(()=>{
-              ElMessage.success('设备类型修改成功');
-              closeDialog(); // 关闭弹窗
-              emit('typeList')
-            })
-          }else{
-            //添加
-            api.instance.add(state.ruleForm).then(()=>{
-              ElMessage.success('设备类型添加成功');
-              closeDialog(); // 关闭弹窗
-              emit('typeList')
-            })
-          }
-        }
-      });
+			rules: {
+				title: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
+				mode: [{ required: true, message: '请选择模式', trigger: 'change' }],
+			},
+			dialogVisible: false,
+			dialogStatus: '',
+			textMap: {
+				update: '模板详情',
+				create: '添加模板',
+			},
+			activeName: '1',
+			tabsKey: Date.now(),
 		};
+	},
 
+	computed: {
+		dialogWidth() {
+			return this.dialogStatus === 'create' ? '500px' : '80%';
+		},
+	},
 
-		return {
-			openDialog,
-			closeDialog,
-			onCancel,
-			onSubmit,
-      formRef,
-			...toRefs(state),
-		};
+	methods: {
+		open(dialogStatus: string, row: any) {
+			this.dialogStatus = dialogStatus;
+			this.tabsKey = Date.now();
+			this.dialogVisible = true;
+			if (row) {
+				this.temp = { ...row };
+				if (this.temp.mode === 0) {
+					this.activeName = '2';
+				}
+			} else {
+				this.getDataId();
+			}
+		},
+		clsoeDialog() {
+			this.dialogVisible = false;
+			this.activeName = '1';
+			this.temp = {
+				title: '',
+				mode: 1,
+				remarks: '',
+				number: '',
+			};
+			// this.$refs.dataForm.resetFields()
+		},
+		// 获取模板id 自动生成
+		getDataId() {
+			api.getDataId().then((res: any) => {
+				this.temp.number = res.data_id;
+			});
+		},
+		createData() {
+			(this.$refs['dataForm'] as any).validate((valid: boolean) => {
+				if (valid) {
+					api.template.addTemplate(this.temp).then(() => {
+						this.$emit('getList');
+						this.clsoeDialog();
+						ElMessage.success('操作成功!');
+					});
+				}
+			});
+		},
+		updateData() {
+			(this.$refs['dataForm'] as any).validate((valid: boolean) => {
+				if (valid) {
+					api.template.editTemplate(this.temp).then(() => {
+						this.$emit('finish');
+						this.clsoeDialog();
+						ElMessage.success('操作成功!');
+					});
+				}
+			});
+		},
+		handleModeChange(value: number) {
+			if (value === 0) {
+				this.activeName = '2';
+			}
+		},
 	},
-});
+};
 </script>
+
+<style lang="scss" scoped>
+.wrapper {
+	max-height: 70vh;
+	overflow-y: auto;
+}
+</style>

BIN
src/views/iot/device/template/component/images/equi_download.png


BIN
src/views/iot/device/template/component/images/equi_upload.png


+ 182 - 0
src/views/iot/device/template/component/importFile.vue

@@ -0,0 +1,182 @@
+<template>
+	<el-dialog title="导入" v-model="dialogVisible" :close-on-click-modal="false" class="import-dialog" :before-close="handleClose">
+		<h3 class="title">上传文件</h3>
+		<p class="tips">说明:请上传文件,支持格式.XLS|.XLSX</p>
+		<input type="file" ref="upload" style="display: none" accept=".xls,.xlsx" class="outputlist_upload" />
+		<div class="file-box" @click="handleUpload">
+			<img :src="equiUploadImg" />
+			<span>上传文件</span>
+		</div>
+		<div v-if="fileName" class="file-content">
+			<span>
+				<i class="el-icon-document"></i>
+				{{ fileName }}
+			</span>
+			<span class="el-icon-close" @click="removeFile"></span>
+		</div>
+
+		<div v-if="info.length" style="color: #f00">错误信息:</div>
+		<div class="file-content" v-for="(item, index) in info" :key="index">
+			<span>{{ item }}</span>
+		</div>
+
+		<el-button type="primary" class="import-btn" @click="handleImport" :loading="importLoading">导入</el-button>
+	</el-dialog>
+</template>
+
+<script lang="ts">
+import equiDownloadImg from './images/equi_download.png';
+import equiUploadImg from './images/equi_upload.png';
+import api from '/@/api/device/modbus';
+import { ElMessage } from 'element-plus';
+
+export default {
+	data() {
+		return {
+			dialogVisible: false,
+			equiDownloadImg,
+			equiUploadImg,
+			fm: {},
+			fileName: '',
+			importLoading: false,
+			importFlag: 1,
+			templateNumber: '',
+			info: [],
+		};
+	},
+
+	methods: {
+		openDialog() {
+			this.dialogVisible = true;
+
+			this.$nextTick(() => {
+				(this.$refs.upload as any).addEventListener('change', this.uploadChange);
+			});
+		},
+
+		handleClose() {
+			this.dialogVisible = false;
+			this.importLoading = false;
+			this.fm = {};
+			this.fileName = '';
+			this.info = [];
+
+			(this.$refs.upload as any).removeEventListener('change', this.uploadChange);
+			(this.$refs.upload as any).value = '';
+		},
+
+		uploadChange(e) {
+			this.readExcel(e);
+		},
+
+		// 上传
+		handleUpload() {
+			(this.$refs.upload as any).click();
+		},
+
+		removeFile() {
+			this.fm = {};
+			this.fileName = '';
+			(this.$refs.upload as any).value = '';
+		},
+
+		readExcel(e: any) {
+			// 表格导入
+			const files = e.target.files;
+			if (files.length <= 0) {
+				// 如果没有文件名
+				return false;
+			} else if (!/\.(xls|xlsx)$/.test(files[0].name.toLowerCase())) {
+				ElMessage.error('上传格式不正确,请上传xls或者xlsx格式!');
+				return false;
+			}
+			let fm = new FormData();
+			fm.append('file', files[0]);
+			fm.append('template_number', this.templateNumber);
+
+			this.fm = fm;
+			this.fileName = files[0].name;
+		},
+
+		handleImport() {
+			if (!this.fm) {
+				return ElMessage.error('请上传文件!');
+			}
+			this.importLoading = true;
+			api.template
+				.importFile(this.fm, {
+					headers: {
+						dataType: 'formData',
+					},
+				})
+				.then((res: any) => {
+					if (res.code === 0) {
+						let info = res.data.Info || [];
+						this.info = info;
+						if (!info.length) {
+							ElMessage.success('导入成功');
+						}
+					}
+				})
+				.finally(() => {
+					this.importLoading = false;
+				});
+		},
+	},
+
+	mounted() {},
+};
+</script>
+
+<style lang="scss" scoped>
+::v-deep {
+	.import-dialog {
+		.el-dialog {
+			width: 575px !important;
+			height: auto;
+		}
+	}
+}
+.title {
+	// color: #000;
+	font-size: 16px;
+	margin-bottom: 10px;
+}
+
+.tips {
+	color: #999;
+	font-size: 14px;
+	margin-bottom: 10px;
+}
+
+.file-box {
+	display: flex;
+	align-items: center;
+	cursor: pointer;
+	padding: 10px;
+	margin-bottom: 20px;
+	border: 2px dashed #ccc;
+	img {
+		margin-right: 10px;
+	}
+}
+
+.import-btn {
+	width: 100%;
+	height: 40px;
+	font-size: 16px;
+}
+
+.file-content {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	line-height: 24px;
+	cursor: pointer;
+	margin-bottom: 20px;
+	padding: 5px 10px;
+	&:hover {
+		background-color: #f5f7fa;
+	}
+}
+</style>

+ 19 - 11
src/views/iot/device/template/index.vue

@@ -19,7 +19,7 @@
 							</el-icon>
 							重置
 						</el-button>
-						<el-button type="success" @click="addOrEdit()" v-auth="'add'">
+						<el-button type="success" @click="addOrEdit()">
 							<el-icon>
 								<ele-FolderAdd />
 							</el-icon>
@@ -32,33 +32,36 @@
 				<el-table-column type="index" label="序号" width="80" align="center" />
 				<el-table-column prop="title" label="模板名称" align="center" show-overflow-tooltip></el-table-column>
 				<el-table-column prop="mode" label="模式" align="center" width="120" show-overflow-tooltip>
-          <template #default="{ row }">
-          {{ row.mode === 0 ? '顺序读取' : '批量读取' }}
-        </template>
-        </el-table-column>
+					<template #default="{ row }">
+						{{ row.mode === 0 ? '顺序读取' : '批量读取' }}
+					</template>
+				</el-table-column>
 				<el-table-column prop="remarks" label="备注" align="center" show-overflow-tooltip></el-table-column>
 				<el-table-column label="操作" width="160" align="center">
 					<template #default="scope">
-						<el-button size="small" text type="primary" >导入</el-button>
-						<el-button size="small" text type="primary" >导出</el-button>
-						<el-button size="small" text type="primary" @click="addOrEdit(scope.row)" v-auth="'edit'">详情</el-button>
-						<el-button size="small" text type="danger" @click="onDel(scope.row)" v-auth="'del'">删除</el-button>
+						<el-button size="small" text type="primary" @click="handleImport(scope.row)">导入</el-button>
+						<el-button size="small" text type="primary">导出</el-button>
+						<el-button size="small" text type="primary" @click="addOrEdit(scope.row)">详情</el-button>
+						<el-button size="small" text type="danger" @click="onDel(scope.row)">删除</el-button>
 					</template>
 				</el-table-column>
 			</el-table>
 			<pagination v-if="params.total" :total="params.total" v-model:page="params.page" v-model:limit="params.size" @pagination="getList()" />
 		</el-card>
 		<EditForm ref="editFormRef" @getList="getList()"></EditForm>
+		<ImportFile ref="importFile" />
 	</div>
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue';
 import EditForm from './component/edit.vue';
+import ImportFile from './component/importFile.vue';
 import api from '/@/api/device/modbus';
 import { ElMessageBox, ElMessage } from 'element-plus';
 import { useSearch } from '/@/hooks/useCommonModbus';
 
+const importFile = ref();
 const editFormRef = ref();
 const queryRef = ref();
 
@@ -67,7 +70,12 @@ const { params, tableData, getList, loading } = useSearch(api.template.getList,
 getList();
 
 const addOrEdit = async (row?: any) => {
-	editFormRef.value.open(row);
+	editFormRef.value.open(row ? 'update' : 'create', row);
+};
+
+const handleImport = async (row: any) => {
+	importFile.value.templateNumber = row.number;
+	importFile.value.openDialog();
 };
 
 // 重置表单
@@ -82,7 +90,7 @@ const onDel = (row: any) => {
 		cancelButtonText: '取消',
 		type: 'warning',
 	}).then(async () => {
-		await api.channel.deleteDevice({ number: row.number });
+		await api.template.deleteTemplate({ number: row.number });
 		ElMessage.success('删除成功');
 		getList();
 	});