Преглед изворни кода

Merge branch 'master' of http://git.mydig.net/Sagoo-Cloud/sagoo-admin-ui

vera_min пре 2 година
родитељ
комит
9eae0c668e
27 измењених фајлова са 3279 додато и 262 уклоњено
  1. 3 1
      .env
  2. 6 0
      public/imgs/weather/晴.svg
  3. 2 1
      src/api/datahub/index.ts
  4. 23 0
      src/api/device/modbus.ts
  5. 51 0
      src/hooks/useCommonModbus.ts
  6. 138 0
      src/utils/request_modbus.ts
  7. 2 2
      src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/floor/component/detail.vue
  8. 2 2
      src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/regional/component/detail.vue
  9. 2 2
      src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/resident/component/detail.vue
  10. 2 2
      src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/unit/component/detail.vue
  11. 2 2
      src/views/heating/heatStation/loop/component/detail.vue
  12. 9 9
      src/views/heating/heatStation/loop/component/edit.vue
  13. 2 2
      src/views/heating/heatStation/loop/index.vue
  14. 2 3
      src/views/heating/home/index.vue
  15. 3 1
      src/views/heating/monitor/loopSupervision/loopDetail.vue
  16. 190 0
      src/views/iot/device/channel/component/edit.vue
  17. 147 0
      src/views/iot/device/channel/component/list.vue
  18. 873 0
      src/views/iot/device/channel/detail.vue
  19. 87 0
      src/views/iot/device/channel/index.vue
  20. 190 0
      src/views/iot/device/template/component/edit.vue
  21. 147 0
      src/views/iot/device/template/component/list.vue
  22. 873 0
      src/views/iot/device/template/detail.vue
  23. 90 0
      src/views/iot/device/template/index.vue
  24. 165 0
      src/views/system/datahub/modeling/component/juhe.vue
  25. 241 211
      src/views/system/datahub/modeling/index.vue
  26. 27 23
      src/views/system/monitor/cache/index.vue
  27. 0 1
      src/views/system/monitor/server/index.vue

+ 3 - 1
.env

@@ -16,4 +16,6 @@ VITE_ASSESS_URL = '/base-api/assess/v1'
 # 大屏前端
 VITE_SCREEN_URL = '/plugin/screen/'
 # 组态图前端
-VITE_TOPO_URL = '/plugin/topo/'
+VITE_TOPO_URL = '/plugin/topo/'
+# modbus服务
+VITE_MODBUS_API = '/base-api/modbus/v1'

Разлика између датотеке није приказан због своје велике величине
+ 6 - 0
public/imgs/weather/晴.svg


+ 2 - 1
src/api/datahub/index.ts

@@ -48,8 +48,9 @@ export default {
       copy: (params: object) => post('/source/template/copy', params),
       relation_check: (id: number) => get('/source/template/relation_check', { id }),
       source_list: (id: number) => get('/source/template/source_list', { id }),
+      aggregate_from: (id: number) => get('/source/template/aggregate_from', { id }),
       relation: (data: object) => post('/source/template/relation', data),
-      
+      aggregate: (data: object) => post('/source/template/aggregate', data),
    } ,
 
    tnode:{

+ 23 - 0
src/api/device/modbus.ts

@@ -0,0 +1,23 @@
+import { get, post } from '/@/utils/request_modbus';
+
+export default {
+  channel: {
+    getList: (params: object) => get('/device', params),
+    addDevice: (data: object) => post('/device/add', data),
+    deleteDevice: (data: object) => post('/device/delete', data),
+    editDevice: (data: object) => post('/device/edit', data),
+  },
+  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),
+    importFile: (data: object, config: object) => post('/data_area/import', data, config),
+    exportFile: (params: object) => get('/data_area/import', params, {
+      headers: {
+        responseType: 'blob'
+      }
+    })
+  }
+}

+ 51 - 0
src/hooks/useCommonModbus.ts

@@ -0,0 +1,51 @@
+import { reactive, ref } from 'vue'
+
+export default function () {
+  const statusParams = reactive({
+    status: 1
+  })
+
+  return { statusParams }
+}
+
+export function useSearch<T>(api: any, resKey: string, expandParams?: any) {
+
+  //  <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
+
+  // import api from '/@/api/system';
+  // import { ApiRow } from '/@/api/model/system/menu';
+  // import { useSearch } from '/@/hooks/useCommon';
+
+  // const { params, tableData, getList } = useSearch<ApiRow[]>(api.api.getList, 'Info', { name: '', address: '' });
+  // getList() // 获取列表数据
+
+  interface SearchParams {
+    page: number;
+    size: number;
+    total: number;
+    [key: string]: any;
+  }
+
+  const params = reactive<SearchParams>({
+    page: 1,
+    size: 10,
+    total: 0,
+    ...expandParams
+  })
+
+  const loading = ref(false)
+
+  const tableData = ref<T[] | any[]>([])
+
+  const getList = async (page?: number) => {
+    page && (params.page = page);
+    tableData.value = [];
+    loading.value = true;
+    params.total = 0;
+    let res = await api(params).finally(() => loading.value = false)
+    tableData.value = (resKey ? (res[resKey]) : (res)) || [];
+    params.total = res.Total;
+  };
+
+  return { params, tableData, getList, loading }
+}

+ 138 - 0
src/utils/request_modbus.ts

@@ -0,0 +1,138 @@
+import axios from 'axios';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { Session } from '/@/utils/storage';
+import getOrigin from '/@/utils/origin'
+
+// 配置新建一个 axios 实例
+const service = axios.create({
+	baseURL: getOrigin(import.meta.env.VITE_MODBUS_API),
+	timeout: 50000,
+	headers: { 'Content-Type': 'application/json' },
+});
+
+// 添加请求拦截器
+service.interceptors.request.use(
+	(config) => {
+		// 在发送请求之前做些什么 token
+		if (Session.get('token')) {
+			(<any>config.headers).common['Authorization'] = `Bearer ${Session.get('token')}`;
+		}
+		return config;
+	},
+	(error) => {
+		// 对请求错误做些什么
+		return Promise.reject(error);
+	}
+);
+
+// 添加响应拦截器
+service.interceptors.response.use(
+	(response) => {
+		// 对响应数据做点什么
+		const res = response.data;
+		const code = response.data.code
+		if (code === 401) {
+			ElMessageBox.alert('登录状态已过期,请重新登录', '提示',
+				{ confirmButtonText: '确定', showCancelButton: false, closeOnHashChange: false, closeOnPressEscape: false, closeOnClickModal: false, showClose: false })
+				.then(() => {
+					Session.clear(); // 清除浏览器全部临时缓存
+					window.location.href = '/'; // 去登录页
+				})
+				.catch(() => { });
+		} else if (code === undefined && res.message === undefined) { // 可能是下载文件
+			return response
+		} else if (code !== 0) {
+			ElMessage.error(res.message)
+			return Promise.reject(new Error(res.message))
+		} else {
+			// 分页的数据
+			if (res.data?.Total !== undefined) {
+				return {
+					list: res.data.Data,
+					total: res.data.Total,
+					page: res.data.currentPage,
+					...res.data,
+				}
+			}
+			// if (res.data?.Data) {
+			// 	return res.data.Data
+			// }
+			if (res.data?.Info && res.data?.Data) { // currentUser接口
+				return res.data
+			}
+			if (res.data?.Data === undefined) {
+				return res.data
+			}
+			return res.data.Data
+		}
+	},
+	(error) => {
+		// 对响应错误做点什么
+		if (error.message.indexOf('timeout') != -1) {
+			ElMessage.error('网络超时');
+		} else if (error.message == 'Network Error') {
+			ElMessage.error('网络连接错误');
+		} else {
+			if (error.response.data) ElMessage.error(error.response.statusText);
+			else ElMessage.error('接口路径找不到');
+		}
+		return Promise.reject(error);
+	}
+);
+
+// 导出 axios 实例
+export default service;
+
+export function get(url: string, params?: any, config?: any): any {
+	return service({
+		url,
+		method: "get",
+		...config,
+		params
+	})
+}
+
+export function post(url: string, data?: any, config?: any): any {
+	return service({
+		url,
+		method: "post",
+		...config,
+		data
+	})
+}
+
+export function put(url: string, data?: any): any {
+	return service({
+		url,
+		method: "put",
+		data
+	})
+}
+export function del(url: string, data?: any): any {
+	return service({
+		url,
+		method: "delete",
+		data
+	})
+}
+
+
+export function file(url: string, params?: any, method: 'get' | 'post' = 'get'): any {
+	if (method === 'get') {
+		return service({
+			url,
+			method,
+			params,
+			timeout: 30000,
+			responseType: 'arraybuffer',
+		});
+	} else {
+		return service({
+			url,
+			method,
+			timeout: 100000,
+			data: params,
+			responseType: 'blob',
+		});
+	}
+}

+ 2 - 2
src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/floor/component/detail.vue

@@ -44,12 +44,12 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供暖面积" prop="heatingArea">
+            <el-form-item label="联网面积" prop="heatingArea">
               {{ ruleForm.heatingArea }}
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实面积" prop="forRealArea">
+            <el-form-item label="实面积" prop="forRealArea">
               {{ ruleForm.forRealArea }}
             </el-form-item>
           </el-col>

+ 2 - 2
src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/regional/component/detail.vue

@@ -44,12 +44,12 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供暖面积" prop="heatingArea">
+            <el-form-item label="联网面积" prop="heatingArea">
               {{ ruleForm.heatingArea }}
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实面积" prop="forRealArea">
+            <el-form-item label="实面积" prop="forRealArea">
               {{ ruleForm.forRealArea }}
             </el-form-item>
           </el-col>

+ 2 - 2
src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/resident/component/detail.vue

@@ -44,12 +44,12 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供暖面积" prop="heatingArea">
+            <el-form-item label="联网面积" prop="heatingArea">
               {{ ruleForm.heatingArea }}
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实面积" prop="forRealArea">
+            <el-form-item label="实面积" prop="forRealArea">
               {{ ruleForm.forRealArea }}
             </el-form-item>
           </el-col>

+ 2 - 2
src/views/heating/heatStation/heatingDistrict/heatingDistrictManage/children/unit/component/detail.vue

@@ -44,12 +44,12 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供暖面积" prop="heatingArea">
+            <el-form-item label="联网面积" prop="heatingArea">
               {{ ruleForm.heatingArea }}
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实面积" prop="forRealArea">
+            <el-form-item label="实面积" prop="forRealArea">
               {{ ruleForm.forRealArea }}
             </el-form-item>
           </el-col>

+ 2 - 2
src/views/heating/heatStation/loop/component/detail.vue

@@ -44,12 +44,12 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供面积" prop="heatingArea">
+            <el-form-item label="供面积" prop="heatingArea">
               {{ ruleForm.heatingArea }}
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实际面积" prop="forRealArea">
+            <el-form-item label="联网面积" prop="forRealArea">
               {{ ruleForm.forRealArea }}
             </el-form-item>
           </el-col>

+ 9 - 9
src/views/heating/heatStation/loop/component/edit.vue

@@ -10,9 +10,9 @@
 				</el-form-item> -->
         <el-form-item label="所属换热站" prop="stationId">
           <el-tree-select v-model="ruleForm.stationId" :data="treeData" @change="stationChange" :props="{
-							label: 'name',
-							children: 'children'
-						}" node-key="id" :clearable="true" filterable check-strictly style="width: 100%;" :render-after-expand="true" />
+            label: 'name',
+            children: 'children'
+          }" node-key="id" :clearable="true" filterable check-strictly style="width: 100%;" :render-after-expand="true" />
         </el-form-item>
         <el-form-item label="环路类型" prop="loopTypes">
           <el-select v-model="ruleForm.loopTypes" placeholder="请选择环路类型" clearable size="default" style="width: 100%">
