<template> <div class="headerInfo" v-show="tapsShow"> <div class="header"> <div class="headerItem" v-for="(item, index) in headerData" :key="index"> <div class="name">{{ item.name }}</div> <div class="value">{{ item.value }}</div> </div> </div> </div> <!-- <div class="mainTable" :style="{ height: tapsShow ? 'calc(100vh - 5.7rem - 222px)' : 'calc(100vh - 5.7rem - 60px)' }" > --> <div class="mainTable" :style="{ height: `${sidebarHeight}px` }"> <!-- <n-spin :show="loadingShow"> --> <vxe-grid ref="tableRef" class="tableGrid" align="center" auto-resize keep-source :height="sidebarHeight - 4" header-row-class-name="headerRowClass" header-cell-class-name="headerCellClass" row-class-name="tableRowClass" cell-class-name="tableCellClass" :sort-config="{ multiple: true, trigger: 'cell' }" :stripe="!tableBorder" :border="tableBorder" :column-config="{ resizable: true, useKey: true }" :row-config="{ useKey: true }" :span-method="mergeRowMethod" :columns="tableColumn" :data="tableData" :loading="loadingShow" show-overflow @cell-dblclick="cellDBLClickEvent" > <template #deviceuuid_default="{ row }"> <div class="title"> <svg-icon icon-class="warning_lights" style="fill: currentColor; width: 15px; height: 15px; color: green" v-if="row.deviceuuid.deviceStatus === 0" /> <svg-icon icon-class="warning_lights" style="fill: currentColor; width: 15px; height: 15px; color: red" v-if="row.deviceuuid.deviceStatus === 2" /> <span class="name" @click.native="nameClick(row.deviceuuid)">{{ row.gTitle }}</span> </div> </template> <template #pager> <!--使用 pager 插槽--> <vxe-pager class="tablePage" :layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']" v-model:current-page="tablePage.currentPage" v-model:page-size="tablePage.pageSize" auto-hidden :total="tablePage.total" @page-change="handlePageChange" > </vxe-pager> </template> </vxe-grid> <!-- <vxe-grid class="tableGrid" v-bind="gridOptions"> </vxe-grid> --> <!-- </n-spin> --> </div> <vxe-modal :title="modalTitle" v-model="editModal"> <template #default> <vxe-form title-colon ref="formRef" title-align="right" title-width="100" :data="formData" :loading="formLoading" @submit="submitEvent" @reset="resetEvent" > <vxe-form-item field="value" span="24" :item-render="{}" title-overflow> <template #default="params"> <vxe-switch v-model="params.data.value" size="small" open-label="启" close-label="停" v-if="params.data.valueType === 'bool'" ></vxe-switch> <vxe-input v-model="params.data.value" placeholder="请输入数值" type="number" clearable v-if="params.data.valueType != 'bool'" ></vxe-input> <!-- <vxe-input v-model="params.data.value" placeholder="请输入数值" clearable v-if="params.data.valueType != 'bool'" ></vxe-input> --> </template> </vxe-form-item> <vxe-form-item align="center" span="24"> <template #default> <vxe-button type="submit" status="primary" content="确认"></vxe-button> <vxe-button type="reset" content="重置"></vxe-button> </template> </vxe-form-item> </vxe-form> </template> </vxe-modal> </template> <script lang="ts" setup> import router from '@/router'; import { VxeGridProps, VxeGridInstance, VxeTableEvents, VxeColumnPropTypes, VXETable, VxeFormInstance, VxeFormPropTypes, VxeFormEvents, VxeTablePropTypes, VxePagerEvents } from 'vxe-table'; import { getTableHeader, getStationInfo, getTableData, editConfig, sendCtrl } from '@/api/table/list'; import { TableVo } from '@/api/table/types'; import { tableStore } from '@/store/modules/table'; import mitt from '@/plugins/bus'; import socket from '@/utils/socket'; import useStorage from '@/utils/useStorage'; import { useUserStoreHook } from '@/store/modules/user'; const sessionStorageIns = useStorage('sessionStorage'); const { perms } = useUserStoreHook(); const requiredPerms = ['model:device:contrl']; //控制单元格可修改权限 const controlPerm = perms?.some(perm => { return requiredPerms.includes(perm); }); const tableStoreCounter = tableStore(); // import type { MenuOption } from 'naive-ui' // import { useMessage } from 'naive-ui' // const message = useMessage() // const mainHeight = ref('calc(100vh - 161px)'); const loadingShow = ref(false); const editModal = ref(false); const modalTitle = ref(''); const menuKey = ref(0); const tableColumn = ref([]); const tableData = ref<TableVo[]>([]); const tableRef = ref<VxeGridInstance<TableVo>>(); const cellRow = ref({}); const cellColumn = ref(); const cellField = ref(); const tableBorder = ref(true); const tablePage = reactive({ total: 0, currentPage: 1, pageSize: 10 }); // const fields = ref(['deviceuuid']) //需要合并的列数据字段 // const waringArrow = ref([]) interface FormDataVO { centeruuid: string; paramcode: string; url: string; valueType: string; value: string | boolean; } const formRef = ref<VxeFormInstance>(); const formLoading = ref(false); const formData = ref<FormDataVO>({ centeruuid: '', paramcode: '', url: '', valueType: '', value: '' }); // const formRules = ref<VxeFormPropTypes.Rules>({ // value: [ // { required: true, message: '请输入数值' }, // { min: 1, max: 100, message: '长度在 1 到 100 个字符' }, // { // validator({ itemValue }) { // // 自定义校验 // const reg = /^-?\d+\.?\d{0,2}$/; // if (!reg.test(itemValue)) { // return new Error('请输入正确数值!'); // } // } // } // ] // }); const userStorageInfo = sessionStorage.getItem('userInfo'); const userInfo = JSON.parse(userStorageInfo === null ? '' : userStorageInfo); //const apiUrl = import.meta.env.VITE_APP_WS_API //const wsUrl = `${apiUrl}websocket/${userInfo.userName}`; //websocket地址 //const wsUrl = `${apiUrl}${userInfo.userName}`; //websocket地址 // const loginIp = userInfo.loginIp.split('.').join(''); // const baseApi = "http://172.1.2.106:9000"//websocket链接地址,与项目接口地址一致。 // const baseApi = "http://board.heatiot.cn:8001/prod-api"//websocket链接地址,与项目接口地址一致。 //const baseApi = import.meta.env.VITE_APP_BASE_API //const apiUrl = baseApi.replace(/https?:/, ''); const wsUrl = `ws://${window.location.host}/ws/websocket/${userInfo.userName}`; //websocket地址 // const wsUrl = `ws://172.1.2.39:9010/websocket/${userInfo.userName}`; //websocket地址 const emit = defineEmits(['tableHeaderData']); // const listData = ref([ // { // name: 'area_default', // value: 1, // }, // ]); defineProps({ tapsShow: { type: Boolean, default: true }, sidebarHeight: { type: Number, default: 0 } }); // watch( // () => tableStoreCounter.tableDataStore, // (newValue, oldValue) => { // console.log('值发生了变更', newValue, oldValue); // const $table = tableRef.value; // $table.loadData(newValue); // } // ); // watchEffect(() => { // const titleRef = props.tapsShow; // console.log(tableStoreCounter.tableDataStore); // tableData.value = tableStoreCounter.tableDataStore; // $table.loadData(tableStoreCounter.tableDataStore); // }); interface HeaderVo { code: string; name: string; value: string; } const headerData = ref<HeaderVo[]>(); onMounted(() => { // stationInfo(); // tableHeader(); console.log('aaaa', sessionStorage.getItem('currentPage')); tablePage.currentPage = sessionStorage.getItem('currentPage') === null ? 1 : Number(sessionStorage.getItem('currentPage')); console.log(Number(sessionStorage.getItem('currentPage'))); tablePage.pageSize = sessionStorage.getItem('pageSize') === null ? 10 : Number(sessionStorage.getItem('pageSize')); socket.initialize(wsUrl); //初始化websocket http:// }); mitt.on('currentPageEmit', (res: any) => { //监听切换菜单时页数初始化 tablePage.currentPage = res; }); mitt.on('menuKey', (res: any) => { //监听左侧菜单点击 menuKey.value = res; tableHeader(); tableDatas(); }); mitt.on('treeData', (res: any) => { //监听表格header点击 tableColumn.value = []; nextTick(() => { tableColumn.value = res; }); const params = res; editConfig(params).then((res: any) => { if (res.code === 200) { mitt.emit('treeClose', true); } }); // tableColumn.value=res // gridOptions.columns?.push(...res); // console.log('列配置', gridOptions.columns); }); mitt.on('tableMessage', (res: any) => { //监听表格数据变化 console.log('tableMessage--', res.data); const $table = tableRef.value; const tableArray = tableData.value; if ($table) { console.log('tableRef--', tableRef.value); res.data.map((item: any) => { const index = tableArray.findIndex(obj => obj.id === item.id); if (index !== -1) { tableArray.splice(index, 1, item); } }); console.log('tableData--', tableArray); $table.loadData(tableArray); // if (res.code === 'datareal') { // const index = tableData.value.findIndex((obj) => obj.id === res.data.id); // if (index !== -1) { // tableData.value.splice(index, 1, res.data); // } // res.data.map((item: any) => { // console.log(item) // debugger // const index = tableData.value.findIndex((obj) => obj.id === item.id); // if (index !== -1) { // tableData.value.splice(index, 1, res.data); // } // }) // $table.loadData(mergedArray); } // } else if (res.code === 'alertDev') { // waringArrow.value.push(res.data) // waringArrow.value = uniqueArrayObject(waringArrow.value, "devUuid") // } // console.log("waringArrow:", waringArrow.value) }); const handlePageChange: VxePagerEvents.PageChange = ({ currentPage, pageSize }) => { tablePage.currentPage = currentPage; tablePage.pageSize = pageSize; sessionStorageIns.setUseStorage('currentPage', currentPage); sessionStorageIns.setUseStorage('pageSize', pageSize); tableDatas(); }; function stationInfo() { //获取热源信息 getStationInfo().then((res: any) => { if (res.code === 200) { headerData.value = res.data; } }); } function tableHeader() { //获取表格header getTableHeader().then((res: any) => { console.log(res); // 列配置 const tableCessText = [ { id: 0, title: 'ID', field: 'id', type: 'html', formatter: formatRole, visible: false } ]; console.log(tableCessText); res.data.map((item: any) => { if (item.formatter != undefined || item.children != undefined) { item.formatter = eval(item.formatter); if (item.children && item.children.length) { item.children.map((res: any) => { res.formatter = eval(res.formatter); }); } } }); nextTick(() => { tableColumn.value = res.data; }); // tableColumn.value = res.data; // const arr = [ // { // id: 1, // title: '设备名称', // field: 'deviceuuid', // colSort: 2, // colType: '2', // width: 150, // show: true, // disabled: true, // fixed: 'left', // type: 'html', // formatter: "formatRole", // }, // ]; // arr.map((item)=>{ // item.formatter=eval(item.formatter); // }) // console.log(arr) // tableColumn.value = arr; // gridOptions.columns = res.data; // console.log('列获取', gridOptions.value.columns); emit('tableHeaderData', res.data); // mitt.emit('tableHeaderData', res.data); // tableDatas(); }); } function tableDatas() { //获取表格数据 // const params = menuKey.value; const params = { pageNum: tablePage.currentPage, pageSize: tablePage.pageSize, orgCode: menuKey.value }; loadingShow.value = true; getTableData(params).then((res: any) => { if (res.code === 200) { // tableData.value=oldData tableData.value = res.rows; tableStoreCounter.tableDataAction(res.rows); tablePage.total = res.total; // gridOptions.data = res.data; loadingShow.value = false; } }); } const cellDBLClickEvent: VxeTableEvents.CellDblclick<TableVo> = ({ row, column }) => { //双击单元格 //edit ty zhousq 2023-10-12 修改控制方式为接口方式,修改formdata提交的属性值 // ctrlPro.centeruuid 设备UUID cellField.ctrlPro.paramcode 设备参数编码 cellField.val 设备值 //ctrlPro 为新增的单元格属性,控制参数回传 console.log('cellData--', row, column); const cellField = row[column.field]; //const data = row.data; cellRow.value = row; cellColumn.value = column; cellField.value = cellField; if (cellField.canBeControl === '1' && controlPerm) { modalTitle.value = column.title; ///formData.value.url = data.url; //formData.value.deviceName = cellField.deviceName; formData.value = cellField.ctrlPro; formData.value.value = cellField.val; // formData.value.paramCode = cellField.ctrlPro.paramcode; editModal.value = true; } console.log(row[column.field]); }; const formatRole: VxeColumnPropTypes.Formatter<HeaderVo> = ({ cellValue }) => { //表单参数为object时处理 // console.log(cellValue); // const iconFont=cellValue.changeProp===-1?'<i class="iconfont icon-decline" />':(cellValue.changeProp===1?'<i class="iconfont icon-rise" />':'<i/>') // const cellData = `<span class="cellClass ${cellValue.alertProp===1?'warning':''}">${cellValue.val}</span>${iconFont}`; // const cellData = `<span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}">${cellValue.val}</span><i class="iconfont ${cellValue.changeProp === -1 ? 'icon-decline' : cellValue.changeProp === 1 ? 'icon-rise' : ''}" ></i>${cellValue.canBeControl === '1' ? '<i class="iconfont icon-edit-icon"></i>' : ''}`; // const cellData = ` // <span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}"> // ${cellValue.ctrlPro.valueType != 'bool' ? cellValue.val : cellValue.val === 'true' ? '启' : '停'} // </span> // <i class="iconfont ${ // cellValue.changeProp === -1 ? 'icon-decline' : cellValue.changeProp === 1 ? 'icon-rise' : '' // }" ></i> // ${cellValue.canBeControl === '1' && controlPerm ? '<i class="iconfont icon-edit-icon"></i>' : ''}`; const cellData = ` <span class="cellClass ${cellValue.alertProp === 1 ? 'warning' : cellValue.canBeControl === '1' ? 'cellEdit' : ''}"> ${cellValue.ctrlPro.valueType != 'bool' ? cellValue.val : cellValue.val === 'true' ? '启' : '停'} </span> ${cellValue.canBeControl === '1' && controlPerm ? '<i class="iconfont icon-edit-icon"></i>' : ''}`; return cellData; }; // 通用行合并函数(将相同多列数据合并为一行) const mergeRowMethod: VxeTablePropTypes.SpanMethod<TableVo> = ({ row, _rowIndex, column, visibleData }) => { const fields = ['gTitle']; const cellValue = row[column.field]; if (cellValue && fields.includes(column.field)) { const prevRow = visibleData[_rowIndex - 1]; let nextRow = visibleData[_rowIndex + 1]; if (prevRow && prevRow[column.field] === cellValue) { return { rowspan: 0, colspan: 0 }; } else { let countRowspan = 1; while (nextRow && nextRow[column.field] === cellValue) { nextRow = visibleData[++countRowspan + _rowIndex]; } if (countRowspan > 1) { return { rowspan: countRowspan, colspan: 1 }; } } } }; const submitEvent: VxeFormEvents.Submit = () => { //修改数据之后通过websocket传给后端 formLoading.value = true; const $table = tableRef.value; const submitData = formData.value; console.log(submitData); //Add by zhousq 2023-10-12 控制接口调用 post 方法 sendCtrl(submitData).then((res: any) => { if (res.code === 200) { ElNotification({ message: res.data.msg }); } }); //socket.onSend(submitData); formLoading.value = false; editModal.value = false; if ($table) { const row = cellRow.value; const field = cellColumn.value.field; row[field].val = formData.value.value; $table.reloadRow(row, null, field); } // VXETable.modal.message({ content: '保存成功', status: 'success' }); }; function nameClick(row: any) { //点击设备名称跳转设备管理 console.log(row); sessionStorageIns.setUseStorage('id', row.id); router.push({ path: '/devicemanage', query: { id: row.id } }); } const resetEvent: VxeFormEvents.Reset = () => { console.log({ content: '重置', status: 'info' }); }; </script> <style lang="scss" scoped> @import '../index.scss'; </style>