@@ -46,13 +46,13 @@
         </el-row>
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-form-item label="供面积" prop="heatingArea">
-              <el-input v-model="ruleForm.heatingArea" placeholder="请输入供暖面积" />
+            <el-form-item label="供面积" prop="heatingArea">
+              <el-input v-model="ruleForm.heatingArea" placeholder="请输入联网面积" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="实际面积" prop="forRealArea">
-              <el-input v-model="ruleForm.forRealArea" placeholder="请输入实面积" />
+            <el-form-item label="联网面积" prop="forRealArea">
+              <el-input v-model="ruleForm.forRealArea" placeholder="请输入实面积" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -164,8 +164,8 @@ export default defineComponent({
         energyTypes: [{ required: true, message: '节能类型不能为空', trigger: ['blur', 'change'] }],
         heatingObject: [{ required: true, message: '供暖对象不能为空', trigger: ['blur', 'change'] }],
         heatingTypes: [{ required: true, message: '供暖类型不能为空', trigger: ['blur', 'change'] }],
-        heatingArea: [{ required: true, message: '供暖面积不能为空', trigger: ['blur', 'change'] }],
-        forRealArea: [{ required: true, message: '实面积不能为空', trigger: ['blur', 'change'] }],
+        heatingArea: [{ required: true, message: '联网面积不能为空', trigger: ['blur', 'change'] }],
+        forRealArea: [{ required: true, message: '实面积不能为空', trigger: ['blur', 'change'] }],
         decade: [{ required: true, message: '年代不能为空', trigger: ['blur', 'change'] }],
         status: [{ required: true, message: '状态不能为空', trigger: ['blur', 'change'] }]
       },

+ 2 - 2
src/views/heating/heatStation/loop/index.vue

@@ -83,8 +83,8 @@
             <el-tag type="info" size="small" v-else-if="scope.row.heatingTypes === 2">暖气片</el-tag>
           </template>
         </el-table-column>
-	    	<el-table-column label="供暖面积" prop="heatingArea" v-col="'heatingArea'" :show-overflow-tooltip="true" />
-	    	<el-table-column label="实际面积" prop="forRealArea" v-col="'forRealArea'" :show-overflow-tooltip="true" />
+	    	<el-table-column label="联网面积" prop="forRealArea" v-col="'forRealArea'" :show-overflow-tooltip="true" />
+	    	<el-table-column label="实供面积" prop="heatingArea" v-col="'heatingArea'" :show-overflow-tooltip="true" />
 <!--	    	<el-table-column label="环路年代" prop="decade" :show-overflow-tooltip="true" />-->
         <el-table-column prop="status" label="状态" v-col="'status'" align="center">
           <template #default="scope">

+ 2 - 3
src/views/heating/home/index.vue

@@ -299,7 +299,7 @@ export default defineComponent({
             color: state.charts.color,
           },
         },
-        grid: { top: 80, bottom: 30 },
+        grid: { top: 60, bottom: 20, left: 30, right: 30, containLabel: true },
         // calculable: true,
         xAxis: [{ data: state.statisticsChartXAxisData }],
         yAxis: [
@@ -349,7 +349,7 @@ export default defineComponent({
             color: state.charts.color,
           },
         },
-        grid: { top: 80, bottom: 30 },
+        grid: { top: 60, bottom: 20, left: 30, right: 30, containLabel: true },
         xAxis: [{ data: state.pressureXAxisData }],
         yAxis: [
           {
@@ -489,7 +489,6 @@ export default defineComponent({
         unit = 'W'
       }
 
-      console.log(dom)
       global.homeCharThree = <any>echarts.init(dom, state.charts.theme);
 
       const common = {

+ 3 - 1
src/views/heating/monitor/loopSupervision/loopDetail.vue

@@ -289,6 +289,8 @@ export default defineComponent({
 				[{ name: '失水量', type: 'line', data: state.waterLossEchart }],
 			];
 
+			const units = ['°C', 'Mpa', 'T/h', 'T'];
+
 			const option = {
 				backgroundColor: state.charts.bgColor,
 				tooltip: {},
@@ -313,7 +315,7 @@ export default defineComponent({
 						type: 'value',
 						scale: true,
 						axisLabel: {
-							formatter: '{value} °C',
+							formatter: '{value} ' + units[tabName.value],
 						},
 					},
 				],

+ 190 - 0
src/views/iot/device/channel/component/edit.vue

@@ -0,0 +1,190 @@
+<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-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>
+</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:{}
+}
+
+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:''
+			},
+      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')
+            })
+          }
+        }
+      });
+		};
+
+
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+      formRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>

+ 147 - 0
src/views/iot/device/channel/component/list.vue

@@ -0,0 +1,147 @@
+<template>
+	<div class="system-edit-dic-container">
+		<el-dialog  v-model="isShowDialog" :show-close="false"  width="75%" :fullscreen="dialogFullScreen">
+		 <template #header="{ close, titleId, titleClass }">
+      <div class="my-header">
+        <h4 :id="titleId" :class="titleClass">数据记录</h4>
+		 
+		 <div>
+            <i class="iconfont "  :class="!dialogFullScreen ? 'icon-fullscreen' : 'icon-tuichuquanping'"   @click="quanping"  style="font-size: 22px;cursor: pointer;"></i>
+			<i class="el-icon"  @click="close" style="font-size: 22px;cursor: pointer;    margin-left: 10px; position: relative; top: 3px;"><svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path></svg></i>
+			
+		</div>
+      </div>
+    </template>
+
+			<el-table :data="tableData.data" style="width: 100%"  v-loading="tableData.loading">
+				<el-table-column label="时间" prop="ts" :show-overflow-tooltip="true" />
+				<el-table-column label="属性值" prop="value" :show-overflow-tooltip="true" />
+			</el-table>
+			<pagination
+				v-show="tableData.total > 0"
+				:total="tableData.total"
+				v-model:page="tableData.param.pageNum"
+				v-model:limit="tableData.param.pageSize"
+				@pagination="typeList"
+			/>
+		</el-dialog>
+	</div>
+</template>
+ 
+<script lang="ts">
+import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import { Close } from '@element-plus/icons-vue';
+
+import api from '/@/api/device';
+import { ElMessage } from 'element-plus';
+
+interface DicState {
+	isShowDialog: boolean;
+}
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	id: number;
+	name: string;
+	key: string;
+
+	createBy: string;
+}
+interface TableDataState {
+	ids: number[];
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+			id: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'deviceEditPro',
+	setup(prop, { emit }) {
+		const formRef = ref<HTMLElement | null>(null);
+		const state = reactive<DicState>({
+			isShowDialog: false,
+		    dialogFullScreen: false,
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+					id: 0,
+					propertyKey:'',
+				},
+			},
+		});
+		// 打开弹窗
+		const openDialog = (row: RuleFormState | null,devid) => {
+			resetForm();
+			if (row) {
+				console.log(row);
+				state.tableData.param.id = devid;
+				state.tableData.param.propertyKey=row.key
+				typeList();
+
+			}
+			state.isShowDialog = true;
+		};
+
+		const typeList = () => {
+			state.tableData.loading = true;
+			api.instance.getLogDetail(state.tableData.param).then((res: any) => {
+				state.tableData.data = res.List;
+				state.tableData.total = res.Total;
+				//state.ruleForm = res.data.dictType
+			}).finally(() => (state.tableData.loading = false));
+
+		};
+		const resetForm = () => {
+			state.tableData= {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			}
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		const quanping=()=>{
+			state.dialogFullScreen = state.dialogFullScreen?false:true;
+		}
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+
+		return {
+            Close,
+			quanping,
+			typeList,
+			openDialog,
+			closeDialog,
+			onCancel,
+			formRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+<style scoped>
+.my-header {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+</style>

+ 873 - 0
src/views/iot/device/channel/detail.vue

@@ -0,0 +1,873 @@
+<template>
+  <div class="system-dic-container">
+    <div class="content">
+      <div class="cont_box">
+        <div class="title">设备:{{ detail.name }}</div>
+        <div class="pro-status"><span :class="developer_status == 2 ? 'on' : 'off'"></span>{{ developer_status == 2 ? '在线' : '离线' }}</div>
+
+        <!-- <div class="pro-option" @click="CkOption">{{ developer_status == 2 ? '下线' : '上线' }}</div> -->
+      </div>
+    </div>
+
+		<div class="content-box">
+			<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+
+				<el-tab-pane label="运行状态" name="3">
+					<div style=" display: flex; padding: 10px;flex-wrap: wrap;" >
+						<div class="ant-card">
+							<div class="ant-card-body">
+								<div class="cardflex">
+									<div>设备状态</div>
+									<div @click="getrunData()" style="cursor: pointer;">
+										<el-icon style="font-size: 18px;">
+											<ele-Refresh />
+										</el-icon>
+									</div>
+								</div>
+
+								<div class="statusname" v-if="areaData.status==0">未启用</div>
+								<div class="statusname" v-if="areaData.status==1">离线</div>
+								<div class="statusname" v-if="areaData.status==2">在线</div>
+								<div class="cardflex comtest">
+									<div> 数据时间</div>
+									<div>{{areaData.lastOnlineTime || '未启用'}}</div>
+								</div>
+							</div>
+						</div>
+
+						<div class="ant-card" v-for="(item, index) in areaData.properties" :key="index">
+							<div class="ant-card-body">
+								<div class="cardflex">
+									<div>{{item.name}}</div>
+									<div style="cursor: pointer;">
+										<el-icon  style="font-size: 18px;"  @click="getrunData()">
+											<ele-Refresh />
+										</el-icon>
+										<el-icon  style="font-size: 18px;    margin-left: 10px;" @click="onOpenListDetail(item)">
+											<ele-Expand />
+										</el-icon>
+									</div>
+								</div>
+
+								<div class="statusname">{{item.value}}{{item.unit}}</div>
+								<div class="">
+									<devantd :json="item.list" :antdid="item.key" v-if="item.type=='int' || item.type=='float'"/>
+								</div>
+							</div>
+						</div>
+
+
+
+					</div>
+				</el-tab-pane>
+
+
+
+
+				<el-tab-pane label="设备信息" name="1">
+					<div class="pro-box">
+						<div class="protitle">设备信息</div>
+            <div>
+              <el-button type="primary" @click="onOpenEditDic(detail)">编辑</el-button>
+            </div>
+          </div>
+
+					<div class="ant-descriptions-view">
+						<table>
+							<tbody>
+								<tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">设备标识</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ detail.key }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">设备名称</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ detail.name }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">所属产品</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ detail.productName }}</td>
+
+								</tr>
+								<tr class="ant-descriptions-row">
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">消息协议</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.messageProtocol }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">链接协议</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.transportProtocol }}</td>
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">设备类型</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ prodetail.deviceType }}</td>
+								</tr>
+								<tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">固件版本</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ prodetail.version }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">注册时间</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.updatedAt }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">最后上线时间</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.lastOnlineTime || '' }}</td>
+								</tr>
+                <tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">说明</th>
+                  <td class="ant-descriptions-item-content" colspan="5">{{ prodetail.desc }}</td>
+                </tr>
+
+
+              </tbody>
+								</table>
+								</div>
+								</el-tab-pane>
+        <el-tab-pane label="物模型" name="2">
+          <div class="wu-box">
+            <el-tabs type="border-card" v-model="activetab" @tab-click="wuhandleClick">
+              <el-tab-pane label="属性定义" name="attr">
+                <div class="wu-title">
+                  <div class="title">属性定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditAttr()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'attr'">
+                  <el-table-column label="属性标识" align="center" prop="key" />
+                  <el-table-column label="属性名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="valueType" label="数据类型" width="100" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.type }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="decimals" label="精度" width="60" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.decimals }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="unit" label="单位" width="60" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.unit }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
+                      <el-tag type="success" size="small" v-else>读写</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="说明" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditAttr(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'attr')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="功能定义" name="fun">
+                <div class="wu-title">
+                  <div class="title">功能定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditFun()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'fun'">
+                  <el-table-column label="功能标识" align="center" prop="key" />
+                  <el-table-column label="名称" prop="name" :show-overflow-tooltip="true" />
+
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditFun(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'fun')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="事件定义" name="event">
+                <div class="wu-title">
+                  <div class="title">事件定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditEvent()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'event'">
+                  <el-table-column label="事件标识" align="center" prop="key" />
+                  <el-table-column label="名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="level" label="事件级别" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="primary" size="small" v-if="scope.row.level == 0">普通</el-tag>
+                      <el-tag type="warning" size="small" v-if="scope.row.level == 1">警告</el-tag>
+                      <el-tag type="danger" size="small" v-if="scope.row.level == 2">紧急</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditEvent(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'event')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="标签定义" name="tab">
+                <div class="wu-title">
+                  <div class="title">标签定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditTab()">添加</el-button>
+                  </div>
+                </div>
+
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'tab'">
+                  <el-table-column label="属性标识" align="center" prop="key" />
+                  <el-table-column label="属性名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="valueType" label="数据类型" width="120" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.type }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
+                      <el-tag type="success" size="small" v-else>读写</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditTag(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'tab')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+            </el-tabs>
+            <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList" />
+          </div>
+        </el-tab-pane>
+
+
+        <el-tab-pane label="日志管理" name="4">
+          <div class="system-user-search mb15">
+            <el-form :model="logtableData.param" ref="queryRef" :inline="true" label-width="68px">
+              <el-form-item label="日志类型" prop="types">
+                <el-select v-model="logtableData.param.types" placeholder="日志类型" clearable size="default">
+                  <el-option v-for="item in logTypeData" :key="item" :label="item" :value="item" />
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="创建时间" prop="dateRange">
+                <el-date-picker v-model="logtableData.param.dateRange" size="default" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+              </el-form-item>
+              <el-form-item>
+                <el-button size="default" type="primary" class="ml10" @click="getlog">
+                  <el-icon>
+                    <ele-Search />
+                  </el-icon>
+                  查询
+                </el-button>
+                <el-button size="default" @click="resetQuery(queryRef)">
+                  <el-icon>
+                    <ele-Refresh />
+                  </el-icon>
+                  重置
+                </el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+          <el-table style="width: 100%" :data="logtableData.data">
+            <el-table-column label="类型" align="center" prop="type" />
+            <el-table-column label="时间" prop="ts" :show-overflow-tooltip="true" />
+
+            <el-table-column label="内容" prop="content" :show-overflow-tooltip="true" />
+            <el-table-column label="操作" width="300" align="center" fixed="right">
+              <template #default="scope">
+                <el-button size="small" text type="warning" @click="onLogDetail(scope.row)">查看</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <pagination v-show="logtableData.total > 0" :total="logtableData.total" v-model:page="logtableData.param.pageNum" v-model:limit="logtableData.param.pageSize" @pagination="getlog" />
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+    <EditDic ref="editDicRef" @typeList="typeList" />
+    <EditAttr ref="editAttrRef" @typeList="getproperty" />
+    <EditFun ref="editFunRef" @typeList="getfunction" />
+    <EditEvent ref="editEventRef" @typeList="getevent" />
+    <EditTab ref="editTabRef" @typeList="gettab" />
+    <ListDic ref="listDicRef" />
+
+    <el-dialog v-model="dialogVisible" title="返回Json数据" width="30%">
+      <JsonViewer :value="jsonData" boxed sort theme="jv-dark" @click="onKeyclick" />
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">关闭</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
+
+import 'vue3-json-viewer/dist/index.css';
+
+import EditDic from '../product/component/editPro.vue';
+import EditAttr from '../product/component/editAttr.vue';
+import EditFun from '../product/component/editFun.vue';
+import EditEvent from '../product/component/editEvent.vue';
+import EditTab from '../product/component/editTab.vue';
+import devantd from '/@/components/devantd/index.vue';
+import ListDic from './component/list.vue';
+
+
+import { useRoute } from 'vue-router';
+
+import api from '/@/api/device';
+
+interface TableDataState {
+  ids: number[];
+  tableData: {
+    data: [];
+    total: number;
+    loading: boolean;
+    param: {
+      pageNum: number;
+      pageSize: number;
+      name: string;
+      deviceType: string;
+      status: string;
+      dateRange: string[];
+    };
+  };
+  logtableData: {
+    data: [];
+    total: number;
+    loading: boolean;
+    param: {
+      pageNum: number;
+      pageSize: number;
+      name: string;
+      deviceType: string;
+      status: string;
+      dateRange: string[];
+    };
+  };
+}
+export default defineComponent({
+  name: 'deviceEditPro',
+  components: { EditDic, EditAttr, EditFun, EditEvent, EditTab, devantd, ListDic },
+
+	setup(prop, context) {
+		const route = useRoute();
+		const editDicRef = ref();
+		const editAttrRef = ref();
+		const editFunRef = ref();
+		const listDicRef=ref();
+		const editEventRef = ref();
+		const editTabRef = ref();
+		const state = reactive<TableDataState>({
+			areaData:[],
+			isShowDialog: false,
+			dialogVisible: false,
+			logTypeData: [],
+			jsonData: '',
+			activeName: '3', // 分类数据
+			activetab: 'attr', // 分类数据
+			detail: [],
+			prodetail: [],
+			product_id: 0,
+			developer_status: 0,
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					productId: 0,
+					pageSize: 10,
+					status: '',
+					dateRange: [],
+				},
+			},
+			logtableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					productId: 0,
+					pageSize: 10,
+					status: '',
+					dateRange: [],
+				},
+			},
+		});
+
+		onMounted(() => {
+			const ids = route.params && route.params.id;
+			api.instance.detail(ids).then((res: any) => {
+				state.detail = res.data;
+				state.developer_status = res.data.status;
+				state.tableData.param.productId = res.data.product.id;
+				state.product_id = res.data.product.id;
+				getrunData();
+				api.product.detail(res.data.product.id).then((res: any) => {
+					state.prodetail = res.data;
+					console.log(res.data);
+				});
+
+				//第一次加载
+				api.model.property(state.tableData.param).then((res: any) => {
+					state.tableData.data = res.Data;
+					state.tableData.total = res.Total;
+				});
+			});
+
+		});
+
+
+    const onLogDetail = (row: TableDataRow) => {
+      state.jsonData = JSON.parse(row.content);
+      console.log(JSON.parse(row.content));
+      state.dialogVisible = true;
+    };
+
+    //编辑属性
+    const onEditAttr = (row: TableDataRow) => {
+      editAttrRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑功能
+    const onEditFun = (row: TableDataRow) => {
+      editFunRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑事件
+    const onEditEvent = (row: TableDataRow) => {
+      editEventRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑标签
+    const onEditTag = (row: TableDataRow) => {
+      editTabRef.value.openDialog(row, state.product_id);
+    };
+
+    //打开添加属性弹窗
+    const onOpenEditAttr = () => {
+      editAttrRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
+    };
+
+    //打开添加功能弹窗
+    const onOpenEditFun = () => {
+      editFunRef.value.openDialog({ product_id: state.product_id, id: 0 });
+    };
+    //打开添加事件弹窗
+    const onOpenEditEvent = () => {
+      editEventRef.value.openDialog({ product_id: state.product_id, id: 0, level: 0 });
+    };
+
+    //打开添加事件弹窗
+    const onOpenEditTab = () => {
+      editTabRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
+    };
+
+    //查看日志列表
+    const onOpenListDetail = (row: TableDataRow) => {
+      listDicRef.value.openDialog(row, state.detail.id);
+    };
+
+    // 打开修改产品弹窗
+    const onOpenEditDic = (row: TableDataRow) => {
+      editDicRef.value.openDialog(row);
+    };
+
+    // 删除产品
+    const onRowDel = (key, type) => {
+      let msg = `此操作将永久删除该数据吗?,是否继续?`;
+
+      if (key.length === 0) {
+        ElMessage.error('请选择要删除的数据。');
+        return;
+      }
+      ElMessageBox.confirm(msg, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          if (type == 'attr') {
+            api.model.propertydel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getproperty();
+            });
+          }
+          if (type == 'fun') {
+            api.model.functiondel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getfunction();
+            });
+          }
+          if (type == 'event') {
+            api.model.eventdel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getevent();
+            });
+          }
+          if (type == 'tab') {
+            api.model.tagdel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              tagdel();
+            });
+          }
+        })
+        .catch(() => { });
+    };
+
+    //根据不同类型获取列表
+    const getList = () => {
+      switch (state.activetab) {
+        case 'attr':
+          getproperty();
+          break;
+        case 'fun':
+          getfunction();
+          break;
+        case 'event':
+          getevent();
+          break;
+        case 'tab':
+          gettab();
+          break;
+      }
+    };
+
+    const getproperty = () => {
+      api.model.property(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const getfunction = () => {
+      api.model.function(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+    const getevent = () => {
+      api.model.event(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const gettab = () => {
+      api.model.tag(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const wuhandleClick = (tab: TabsPaneContext) => {
+      state.activetab = tab.props.name;
+      switch (tab.props.name) {
+        case 'attr':
+          getproperty();
+          break;
+        case 'fun':
+          getfunction();
+          break;
+        case 'event':
+          getevent();
+          break;
+        case 'tab':
+          gettab();
+          break;
+      }
+    };
+
+    const handleClick = (tab: TabsPaneContext, event: Event) => {
+      console.log(tab, event);
+      if (tab.props.name == 4) {
+        //获取日志
+        getlog();
+        getlogtype();
+      } else if (tab.props.name == 2) {
+        getList();
+      } else if (tab.props.name == 3) {
+        getrunData();
+      }
+    };
+
+    const getrunData = () => {
+      api.instance.getrun_status({ id: state.detail.id }).then((res: any) => {
+          state.areaData = res
+          let properties=state.areaData.properties;
+
+          var temp = new Array();
+
+          properties.forEach(function (item, index) {
+              let datalist=item.list;
+              temp[index] = [];
+              var temps = new Array();
+              datalist.forEach(function (a, b) {
+                 if(b<15){
+                  temps.push(a);
+                 }
+              });
+              temp[index]['name']=item.name
+              temp[index]['key']=item.key
+              temp[index]['type']=item.type
+              temp[index]['unit']=item.unit
+              temp[index]['value']=item.value
+              temp[index]['list']=temps
+
+          });
+
+          state.areaData.properties=temp;
+      });
+
+    };
+
+    const getlogtype = () => {
+      api.instance.getlogcate({}).then((res: any) => {
+        state.logTypeData = res.list;
+      });
+    };
+
+    const getlog = () => {
+      state.logtableData.param.deviceKey = state.detail.key;
+      api.instance.getLogList(state.logtableData.param).then((res: any) => {
+        console.log(res, '22222222');
+        state.logtableData.data = res.list;
+        state.logtableData.total = res.Total;
+      });
+    };
+
+    const CkOption = () => {
+      if (state.developer_status == 2) {
+        api.instance.devoffline({ id: state.detail.id }).then((res: any) => {
+          ElMessage.success('操作成功');
+          state.developer_status = 1;
+        });
+      } else {
+        api.instance.devonline({ id: state.detail.id }).then((res: any) => {
+          ElMessage.success('操作成功');
+          state.developer_status = 2;
+        });
+      }
+    };
+    const tinyAreas = () => {
+      var data = state.data;
+
+      const tinyArea = new TinyArea('container', {
+        height: 60,
+        autoFit: false,
+        data,
+        smooth: true,
+        areaStyle: {
+          fill: '#d6e3fd',
+        },
+      });
+      tinyArea.render();
+    }
+    return {
+      tinyAreas,
+      editDicRef,
+      editAttrRef,
+      listDicRef,
+      editFunRef,
+      editEventRef,
+      editTabRef,
+      onOpenListDetail,
+      getrunData,
+      getlog,
+      getlogtype,
+      onLogDetail,
+      CkOption,
+      onRowDel,
+      onEditFun,
+      onEditEvent,
+      onEditTag,
+      onEditAttr,
+      getList,
+      getproperty,
+      getfunction,
+      getevent,
+      gettab,
+      wuhandleClick,
+      onOpenEditTab,
+      onOpenEditEvent,
+      onOpenEditAttr,
+      onOpenEditFun,
+      onOpenEditDic,
+      handleClick,
+      ...toRefs(state),
+    };
+  },
+});
+</script>
+  <style>
+.content {
+	background: #fff;
+	width: 100%;
+	padding: 20px;
+}
+.content-box {
+	background: #fff;
+	width: 100%;
+	padding: 20px;
+	margin-top: 20px;
+}
+.cont_box {
+	display: flex;
+}
+.cont_box .title {
+	font-size: 24px;
+}
+.cont_box .pro-status {
+	line-height: 40px;
+	margin-left: 30px;
+}
+.cont_box .pro-status .on {
+	background: #52c41a;
+}
+.cont_box .pro-status .off {
+	background: #c41a1a;
+}
+.cont_box .pro-status span {
+	position: relative;
+	top: -1px;
+	display: inline-block;
+	width: 6px;
+	height: 6px;
+	vertical-align: middle;
+	border-radius: 50%;
+	margin-right: 5px;
+}
+.cont_box .pro-option {
+	line-height: 40px;
+	margin-left: 10px;
+	color: #1890ff;
+	cursor: pointer;
+}
+.content-box .pro-box {
+	display: flex;
+	padding: 10px;
+  justify-content: space-between;
+
+}
+.content-box .pro-box .protitle {
+	font-size: 18px;
+	font-weight: bold;
+	line-height: 35px;
+}
+.content-box .pro-box .buttonedit {
+	border: 0px;
+	color: #1890ff;
+}
+table {
+	border-collapse: collapse;
+	text-indent: initial;
+	border-spacing: 2px;
+}
+tbody {
+	box-sizing: border-box;
+	display: table-row-group;
+	vertical-align: middle;
+	border-color: inherit;
+}
+
+tr {
+	display: table-row;
+	vertical-align: inherit;
+	border-color: inherit;
+}
+.ant-descriptions-view {
+	width: 100%;
+	overflow: hidden;
+	border-radius: 4px;
+}
+.ant-descriptions-view {
+	border: 1px solid #e8e8e8;
+}
+.ant-descriptions-view table {
+	width: 100%;
+	table-layout: fixed;
+}
+.ant-descriptions-view > table {
+	table-layout: auto;
+}
+.ant-descriptions-row {
+	border-bottom: 1px solid #e8e8e8;
+}
+.ant-descriptions-item-label {
+	color: rgba(0, 0, 0, 0.85);
+	font-weight: 400;
+	font-size: 14px;
+	line-height: 1.5;
+}
+.ant-descriptions-item-label {
+	padding: 16px 24px;
+	border-right: 1px solid #e8e8e8;
+}
+.ant-descriptions-item-label {
+	background-color: #fafafa;
+}
+.ant-descriptions-item-content {
+	padding: 16px 24px;
+	border-right: 1px solid #e8e8e8;
+	display: table-cell;
+	color: rgba(0, 0, 0, 0.65);
+	font-size: 14px;
+	line-height: 1.5;
+}
+.wu-box {
+	border: #e8e8e8 solid 1px;
+	padding: 20px;
+	width: 100%;
+}
+.wu-box .wu-title {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	padding: 20px;
+	border-bottom: #e8e8e8 1px solid;
+}
+.wu-box .wu-title .title {
+	font-size: 18px;
+}
+.ant-card {
+	box-sizing: border-box;
+	margin: 10px;
+	width: 23.2%;
+	font-size: 14px;
+	font-variant: tabular-nums;
+  border: 1px solid  var(--next-border-color-light);
+
+	line-height: 1.5;
+	list-style: none;
+	font-feature-settings: 'tnum';
+	position: relative;
+	border-radius: 2px;
+	transition: all 0.3s;
+}
+.ant-card-body {
+	padding: 24px;
+	zoom: 1;
+}
+.cardflex {
+	display: flex;
+	justify-content: space-between;
+}
+.statusname {
+	font-size: 30px;
+	margin-top: 10px;
+  margin-bottom: 15px;
+}
+.comtest {
+	margin-top: 20px;
+	height: 30px;
+	line-height: 30px;
+}
+</style>
+
+

+ 87 - 0
src/views/iot/device/channel/index.vue

@@ -0,0 +1,87 @@
+<template>
+	<div class="page">
+		<el-card shadow="hover">
+			<div class="search">
+				<el-form :model="params" :inline="true" ref="queryRef">
+					<el-form-item label="通道名称" prop="title">
+						<el-input v-model="params.title" placeholder="请输入通道名称" clearablestyle="width: 240px" @keyup.enter.native="getList(1)" />
+					</el-form-item>
+					<el-form-item label="注册码" prop="number">
+						<el-input v-model="params.number" placeholder="请输入注册码" clearablestyle="width: 240px" @keyup.enter.native="getList(1)" />
+					</el-form-item>
+					<el-form-item>
+						<el-button size="default" type="primary" class="ml10" @click="getList(1)">
+							<el-icon>
+								<ele-Search />
+							</el-icon>
+							查询
+						</el-button>
+						<el-button size="default" @click="resetQuery()">
+							<el-icon>
+								<ele-Refresh />
+							</el-icon>
+							重置
+						</el-button>
+						<el-button type="success" @click="addOrEdit()" v-auth="'add'">
+							<el-icon>
+								<ele-FolderAdd />
+							</el-icon>
+							新增通道
+						</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+			<el-table :data="tableData" style="width: 100%" v-loading="loading">
+				<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="number" label="注册码" align="center" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="slaveId" label="设备地址" align="center" show-overflow-tooltip></el-table-column>
+				<el-table-column label="操作" width="100" align="center">
+					<template #default="scope">
+						<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>
+					</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>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import EditForm from './component/edit.vue';
+import api from '/@/api/device/modbus';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import { useSearch } from '/@/hooks/useCommonModbus';
+
+const editFormRef = ref();
+const queryRef = ref();
+
+const { params, tableData, getList, loading } = useSearch(api.channel.getList, 'list', { title: '', number: '' });
+
+getList();
+
+const addOrEdit = async (row?: any) => {
+	editFormRef.value.open(row);
+};
+
+// 重置表单
+const resetQuery = () => {
+	queryRef.value.resetFields();
+	getList(1);
+};
+
+const onDel = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除接口:“${row.title}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.channel.deleteDevice({ number: row.number });
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+</script>

+ 190 - 0
src/views/iot/device/template/component/edit.vue

@@ -0,0 +1,190 @@
+<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-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>
+</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:{}
+}
+
+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:''
+			},
+      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')
+            })
+          }
+        }
+      });
+		};
+
+
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+      formRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>

+ 147 - 0
src/views/iot/device/template/component/list.vue

@@ -0,0 +1,147 @@
+<template>
+	<div class="system-edit-dic-container">
+		<el-dialog  v-model="isShowDialog" :show-close="false"  width="75%" :fullscreen="dialogFullScreen">
+		 <template #header="{ close, titleId, titleClass }">
+      <div class="my-header">
+        <h4 :id="titleId" :class="titleClass">数据记录</h4>
+		 
+		 <div>
+            <i class="iconfont "  :class="!dialogFullScreen ? 'icon-fullscreen' : 'icon-tuichuquanping'"   @click="quanping"  style="font-size: 22px;cursor: pointer;"></i>
+			<i class="el-icon"  @click="close" style="font-size: 22px;cursor: pointer;    margin-left: 10px; position: relative; top: 3px;"><svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa=""><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path></svg></i>
+			
+		</div>
+      </div>
+    </template>
+
+			<el-table :data="tableData.data" style="width: 100%"  v-loading="tableData.loading">
+				<el-table-column label="时间" prop="ts" :show-overflow-tooltip="true" />
+				<el-table-column label="属性值" prop="value" :show-overflow-tooltip="true" />
+			</el-table>
+			<pagination
+				v-show="tableData.total > 0"
+				:total="tableData.total"
+				v-model:page="tableData.param.pageNum"
+				v-model:limit="tableData.param.pageSize"
+				@pagination="typeList"
+			/>
+		</el-dialog>
+	</div>
+</template>
+ 
+<script lang="ts">
+import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import { Close } from '@element-plus/icons-vue';
+
+import api from '/@/api/device';
+import { ElMessage } from 'element-plus';
+
+interface DicState {
+	isShowDialog: boolean;
+}
+
+// 定义接口来定义对象的类型
+interface TableDataRow {
+	id: number;
+	name: string;
+	key: string;
+
+	createBy: string;
+}
+interface TableDataState {
+	ids: number[];
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+			id: number;
+		};
+	};
+}
+
+export default defineComponent({
+	name: 'deviceEditPro',
+	setup(prop, { emit }) {
+		const formRef = ref<HTMLElement | null>(null);
+		const state = reactive<DicState>({
+			isShowDialog: false,
+		    dialogFullScreen: false,
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+					id: 0,
+					propertyKey:'',
+				},
+			},
+		});
+		// 打开弹窗
+		const openDialog = (row: RuleFormState | null,devid) => {
+			resetForm();
+			if (row) {
+				console.log(row);
+				state.tableData.param.id = devid;
+				state.tableData.param.propertyKey=row.key
+				typeList();
+
+			}
+			state.isShowDialog = true;
+		};
+
+		const typeList = () => {
+			state.tableData.loading = true;
+			api.instance.getLogDetail(state.tableData.param).then((res: any) => {
+				state.tableData.data = res.List;
+				state.tableData.total = res.Total;
+				//state.ruleForm = res.data.dictType
+			}).finally(() => (state.tableData.loading = false));
+
+		};
+		const resetForm = () => {
+			state.tableData= {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+				},
+			}
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			state.isShowDialog = false;
+		};
+		const quanping=()=>{
+			state.dialogFullScreen = state.dialogFullScreen?false:true;
+		}
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+
+		return {
+            Close,
+			quanping,
+			typeList,
+			openDialog,
+			closeDialog,
+			onCancel,
+			formRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+<style scoped>
+.my-header {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+</style>

+ 873 - 0
src/views/iot/device/template/detail.vue

@@ -0,0 +1,873 @@
+<template>
+  <div class="system-dic-container">
+    <div class="content">
+      <div class="cont_box">
+        <div class="title">设备:{{ detail.name }}</div>
+        <div class="pro-status"><span :class="developer_status == 2 ? 'on' : 'off'"></span>{{ developer_status == 2 ? '在线' : '离线' }}</div>
+
+        <!-- <div class="pro-option" @click="CkOption">{{ developer_status == 2 ? '下线' : '上线' }}</div> -->
+      </div>
+    </div>
+
+		<div class="content-box">
+			<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+
+				<el-tab-pane label="运行状态" name="3">
+					<div style=" display: flex; padding: 10px;flex-wrap: wrap;" >
+						<div class="ant-card">
+							<div class="ant-card-body">
+								<div class="cardflex">
+									<div>设备状态</div>
+									<div @click="getrunData()" style="cursor: pointer;">
+										<el-icon style="font-size: 18px;">
+											<ele-Refresh />
+										</el-icon>
+									</div>
+								</div>
+
+								<div class="statusname" v-if="areaData.status==0">未启用</div>
+								<div class="statusname" v-if="areaData.status==1">离线</div>
+								<div class="statusname" v-if="areaData.status==2">在线</div>
+								<div class="cardflex comtest">
+									<div> 数据时间</div>
+									<div>{{areaData.lastOnlineTime || '未启用'}}</div>
+								</div>
+							</div>
+						</div>
+
+						<div class="ant-card" v-for="(item, index) in areaData.properties" :key="index">
+							<div class="ant-card-body">
+								<div class="cardflex">
+									<div>{{item.name}}</div>
+									<div style="cursor: pointer;">
+										<el-icon  style="font-size: 18px;"  @click="getrunData()">
+											<ele-Refresh />
+										</el-icon>
+										<el-icon  style="font-size: 18px;    margin-left: 10px;" @click="onOpenListDetail(item)">
+											<ele-Expand />
+										</el-icon>
+									</div>
+								</div>
+
+								<div class="statusname">{{item.value}}{{item.unit}}</div>
+								<div class="">
+									<devantd :json="item.list" :antdid="item.key" v-if="item.type=='int' || item.type=='float'"/>
+								</div>
+							</div>
+						</div>
+
+
+
+					</div>
+				</el-tab-pane>
+
+
+
+
+				<el-tab-pane label="设备信息" name="1">
+					<div class="pro-box">
+						<div class="protitle">设备信息</div>
+            <div>
+              <el-button type="primary" @click="onOpenEditDic(detail)">编辑</el-button>
+            </div>
+          </div>
+
+					<div class="ant-descriptions-view">
+						<table>
+							<tbody>
+								<tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">设备标识</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ detail.key }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">设备名称</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ detail.name }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">所属产品</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ detail.productName }}</td>
+
+								</tr>
+								<tr class="ant-descriptions-row">
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">消息协议</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.messageProtocol }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">链接协议</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.transportProtocol }}</td>
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">设备类型</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ prodetail.deviceType }}</td>
+								</tr>
+								<tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">固件版本</th>
+                  <td class="ant-descriptions-item-content" colspan="1">{{ prodetail.version }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">注册时间</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.updatedAt }}</td>
+									<th class="ant-descriptions-item-label ant-descriptions-item-colon">最后上线时间</th>
+									<td class="ant-descriptions-item-content" colspan="1">{{ prodetail.lastOnlineTime || '' }}</td>
+								</tr>
+                <tr class="ant-descriptions-row">
+                  <th class="ant-descriptions-item-label ant-descriptions-item-colon">说明</th>
+                  <td class="ant-descriptions-item-content" colspan="5">{{ prodetail.desc }}</td>
+                </tr>
+
+
+              </tbody>
+								</table>
+								</div>
+								</el-tab-pane>
+        <el-tab-pane label="物模型" name="2">
+          <div class="wu-box">
+            <el-tabs type="border-card" v-model="activetab" @tab-click="wuhandleClick">
+              <el-tab-pane label="属性定义" name="attr">
+                <div class="wu-title">
+                  <div class="title">属性定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditAttr()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'attr'">
+                  <el-table-column label="属性标识" align="center" prop="key" />
+                  <el-table-column label="属性名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="valueType" label="数据类型" width="100" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.type }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="decimals" label="精度" width="60" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.decimals }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="unit" label="单位" width="60" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.unit }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
+                      <el-tag type="success" size="small" v-else>读写</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="说明" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditAttr(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'attr')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="功能定义" name="fun">
+                <div class="wu-title">
+                  <div class="title">功能定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditFun()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'fun'">
+                  <el-table-column label="功能标识" align="center" prop="key" />
+                  <el-table-column label="名称" prop="name" :show-overflow-tooltip="true" />
+
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditFun(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'fun')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="事件定义" name="event">
+                <div class="wu-title">
+                  <div class="title">事件定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditEvent()">添加</el-button>
+                  </div>
+                </div>
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'event'">
+                  <el-table-column label="事件标识" align="center" prop="key" />
+                  <el-table-column label="名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="level" label="事件级别" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="primary" size="small" v-if="scope.row.level == 0">普通</el-tag>
+                      <el-tag type="warning" size="small" v-if="scope.row.level == 1">警告</el-tag>
+                      <el-tag type="danger" size="small" v-if="scope.row.level == 2">紧急</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditEvent(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'event')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+              <el-tab-pane label="标签定义" name="tab">
+                <div class="wu-title">
+                  <div class="title">标签定义</div>
+                  <div>
+                    <el-button type="primary" @click="onOpenEditTab()">添加</el-button>
+                  </div>
+                </div>
+
+
+                <el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'tab'">
+                  <el-table-column label="属性标识" align="center" prop="key" />
+                  <el-table-column label="属性名称" prop="name" :show-overflow-tooltip="true" />
+                  <el-table-column prop="valueType" label="数据类型" width="120" align="center">
+                    <template #default="scope">
+                      <span>{{ scope.row.valueType.type }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+                    <template #default="scope">
+                      <el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
+                      <el-tag type="success" size="small" v-else>读写</el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" />
+                  <el-table-column label="操作" width="300" align="center" fixed="right">
+                    <template #default="scope">
+                      <el-button size="small" text type="warning" @click="onEditTag(scope.row)">修改</el-button>
+                      <el-button size="small" text type="danger" @click="onRowDel(scope.row.key, 'tab')">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+            </el-tabs>
+            <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList" />
+          </div>
+        </el-tab-pane>
+
+
+        <el-tab-pane label="日志管理" name="4">
+          <div class="system-user-search mb15">
+            <el-form :model="logtableData.param" ref="queryRef" :inline="true" label-width="68px">
+              <el-form-item label="日志类型" prop="types">
+                <el-select v-model="logtableData.param.types" placeholder="日志类型" clearable size="default">
+                  <el-option v-for="item in logTypeData" :key="item" :label="item" :value="item" />
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="创建时间" prop="dateRange">
+                <el-date-picker v-model="logtableData.param.dateRange" size="default" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+              </el-form-item>
+              <el-form-item>
+                <el-button size="default" type="primary" class="ml10" @click="getlog">
+                  <el-icon>
+                    <ele-Search />
+                  </el-icon>
+                  查询
+                </el-button>
+                <el-button size="default" @click="resetQuery(queryRef)">
+                  <el-icon>
+                    <ele-Refresh />
+                  </el-icon>
+                  重置
+                </el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+          <el-table style="width: 100%" :data="logtableData.data">
+            <el-table-column label="类型" align="center" prop="type" />
+            <el-table-column label="时间" prop="ts" :show-overflow-tooltip="true" />
+
+            <el-table-column label="内容" prop="content" :show-overflow-tooltip="true" />
+            <el-table-column label="操作" width="300" align="center" fixed="right">
+              <template #default="scope">
+                <el-button size="small" text type="warning" @click="onLogDetail(scope.row)">查看</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <pagination v-show="logtableData.total > 0" :total="logtableData.total" v-model:page="logtableData.param.pageNum" v-model:limit="logtableData.param.pageSize" @pagination="getlog" />
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+    <EditDic ref="editDicRef" @typeList="typeList" />
+    <EditAttr ref="editAttrRef" @typeList="getproperty" />
+    <EditFun ref="editFunRef" @typeList="getfunction" />
+    <EditEvent ref="editEventRef" @typeList="getevent" />
+    <EditTab ref="editTabRef" @typeList="gettab" />
+    <ListDic ref="listDicRef" />
+
+    <el-dialog v-model="dialogVisible" title="返回Json数据" width="30%">
+      <JsonViewer :value="jsonData" boxed sort theme="jv-dark" @click="onKeyclick" />
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">关闭</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script lang="ts">
+import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
+
+import 'vue3-json-viewer/dist/index.css';
+
+import EditDic from '../product/component/editPro.vue';
+import EditAttr from '../product/component/editAttr.vue';
+import EditFun from '../product/component/editFun.vue';
+import EditEvent from '../product/component/editEvent.vue';
+import EditTab from '../product/component/editTab.vue';
+import devantd from '/@/components/devantd/index.vue';
+import ListDic from './component/list.vue';
+
+
+import { useRoute } from 'vue-router';
+
+import api from '/@/api/device';
+
+interface TableDataState {
+  ids: number[];
+  tableData: {
+    data: [];
+    total: number;
+    loading: boolean;
+    param: {
+      pageNum: number;
+      pageSize: number;
+      name: string;
+      deviceType: string;
+      status: string;
+      dateRange: string[];
+    };
+  };
+  logtableData: {
+    data: [];
+    total: number;
+    loading: boolean;
+    param: {
+      pageNum: number;
+      pageSize: number;
+      name: string;
+      deviceType: string;
+      status: string;
+      dateRange: string[];
+    };
+  };
+}
+export default defineComponent({
+  name: 'deviceEditPro',
+  components: { EditDic, EditAttr, EditFun, EditEvent, EditTab, devantd, ListDic },
+
+	setup(prop, context) {
+		const route = useRoute();
+		const editDicRef = ref();
+		const editAttrRef = ref();
+		const editFunRef = ref();
+		const listDicRef=ref();
+		const editEventRef = ref();
+		const editTabRef = ref();
+		const state = reactive<TableDataState>({
+			areaData:[],
+			isShowDialog: false,
+			dialogVisible: false,
+			logTypeData: [],
+			jsonData: '',
+			activeName: '3', // 分类数据
+			activetab: 'attr', // 分类数据
+			detail: [],
+			prodetail: [],
+			product_id: 0,
+			developer_status: 0,
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					productId: 0,
+					pageSize: 10,
+					status: '',
+					dateRange: [],
+				},
+			},
+			logtableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					productId: 0,
+					pageSize: 10,
+					status: '',
+					dateRange: [],
+				},
+			},
+		});
+
+		onMounted(() => {
+			const ids = route.params && route.params.id;
+			api.instance.detail(ids).then((res: any) => {
+				state.detail = res.data;
+				state.developer_status = res.data.status;
+				state.tableData.param.productId = res.data.product.id;
+				state.product_id = res.data.product.id;
+				getrunData();
+				api.product.detail(res.data.product.id).then((res: any) => {
+					state.prodetail = res.data;
+					console.log(res.data);
+				});
+
+				//第一次加载
+				api.model.property(state.tableData.param).then((res: any) => {
+					state.tableData.data = res.Data;
+					state.tableData.total = res.Total;
+				});
+			});
+
+		});
+
+
+    const onLogDetail = (row: TableDataRow) => {
+      state.jsonData = JSON.parse(row.content);
+      console.log(JSON.parse(row.content));
+      state.dialogVisible = true;
+    };
+
+    //编辑属性
+    const onEditAttr = (row: TableDataRow) => {
+      editAttrRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑功能
+    const onEditFun = (row: TableDataRow) => {
+      editFunRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑事件
+    const onEditEvent = (row: TableDataRow) => {
+      editEventRef.value.openDialog(row, state.product_id);
+    };
+
+    //编辑标签
+    const onEditTag = (row: TableDataRow) => {
+      editTabRef.value.openDialog(row, state.product_id);
+    };
+
+    //打开添加属性弹窗
+    const onOpenEditAttr = () => {
+      editAttrRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
+    };
+
+    //打开添加功能弹窗
+    const onOpenEditFun = () => {
+      editFunRef.value.openDialog({ product_id: state.product_id, id: 0 });
+    };
+    //打开添加事件弹窗
+    const onOpenEditEvent = () => {
+      editEventRef.value.openDialog({ product_id: state.product_id, id: 0, level: 0 });
+    };
+
+    //打开添加事件弹窗
+    const onOpenEditTab = () => {
+      editTabRef.value.openDialog({ product_id: state.product_id, id: 0, accessMode: 0 });
+    };
+
+    //查看日志列表
+    const onOpenListDetail = (row: TableDataRow) => {
+      listDicRef.value.openDialog(row, state.detail.id);
+    };
+
+    // 打开修改产品弹窗
+    const onOpenEditDic = (row: TableDataRow) => {
+      editDicRef.value.openDialog(row);
+    };
+
+    // 删除产品
+    const onRowDel = (key, type) => {
+      let msg = `此操作将永久删除该数据吗?,是否继续?`;
+
+      if (key.length === 0) {
+        ElMessage.error('请选择要删除的数据。');
+        return;
+      }
+      ElMessageBox.confirm(msg, '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          if (type == 'attr') {
+            api.model.propertydel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getproperty();
+            });
+          }
+          if (type == 'fun') {
+            api.model.functiondel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getfunction();
+            });
+          }
+          if (type == 'event') {
+            api.model.eventdel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              getevent();
+            });
+          }
+          if (type == 'tab') {
+            api.model.tagdel(state.product_id, key).then(() => {
+              ElMessage.success('删除成功');
+              tagdel();
+            });
+          }
+        })
+        .catch(() => { });
+    };
+
+    //根据不同类型获取列表
+    const getList = () => {
+      switch (state.activetab) {
+        case 'attr':
+          getproperty();
+          break;
+        case 'fun':
+          getfunction();
+          break;
+        case 'event':
+          getevent();
+          break;
+        case 'tab':
+          gettab();
+          break;
+      }
+    };
+
+    const getproperty = () => {
+      api.model.property(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const getfunction = () => {
+      api.model.function(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+    const getevent = () => {
+      api.model.event(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const gettab = () => {
+      api.model.tag(state.tableData.param).then((res: any) => {
+        state.tableData.data = res.Data;
+        state.tableData.total = res.Total;
+      });
+    };
+
+    const wuhandleClick = (tab: TabsPaneContext) => {
+      state.activetab = tab.props.name;
+      switch (tab.props.name) {
+        case 'attr':
+          getproperty();
+          break;
+        case 'fun':
+          getfunction();
+          break;
+        case 'event':
+          getevent();
+          break;
+        case 'tab':
+          gettab();
+          break;
+      }
+    };
+
+    const handleClick = (tab: TabsPaneContext, event: Event) => {
+      console.log(tab, event);
+      if (tab.props.name == 4) {
+        //获取日志
+        getlog();
+        getlogtype();
+      } else if (tab.props.name == 2) {
+        getList();
+      } else if (tab.props.name == 3) {
+        getrunData();
+      }
+    };
+
+    const getrunData = () => {
+      api.instance.getrun_status({ id: state.detail.id }).then((res: any) => {
+          state.areaData = res
+          let properties=state.areaData.properties;
+
+          var temp = new Array();
+
+          properties.forEach(function (item, index) {
+              let datalist=item.list;
+              temp[index] = [];
+              var temps = new Array();
+              datalist.forEach(function (a, b) {
+                 if(b<15){
+                  temps.push(a);
+                 }
+              });
+              temp[index]['name']=item.name
+              temp[index]['key']=item.key
+              temp[index]['type']=item.type
+              temp[index]['unit']=item.unit
+              temp[index]['value']=item.value
+              temp[index]['list']=temps
+
+          });
+
+          state.areaData.properties=temp;
+      });
+
+    };
+
+    const getlogtype = () => {
+      api.instance.getlogcate({}).then((res: any) => {
+        state.logTypeData = res.list;
+      });
+    };
+
+    const getlog = () => {
+      state.logtableData.param.deviceKey = state.detail.key;
+      api.instance.getLogList(state.logtableData.param).then((res: any) => {
+        console.log(res, '22222222');
+        state.logtableData.data = res.list;
+        state.logtableData.total = res.Total;
+      });
+    };
+
+    const CkOption = () => {
+      if (state.developer_status == 2) {
+        api.instance.devoffline({ id: state.detail.id }).then((res: any) => {
+          ElMessage.success('操作成功');
+          state.developer_status = 1;
+        });
+      } else {
+        api.instance.devonline({ id: state.detail.id }).then((res: any) => {
+          ElMessage.success('操作成功');
+          state.developer_status = 2;
+        });
+      }
+    };
+    const tinyAreas = () => {
+      var data = state.data;
+
+      const tinyArea = new TinyArea('container', {
+        height: 60,
+        autoFit: false,
+        data,
+        smooth: true,
+        areaStyle: {
+          fill: '#d6e3fd',
+        },
+      });
+      tinyArea.render();
+    }
+    return {
+      tinyAreas,
+      editDicRef,
+      editAttrRef,
+      listDicRef,
+      editFunRef,
+      editEventRef,
+      editTabRef,
+      onOpenListDetail,
+      getrunData,
+      getlog,
+      getlogtype,
+      onLogDetail,
+      CkOption,
+      onRowDel,
+      onEditFun,
+      onEditEvent,
+      onEditTag,
+      onEditAttr,
+      getList,
+      getproperty,
+      getfunction,
+      getevent,
+      gettab,
+      wuhandleClick,
+      onOpenEditTab,
+      onOpenEditEvent,
+      onOpenEditAttr,
+      onOpenEditFun,
+      onOpenEditDic,
+      handleClick,
+      ...toRefs(state),
+    };
+  },
+});
+</script>
+  <style>
+.content {
+	background: #fff;
+	width: 100%;
+	padding: 20px;
+}
+.content-box {
+	background: #fff;
+	width: 100%;
+	padding: 20px;
+	margin-top: 20px;
+}
+.cont_box {
+	display: flex;
+}
+.cont_box .title {
+	font-size: 24px;
+}
+.cont_box .pro-status {
+	line-height: 40px;
+	margin-left: 30px;
+}
+.cont_box .pro-status .on {
+	background: #52c41a;
+}
+.cont_box .pro-status .off {
+	background: #c41a1a;
+}
+.cont_box .pro-status span {
+	position: relative;
+	top: -1px;
+	display: inline-block;
+	width: 6px;
+	height: 6px;
+	vertical-align: middle;
+	border-radius: 50%;
+	margin-right: 5px;
+}
+.cont_box .pro-option {
+	line-height: 40px;
+	margin-left: 10px;
+	color: #1890ff;
+	cursor: pointer;
+}
+.content-box .pro-box {
+	display: flex;
+	padding: 10px;
+  justify-content: space-between;
+
+}
+.content-box .pro-box .protitle {
+	font-size: 18px;
+	font-weight: bold;
+	line-height: 35px;
+}
+.content-box .pro-box .buttonedit {
+	border: 0px;
+	color: #1890ff;
+}
+table {
+	border-collapse: collapse;
+	text-indent: initial;
+	border-spacing: 2px;
+}
+tbody {
+	box-sizing: border-box;
+	display: table-row-group;
+	vertical-align: middle;
+	border-color: inherit;
+}
+
+tr {
+	display: table-row;
+	vertical-align: inherit;
+	border-color: inherit;
+}
+.ant-descriptions-view {
+	width: 100%;
+	overflow: hidden;
+	border-radius: 4px;
+}
+.ant-descriptions-view {
+	border: 1px solid #e8e8e8;
+}
+.ant-descriptions-view table {
+	width: 100%;
+	table-layout: fixed;
+}
+.ant-descriptions-view > table {
+	table-layout: auto;
+}
+.ant-descriptions-row {
+	border-bottom: 1px solid #e8e8e8;
+}
+.ant-descriptions-item-label {
+	color: rgba(0, 0, 0, 0.85);
+	font-weight: 400;
+	font-size: 14px;
+	line-height: 1.5;
+}
+.ant-descriptions-item-label {
+	padding: 16px 24px;
+	border-right: 1px solid #e8e8e8;
+}
+.ant-descriptions-item-label {
+	background-color: #fafafa;
+}
+.ant-descriptions-item-content {
+	padding: 16px 24px;
+	border-right: 1px solid #e8e8e8;
+	display: table-cell;
+	color: rgba(0, 0, 0, 0.65);
+	font-size: 14px;
+	line-height: 1.5;
+}
+.wu-box {
+	border: #e8e8e8 solid 1px;
+	padding: 20px;
+	width: 100%;
+}
+.wu-box .wu-title {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	padding: 20px;
+	border-bottom: #e8e8e8 1px solid;
+}
+.wu-box .wu-title .title {
+	font-size: 18px;
+}
+.ant-card {
+	box-sizing: border-box;
+	margin: 10px;
+	width: 23.2%;
+	font-size: 14px;
+	font-variant: tabular-nums;
+  border: 1px solid  var(--next-border-color-light);
+
+	line-height: 1.5;
+	list-style: none;
+	font-feature-settings: 'tnum';
+	position: relative;
+	border-radius: 2px;
+	transition: all 0.3s;
+}
+.ant-card-body {
+	padding: 24px;
+	zoom: 1;
+}
+.cardflex {
+	display: flex;
+	justify-content: space-between;
+}
+.statusname {
+	font-size: 30px;
+	margin-top: 10px;
+  margin-bottom: 15px;
+}
+.comtest {
+	margin-top: 20px;
+	height: 30px;
+	line-height: 30px;
+}
+</style>
+
+

+ 90 - 0
src/views/iot/device/template/index.vue

@@ -0,0 +1,90 @@
+<template>
+	<div class="page">
+		<el-card shadow="hover">
+			<div class="search">
+				<el-form :model="params" :inline="true" ref="queryRef">
+					<el-form-item label="模板名称" prop="title">
+						<el-input v-model="params.title" placeholder="请输入模板名称" clearablestyle="width: 240px" @keyup.enter.native="getList(1)" />
+					</el-form-item>
+					<el-form-item>
+						<el-button size="default" type="primary" class="ml10" @click="getList(1)">
+							<el-icon>
+								<ele-Search />
+							</el-icon>
+							查询
+						</el-button>
+						<el-button size="default" @click="resetQuery()">
+							<el-icon>
+								<ele-Refresh />
+							</el-icon>
+							重置
+						</el-button>
+						<el-button type="success" @click="addOrEdit()" v-auth="'add'">
+							<el-icon>
+								<ele-FolderAdd />
+							</el-icon>
+							新增模板
+						</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+			<el-table :data="tableData" style="width: 100%" v-loading="loading">
+				<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>
+				<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>
+					</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>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import EditForm from './component/edit.vue';
+import api from '/@/api/device/modbus';
+import { ElMessageBox, ElMessage } from 'element-plus';
+import { useSearch } from '/@/hooks/useCommonModbus';
+
+const editFormRef = ref();
+const queryRef = ref();
+
+const { params, tableData, getList, loading } = useSearch(api.template.getList, 'list', { title: '', number: '' });
+
+getList();
+
+const addOrEdit = async (row?: any) => {
+	editFormRef.value.open(row);
+};
+
+// 重置表单
+const resetQuery = () => {
+	queryRef.value.resetFields();
+	getList(1);
+};
+
+const onDel = (row: any) => {
+	ElMessageBox.confirm(`此操作将删除接口:“${row.title}”,是否继续?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		await api.channel.deleteDevice({ number: row.number });
+		ElMessage.success('删除成功');
+		getList();
+	});
+};
+</script>

+ 165 - 0
src/views/system/datahub/modeling/component/juhe.vue

@@ -0,0 +1,165 @@
+<template>
+	<div class="system-edit-dic-container">
+		<el-dialog title="设置聚合" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="110px">
+				<el-form-item label="分组节点" prop="GroupNodeKey">
+					<el-select v-model="ruleForm.GroupNodeKey" filterable placeholder="请选择分组节点" class="w100">
+						<el-option v-for="item in sourceData" :key="item.id" :label="item.name" :value="item.key">
+							<span style="float: left">{{ item.name }}</span>
+							<span style="float: right; font-size: 13px">{{ item.key }}</span>
+						</el-option>
+					</el-select>
+				</el-form-item>
+
+				<el-form-item label="时间窗口节点" prop="TimeNodeKey">
+					<el-select v-model="ruleForm.TimeNodeKey" filterable placeholder="请选择时间窗口节点" class="w100">
+						<el-option v-for="item in sourceData" :key="item.id" :label="item.name" :value="item.key">
+							<span style="float: left">{{ item.name }}</span>
+							<span style="float: right; font-size: 13px">{{ item.key }}</span>
+						</el-option>
+					</el-select>
+				</el-form-item>
+
+				<el-form-item label="时间值" prop="Duration">
+					<el-input v-model="ruleForm.Duration" placeholder="请输入时间值" class="w-35" />
+					<el-select v-model="ruleForm.TimeUnit" placeholder="请选择单位">
+						<el-option v-for="item in unitData" :key="item.value" :label="item.label" :value="item.value" />
+					</el-select>
+				</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">设置</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import api from '/@/api/datahub';
+import { ElMessage } from 'element-plus';
+
+interface RuleFormState {
+	id?: number;
+	GroupNodeKey: string;
+	TimeNodeKey: string;
+	Duration: string;
+	TimeUnit: string;
+}
+interface DicState {
+	isShowDialog: boolean;
+	ruleForm: RuleFormState;
+	rules: {};
+	unitData: [];
+}
+
+export default defineComponent({
+	name: 'Edit',
+
+	setup(prop, { emit }) {
+		const formRef = ref<HTMLElement | null>(null);
+
+		const state = reactive<DicState>({
+			isShowDialog: false,
+			sourceData: [],
+			unitData: [
+				{
+					label: '秒',
+					value: 1,
+				},
+				{
+					label: '分',
+					value: 2,
+				},
+				{
+					label: '时',
+					value: 3,
+				},
+				{
+					label: '天',
+					value: 4,
+				},
+			],
+			ruleForm: {
+				id: 0,
+				GroupNodeKey: '',
+				TimeNodeKey: '',
+				Duration: '',
+				TimeUnit: '',
+			},
+			rules: {
+				GroupNodeKey: [{ required: true, message: '分组节点key不能为空', trigger: 'blur' }],
+				TimeNodeKey: [{ required: true, message: '时间窗口节点key不能为空', trigger: 'blur' }],
+				Duration: [{ required: true, message: '时间值不能为空', trigger: 'blur' }],
+				TimeUnit: [{ required: true, message: '时间单位不能为空', trigger: 'blur' }],
+			},
+		});
+
+		// 打开弹窗
+		const openDialog = (row: RuleFormState | null) => {
+			resetForm();
+
+			if (row) {
+				state.ruleForm.id = row.id;
+				api.template.aggregate_from(row.id).then((res: any) => {
+					api.node.getList({ sourceId: res.id }).then((res: any) => {
+						state.sourceData = res.list;
+						state.ruleForm.GroupNodeKey = row.groupNodeKey;
+						state.ruleForm.TimeNodeKey = row.timeNodeKey;
+						if(row.duration>0){
+							state.ruleForm.Duration = row.duration;
+							state.ruleForm.TimeUnit = row.timeUnit;
+						}
+					});
+				});
+			}
+			state.isShowDialog = true;
+		};
+		const resetForm = () => {
+			state.ruleForm = {
+				id: 0,
+				GroupNodeKey: '',
+				TimeNodeKey: '',
+				Duration: '',
+				TimeUnit: '',
+			};
+		};
+
+		// 关闭弹窗
+		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) {
+					api.template.aggregate(state.ruleForm).then(() => {
+						ElMessage.success('设置成功');
+						closeDialog(); // 关闭弹窗
+						emit('typeList');
+					});
+				}
+			});
+		};
+
+		return {
+			openDialog,
+			closeDialog,
+			onCancel,
+			onSubmit,
+			formRef,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+

+ 241 - 211
src/views/system/datahub/modeling/index.vue

@@ -1,238 +1,268 @@
 <template>
-  <div class="system-dic-container">
-    <el-card shadow="hover">
-      <div class="system-user-search mb15">
-        <el-form :model="tableData.param" ref="queryRef" :inline="true">
-          <!-- <el-form-item label="模型标识" prop="key">
+	<div class="system-dic-container">
+		<el-card shadow="hover">
+			<div class="system-user-search mb15">
+				<el-form :model="tableData.param" ref="queryRef" :inline="true">
+					<!-- <el-form-item label="模型标识" prop="key">
             <el-input v-model="tableData.param.key" placeholder="请输入模型标识" clearable size="default" style="width: 240px" @keyup.enter.native="typeList" />
           </el-form-item> -->
-          <el-form-item label="模型名称" prop="name">
-            <el-input v-model="tableData.param.name" placeholder="请输入模型名称" clearable size="default" style="width: 240px" @keyup.enter.native="typeList" />
-          </el-form-item>
-          <el-form-item label="模型类型" prop="type">
-            <el-select v-model="tableData.param.type"  placeholder="请选择模型类型" class="w100" >
-						<el-option v-for="item in datahub_model_type" :key="item.value" :label="item.label" :value="item.value" />
-					</el-select>
-          </el-form-item>
-          <el-form-item>
-            <el-button size="default" type="primary" class="ml10" @click="typeList">
-              <el-icon>
-                <ele-Search />
-              </el-icon>
-              查询
-            </el-button>
-            <el-button size="default" @click="resetQuery(queryRef)">
-              <el-icon>
-                <ele-Refresh />
-              </el-icon>
-              重置
-            </el-button>
-            <el-button size="default" type="success" class="ml10" @click="onOpenAdd" v-auth="'add'">
-              <el-icon>
-                <ele-FolderAdd />
-              </el-icon>
-              新增模型
-            </el-button>
-            <el-button size="default" type="danger" class="ml10" @click="onRowDel()" v-auth="'del'">
-              <el-icon>
-                <ele-Delete />
-              </el-icon>
-              删除
-            </el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-      <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="ID" align="center" prop="id" width="80" v-col="'id'" />
-       <!-- <el-table-column label="模型标识" prop="key" :show-overflow-tooltip="true" v-col="'key'"/> -->
-        <el-table-column label="模型名称" prop="name" :show-overflow-tooltip="true" v-col="'name'" />
-        <el-table-column label="类型" prop="typeName" :show-overflow-tooltip="true" v-col="'typeName'" />
+					<el-form-item label="模型名称" prop="name">
+						<el-input
+							v-model="tableData.param.name"
+							placeholder="请输入模型名称"
+							clearable
+							size="default"
+							style="width: 240px"
+							@keyup.enter.native="typeList"
+						/>
+					</el-form-item>
+					<el-form-item label="模型类型" prop="type">
+						<el-select v-model="tableData.param.type" placeholder="请选择模型类型" class="w100">
+							<el-option v-for="item in datahub_model_type" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+					</el-form-item>
+					<el-form-item>
+						<el-button size="default" type="primary" class="ml10" @click="typeList">
+							<el-icon>
+								<ele-Search />
+							</el-icon>
+							查询
+						</el-button>
+						<el-button size="default" @click="resetQuery(queryRef)">
+							<el-icon>
+								<ele-Refresh />
+							</el-icon>
+							重置
+						</el-button>
+						<el-button size="default" type="success" class="ml10" @click="onOpenAdd" v-auth="'add'">
+							<el-icon>
+								<ele-FolderAdd />
+							</el-icon>
+							新增模型
+						</el-button>
+						<el-button size="default" type="danger" class="ml10" @click="onRowDel()" v-auth="'del'">
+							<el-icon>
+								<ele-Delete />
+							</el-icon>
+							删除
+						</el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+			<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="ID" align="center" prop="id" width="80" v-col="'id'" />
+				<!-- <el-table-column label="模型标识" prop="key" :show-overflow-tooltip="true" v-col="'key'"/> -->
+				<el-table-column label="模型名称" prop="name" :show-overflow-tooltip="true" v-col="'name'" />
+				<el-table-column label="类型" prop="typeName" :show-overflow-tooltip="true" v-col="'typeName'" />
 
-        <el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" v-col="'desc'" />
-        <el-table-column prop="status" label="状态" width="100" align="center" v-col="'status'">
-          <template #default="scope">
-            <el-tag type="success" size="small" v-if="scope.row.status==1">已发布</el-tag>
-            <el-tag type="info" size="small" v-else>未发布</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="200" align="center" v-col="'createdAt'"></el-table-column>
+				<el-table-column label="描述" prop="desc" :show-overflow-tooltip="true" v-col="'desc'" />
+				<el-table-column prop="status" label="状态" width="100" align="center" v-col="'status'">
+					<template #default="scope">
+						<el-tag type="success" size="small" v-if="scope.row.status == 1">已发布</el-tag>
+						<el-tag type="info" size="small" v-else>未发布</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="createdAt" label="创建时间" width="200" align="center" v-col="'createdAt'"></el-table-column>
 
-        <el-table-column label="操作" width="280" align="center" fixed="right">
-          <template #default="scope">
-            <router-link :to="'/config/datahub/modeling/' + scope.row.id" class="link-type" style="padding-right: 12px; font-size: 12px; color: #409eff" v-auth="'detail'">
-              <span>字段管理</span>
-            </router-link>
-            <el-button size="small" text type="success" @click="onOpenRecord(scope.row)" v-if="scope.row.status==1" v-auth="'record'">数据记录</el-button>
-            <el-button size="small" text type="warning" @click="onOpenEdit(scope.row)" v-if="scope.row.status==0" v-auth="'edit'">修改</el-button>
-            <el-button size="small" text type="danger" @click="onRowDel(scope.row)" v-if="scope.row.status==0" v-auth="'del'">删除</el-button>
-            <el-button size="small" text type="primary" @click="copy(scope.row)" v-auth="'copy'">复制</el-button>
-
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="typeList" />
-    </el-card>
-    <EditDic ref="editDicRef" @typeList="typeList" />
-    <Detail ref="detailRef" />
-  </div>
+				<el-table-column label="操作" width="280" align="center" fixed="right">
+					<template #default="scope">
+						<router-link
+							:to="'/config/datahub/modeling/' + scope.row.id"
+							class="link-type"
+							style="padding-right: 12px; font-size: 12px; color: #409eff"
+							v-auth="'detail'"
+						>
+							<span>字段管理</span>
+						</router-link>
+						<el-button size="small" text type="success" @click="onOpenRecord(scope.row)" v-if="scope.row.status == 1" v-auth="'record'"
+							>数据记录</el-button
+						>
+						<el-button size="small" text type="danger" @click="onOpenJuhe(scope.row)" v-auth="'juhe'">聚合设置</el-button>
+						<el-button size="small" text type="warning" @click="onOpenEdit(scope.row)" v-if="scope.row.status == 0" v-auth="'edit'">修改</el-button>
+						<el-button size="small" text type="danger" @click="onRowDel(scope.row)" v-if="scope.row.status == 0" v-auth="'del'">删除</el-button>
+						<el-button size="small" text type="primary" @click="copy(scope.row)" v-auth="'copy'">复制</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<pagination
+				v-show="tableData.total > 0"
+				:total="tableData.total"
+				v-model:page="tableData.param.pageNum"
+				v-model:limit="tableData.param.pageSize"
+				@pagination="typeList"
+			/>
+		</el-card>
+		<EditDic ref="editDicRef" @typeList="typeList" />
+		<Detail ref="detailRef" />
+		<Juhe ref="juheRef" />
+	</div>
 </template>
 
 <script lang="ts">
-import { toRefs, reactive, onMounted, ref, defineComponent,getCurrentInstance } from 'vue';
+import { toRefs, reactive, onMounted, ref, defineComponent, getCurrentInstance } from 'vue';
 import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
 import EditDic from './component/edit.vue';
 import Detail from './component/detail.vue';
+import Juhe from './component/juhe.vue';
 import api from '/@/api/datahub';
 
 // 定义接口来定义对象的类型
 interface TableDataRow {
-  id: number;
-  name: string;
-  key: string;
+	id: number;
+	name: string;
+	key: string;
 
-  createBy: string;
+	createBy: string;
 }
 interface TableDataState {
-  ids: number[];
-  tableData: {
-    data: Array<TableDataRow>;
-    total: number;
-    loading: boolean;
-    param: {
-      pageNum: number;
-      pageSize: number;
-      name: string;
-      key: string;
-      type: string;
-    };
-  };
+	ids: number[];
+	tableData: {
+		data: Array<TableDataRow>;
+		total: number;
+		loading: boolean;
+		param: {
+			pageNum: number;
+			pageSize: number;
+			name: string;
+			key: string;
+			type: string;
+		};
+	};
 }
 
 export default defineComponent({
-  name: 'sourcelist',
-  components: { EditDic, Detail },
-  setup() {
-    const addDicRef = ref();
-    const editDicRef = ref();
-    const detailRef = ref();
-    const queryRef = ref();
-    const { proxy } = getCurrentInstance() as any;
-
-const { datahub_model_type } = proxy.useDict('datahub_model_type');
-    const state = reactive<TableDataState>({
-      tableData: {
-        data: [],
-        total: 0,
-        loading: false,
-        param: {
-          pageNum: 1,
-          pageSize: 10,
-          name: '',
-          key: '',
-          type: '',
-        },
-      },
-    });
-    // 初始化表格数据
-    const initTableData = () => {
-      typeList();
-    };
-    const typeList = () => {
-      state.tableData.loading = true;
-      api.template.getList(state.tableData.param).then((res: any) => {
-        state.tableData.data = res.list;
-        state.tableData.total = res.Total;
-      }).finally(() => (state.tableData.loading = false));
-    };
-    // 打开新增菜单弹窗
-    const onOpenAdd = (row?: TableDataRow) => {
-      editDicRef.value.openDialog(row?.id);
-    };
-    // 打开修改模型弹窗
-    const onOpenEdit = (row: TableDataRow) => {
-      editDicRef.value.openDialog({ ...row });
-    };
-    //打开数据记录
-    const onOpenRecord = (row: TableDataRow) => {
-      detailRef.value.openDialog(row);
-    };
-    const onRowDel = (row?: TableDataRow) => {
-      let msg = '你确定要删除所选数据?';
-      let ids: number[] = [];
-      if (row) {
-        msg = `此操作将永久删除模型:“${row.name}”,是否继续?`;
-        ids = [row.id];
-      } else {
-        ids = state.ids;
-      }
-      if (ids.length === 0) {
-        ElMessage.error('请选择要删除的数据。');
-        return;
-      }
-      ElMessageBox.confirm(msg, '提示', {
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning',
-      })
-        .then(() => {
-          api.template.delete(ids).then(() => {
-            ElMessage.success('删除成功');
-            typeList();
-          });
-        })
-        .catch(() => { });
-    };
-
-    //复制数据
-    const copy = (row: TableDataRow) => {
-      ElMessageBox.confirm("确定要复制该数据吗?", '提示', {
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning',
-      })
-        .then(() => {
+	name: 'sourcelist',
+	components: { EditDic, Detail, Juhe },
+	setup() {
+		const addDicRef = ref();
+		const editDicRef = ref();
+		const juheRef = ref();
+		const detailRef = ref();
+		const queryRef = ref();
+		const { proxy } = getCurrentInstance() as any;
 
-          api.template.copy({ id: row.id }).then(() => {
-            ElMessage.success('复制成功');
-            typeList();
-          });
-        })
-        .catch(() => { });
-    }
+		const { datahub_model_type } = proxy.useDict('datahub_model_type');
+		const state = reactive<TableDataState>({
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+					pageNum: 1,
+					pageSize: 10,
+					name: '',
+					key: '',
+					type: '',
+				},
+			},
+		});
+		// 初始化表格数据
+		const initTableData = () => {
+			typeList();
+		};
+		const typeList = () => {
+			state.tableData.loading = true;
+			api.template
+				.getList(state.tableData.param)
+				.then((res: any) => {
+					state.tableData.data = res.list;
+					state.tableData.total = res.Total;
+				})
+				.finally(() => (state.tableData.loading = false));
+		};
+		// 打开新增菜单弹窗
+		const onOpenAdd = (row?: TableDataRow) => {
+			editDicRef.value.openDialog(row?.id);
+		};
+		// 打开修改模型弹窗
+		const onOpenEdit = (row: TableDataRow) => {
+			editDicRef.value.openDialog({ ...row });
+		};
+		//打开数据记录
+		const onOpenRecord = (row: TableDataRow) => {
+			detailRef.value.openDialog(row);
+		};
+		//聚合设置
+		const onOpenJuhe = (row: TableDataRow) => {
+			juheRef.value.openDialog(row);
+		};
+		const onRowDel = (row?: TableDataRow) => {
+			let msg = '你确定要删除所选数据?';
+			let ids: number[] = [];
+			if (row) {
+				msg = `此操作将永久删除模型:“${row.name}”,是否继续?`;
+				ids = [row.id];
+			} else {
+				ids = state.ids;
+			}
+			if (ids.length === 0) {
+				ElMessage.error('请选择要删除的数据。');
+				return;
+			}
+			ElMessageBox.confirm(msg, '提示', {
+				confirmButtonText: '确认',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(() => {
+					api.template.delete(ids).then(() => {
+						ElMessage.success('删除成功');
+						typeList();
+					});
+				})
+				.catch(() => {});
+		};
 
+		//复制数据
+		const copy = (row: TableDataRow) => {
+			ElMessageBox.confirm('确定要复制该数据吗?', '提示', {
+				confirmButtonText: '确认',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(() => {
+					api.template.copy({ id: row.id }).then(() => {
+						ElMessage.success('复制成功');
+						typeList();
+					});
+				})
+				.catch(() => {});
+		};
 
-    // 页面加载时
-    onMounted(() => {
-      initTableData();
-    });
-    /** 重置按钮操作 */
-    const resetQuery = (formEl: FormInstance | undefined) => {
-      if (!formEl) return;
-      formEl.resetFields();
-      typeList();
-    };
-    // 多选框选中数据
-    const handleSelectionChange = (selection: TableDataRow[]) => {
-      state.ids = selection.map((item) => item.id);
-    };
+		// 页面加载时
+		onMounted(() => {
+			initTableData();
+		});
+		/** 重置按钮操作 */
+		const resetQuery = (formEl: FormInstance | undefined) => {
+			if (!formEl) return;
+			formEl.resetFields();
+			typeList();
+		};
+		// 多选框选中数据
+		const handleSelectionChange = (selection: TableDataRow[]) => {
+			state.ids = selection.map((item) => item.id);
+		};
 
-    return {
-      addDicRef,
-      editDicRef,
-      detailRef,
-      queryRef,
-      onOpenRecord,
-      datahub_model_type,
-      onOpenAdd,
-      onOpenEdit,
-      onRowDel,
-      copy,
-      typeList,
-      resetQuery,
-      handleSelectionChange,
-      ...toRefs(state),
-    };
-  },
+		return {
+			addDicRef,
+			editDicRef,
+			detailRef,
+			juheRef,
+			queryRef,
+			onOpenRecord,
+			onOpenJuhe,
+			datahub_model_type,
+			onOpenAdd,
+			onOpenEdit,
+			onRowDel,
+			copy,
+			typeList,
+			resetQuery,
+			handleSelectionChange,
+			...toRefs(state),
+		};
+	},
 });
 </script>

+ 27 - 23
src/views/system/monitor/cache/index.vue

@@ -31,7 +31,7 @@
                                 <div class="cell-card">平均存活时间: </div>
                             </td>
                             <td>
-                                <div class="cell-card">{{ sysInfo.keyspace.db0.avg_ttl }}</div>
+                                <div class="cell-card">{{timeFormat(sysInfo.keyspace.db0.avg_ttl)}}</div>
                             </td>
                         </tr>
                     </tbody>
@@ -69,7 +69,7 @@
                                 <div class="cell-card">输入缓冲区中占用的最大容量:</div>
                             </td>
                             <td>
-                                <div class="cell-card">{{ sysInfo.clients.client_recent_max_input_buffer }}</div>
+                                <div class="cell-card">{{ memorySizeFormat(sysInfo.clients.client_recent_max_input_buffer) }}</div>
                             </td>
                         </tr>
                         <tr>
@@ -89,14 +89,14 @@
             <el-card class="box-card-height" style="height:auto">
                 <template #header>
                     <div class="card-header">
-                        <span>info CPU</span>
+                        <span>CPU 信息</span>
                     </div>
                 </template>
                 <table cellspacing="0" style="height: 230px;width: 100%">
                     <tbody v-if="sysInfo.cpu">
                         <tr>
                             <td>
-                                <div class="cell-card">Redis主进程在内核态所占用的CPU时钟总和: </div>
+                                <div class="cell-card" title="Redis主进程在内核态所占用的CPU时钟总和">主进程内核态占用CPU时钟: </div>
                             </td>
                             <td>
                                 <div class="cell-card">{{lengthToFixed2(sysInfo.cpu.used_cpu_sys)}}</div>
@@ -104,7 +104,7 @@
                         </tr>
                         <tr>
                             <td>
-                                <div class="cell-card">Redis主进程在用户态所占用的CPU时钟总和: </div>
+                                <div class="cell-card" title="Redis主进程在用户态所占用的CPU时钟总和">主进程用户态占用CPU时钟: </div>
                             </td>
                             <td>
                                 <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_user) }}</div>
@@ -113,7 +113,7 @@
 
                         <tr>
                             <td>
-                                <div class="cell-card">Redis子进程在内核态所占用的CPU时钟总和:</div>
+                                <div class="cell-card" title="Redis子进程在内核态所占用的CPU时钟总和">子进程内核态占用CPU时钟:</div>
                             </td>
                             <td>
                                 <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_sys_children) }}</div>
@@ -121,7 +121,7 @@
                         </tr>
                         <tr>
                             <td>
-                                <div class="cell-card">Redis子进程在用户态所占用的CPU时钟总和:</div>
+                                <div class="cell-card" title="Redis子进程在用户态所占用的CPU时钟总和">子进程用户态占用CPU时钟:</div>
                             </td>
                             <td>
                                 <div class="cell-card">{{ lengthToFixed2(sysInfo.cpu.used_cpu_user_children) }}</div>
@@ -138,7 +138,7 @@
         <el-card class="box-card-height" style="height:auto">
             <template #header>
                 <div class="card-header">
-                    <span>server信息</span>
+                    <span>服务信息</span>
                 </div>
             </template>
             <table cellspacing="0" style="height: 300px;width: 100%">
@@ -227,7 +227,11 @@
                             <div class="cell-card">Redis分配器分配的内存总量: </div>
                         </td>
                         <td>
-                            <div class="cell-card">{{ sysInfo.memory.used_memory }}</div>
+                            <div class="cell-card">
+                              {{ memorySizeFormat(sysInfo.memory.used_memory) }} -
+                              {{ lengthToFixed2(sysInfo.memory.used_memory/sysInfo.memory.maxmemory)}}%
+
+                            </div>
                         </td>
                     </tr>
                     <tr>
@@ -235,7 +239,7 @@
                             <div class="cell-card">以可读的格式返回used_memory: </div>
                         </td>
                         <td>
-                            <div class="cell-card">{{ sysInfo.memory.used_memory_human }}</div>
+                            <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_human) }}</div>
                         </td>
                     </tr>
 
@@ -244,7 +248,7 @@
                             <div class="cell-card">从操作系统的角度,Redis进程占用的物理内存总量:</div>
                         </td>
                         <td>
-                            <div class="cell-card">{{ sysInfo.memory.used_memory_rss }}</div>
+                            <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_rss) }}</div>
                         </td>
                     </tr>
                     <tr>
@@ -252,7 +256,7 @@
                             <div class="cell-card">内存使用的最大值:</div>
                         </td>
                         <td>
-                        <div class="cell-card">{{ sysInfo.memory.used_memory_peak }}</div>
+                        <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_peak) }}</div>
                         </td>
                     </tr>
                     <tr>
@@ -260,7 +264,7 @@
                             <div class="cell-card">以可读的格式返回used_memory_peak:</div>
                         </td>
                         <td>
-                            <div class="cell-card">{{ sysInfo.memory.used_memory_peak_human }}</div>
+                            <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_peak_human) }}</div>
                         </td>
                     </tr>
                     <tr>
@@ -268,7 +272,7 @@
                             <div class="cell-card">Lua引擎所消耗的内存大小:</div>
                         </td>
                         <td>
-                            <div class="cell-card">{{ sysInfo.memory.used_memory_lua }}</div>
+                            <div class="cell-card">{{ memorySizeFormat(sysInfo.memory.used_memory_lua) }}</div>
                         </td>
                     </tr>
                     <tr>
@@ -287,7 +291,7 @@
                             <div class="cell-card">{{ sysInfo.memory.mem_allocator }}</div>
                         </td>
                     </tr>
-                
+
                 </tbody>
             </table>
           <!-- <div style="height: 250px" ref="chartsWarningRef6"></div> -->
@@ -311,12 +315,12 @@
                 <el-form-item label="key数量">{{sysInfo.stats.evicted_keys}}</el-form-item>
 
             </el-form>
-              
+
             <el-form v-if="sysInfo.stats" label-position="right" label-width="160px" class="flex1" style="max-width: 460px">
-                <el-form-item  label="输入总网络流量">{{ memorySizeFormat(sysInfo.stats.total_net_input_bytes) }}</el-form-item>
-                <el-form-item label="输出总网络流量">{{memorySizeFormat(sysInfo.stats.total_net_output_bytes)}}</el-form-item>
-                <el-form-item label="每秒输入字节数">{{sysInfo.stats.instantaneous_input_kbps}}</el-form-item>
-                <el-form-item label="每秒输出字节数">{{sysInfo.stats.instantaneous_output_kbps}}</el-form-item>
+                <el-form-item  label="网络总入流量">{{ memorySizeFormat(sysInfo.stats.total_net_input_bytes) }}</el-form-item>
+                <el-form-item label="网络总出流量">{{memorySizeFormat(sysInfo.stats.total_net_output_bytes)}}</el-form-item>
+                <el-form-item label="每秒输入量">{{sysInfo.stats.instantaneous_input_kbps}} kb/s</el-form-item>
+                <el-form-item label="每秒输出量">{{sysInfo.stats.instantaneous_output_kbps}} kb/s</el-form-item>
                 <el-form-item label="最近fork消耗时间">{{sysInfo.stats.evicted_keys}} μs</el-form-item>
             </el-form>
 
@@ -406,7 +410,7 @@ export default defineComponent({
     //   "runTime": '', "startTime": "-", "intranet_ip": "-"
     // });
 
-    // 
+    //
     // const stats = reactive({
     //   "total_connections_received": "-",
     //   "arch": "-",
@@ -422,7 +426,7 @@ export default defineComponent({
     //   "startTime": "-",
     //   "intranet_ip": "-"
     // });
-    // 
+    //
 
     // function goInfo(event: { data: any; }) {
     //   const data = JSON.parse(event.data);
@@ -776,7 +780,7 @@ export default defineComponent({
         const data = JSON.parse(event.data);
         state.sysInfo.cpu = data
     }
-    
+
 
     function statsInfoMsg(event: { data: any; }) {
         const data = JSON.parse(event.data);

+ 0 - 1
src/views/system/monitor/server/index.vue

@@ -601,7 +601,6 @@ export default defineComponent({
 
     function displayHost(event: { data: any; }) {
       const data = JSON.parse(event.data);
-      console.log(data)
       state.sysInfo.os = data.os
       state.sysInfo.kernelArch = data.kernelArch
       state.sysInfo.sysComputerName = data.hostname

Неке датотеке нису приказане због велике количине промена