Jelajahi Sumber

[Fix 0000]皮带巷三级防灭火页面优化 bug修复

bobo04052021@163.com 1 Minggu lalu
induk
melakukan
922615552e

+ 65 - 107
src/views/vent/deviceManager/comment/warningTabel/BaseModal1Leather.vue

@@ -25,6 +25,7 @@
         </div>
         <a-button type="dashed" block @click="addWarnRow" class="setting-form__button">新增预警配置</a-button>
       </div>
+
       <!-- 配置控制设备 -->
       <div class="setting-form__wrapper">
         <h3 class="panel-title">配置控制设备</h3>
@@ -84,7 +85,6 @@ const isUpdate = ref(false);
 // 表单Ref数组
 const warnFormRefs = ref<FormActionType[]>([]);
 const controlFormRefs = ref<FormActionType[]>([]);
-// 点位弹窗临时保存当前行
 const tempCurrentModel = ref<any>(null);
 const devicePointList = ref<any[]>([]);
 
@@ -103,13 +103,11 @@ function getEmptyWarnRow() {
     monitorType: '2',
   };
 }
-// 收集预警表单Ref
 function setWarnFormRef(el: any, index: number) {
   if (el) warnFormRefs.value[index] = el;
 }
 function addWarnRow() {
   warnList.value.push(getEmptyWarnRow());
-  warnFormRefs.value.push(null);
 }
 function delWarnRow(idx: number) {
   if (warnList.value.length <= 1) return message.warning('至少保留一行');
@@ -130,160 +128,123 @@ function getEmptyControlRow() {
     monitorType: '1',
   };
 }
-// 收集控制表单Ref
 function setControlFormRef(el: any, index: number) {
   if (el) controlFormRefs.value[index] = el;
 }
 function addControlRow() {
   controlList.value.push(getEmptyControlRow());
-  controlFormRefs.value.push(null);
 }
 async function delControlRow(idx: number, record: any) {
   if (controlList.value.length <= 1) return message.warning('至少保留一行');
   controlList.value.splice(idx, 1);
   controlFormRefs.value.splice(idx, 1);
-  await workFaceWarningDelete({ id: record.id });
+  if (record.id) await workFaceWarningDelete({ id: record.id });
 }
 
-// 获取点位列表
+// 获取点位
 async function getDevicePointList(strtype: string) {
   try {
-    const result = await workFacePointList({ deviceType: strtype, valueTypes: props.monitorType });
-    devicePointList.value = result;
-  } catch (error) {
+    const res = await workFacePointList({ deviceType: strtype, valueTypes: props.monitorType });
+    devicePointList.value = res;
+  } catch {
     devicePointList.value = [];
   }
 }
-
-// 打开点位选择弹窗
 async function selectPoint(model: any) {
-  if (!model.deviceType) {
-    message.info('请先选择设备!');
-    return;
-  }
-  console.log(model, '=====model');
-  // 缓存当前编辑行
+  if (!model.deviceType) return message.info('请先选择设备!');
   tempCurrentModel.value = model;
   await getDevicePointList(model.deviceType);
   openPointModal(true, { deviceType: model.deviceType });
 }
-
-// 选中点位回填到当前行
 function setPoint(value: any[]) {
-  if (!value || !value.length || !tempCurrentModel.value) return;
+  if (!value?.length || !tempCurrentModel.value) return;
   const data = value[0];
-  const model = tempCurrentModel.value;
-  // 赋值当前行
-  model.monitorId = data.id;
-  model.pointOptions = [{ value: data.id, label: data.valuename }];
+  const m = tempCurrentModel.value;
+  m.monitorId = data.id;
+  m.pointOptions = [{ value: data.id, label: data.valuename }];
 }
 
+// 主表单
 const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
   schemas: props.formSchemas,
   showActionButtonGroup: false,
 });
 
+// 弹窗
 const [register, { setModalProps, closeModal }] = useModalInner(async (data) => {
   isUpdate.value = unref(data.isUpdate);
-  await resetFields();
   title.value = data.isUpdate ? '编辑' : '新增';
-
+  await resetFields();
   warnFormRefs.value = [];
   controlFormRefs.value = [];
   tempCurrentModel.value = null;
-
+  warnList.value = [getEmptyWarnRow()];
+  controlList.value = [getEmptyControlRow()];
   if (data.isUpdate && data.record) {
     await setFieldsValue(data.record);
-    const allAlarms = data.record.alarmList || [];
-    const warnings = allAlarms.filter((item: any) => String(item.monitorType) === '2');
-    const controls = allAlarms.filter((item: any) => String(item.monitorType) === '1');
-    warnList.value = warnings.length ? warnings : [getEmptyWarnRow()];
-    controlList.value = controls.length ? controls : [getEmptyControlRow()];
-  } else {
-    warnList.value = [getEmptyWarnRow()];
-    controlList.value = [getEmptyControlRow()];
-  }
-
-  if (data.type === 'devicePoint') {
-    title.value = unref(data.title);
-    if (data.record) {
-      await getDevicePointList(data.record['deviceType']);
-      devicePointList.value.forEach((item) => {
-        if (item.id == data.record.monitorId) {
-          const model = data.record;
-          model.monitorId = item.id;
-          model.pointOptions = [{ value: item.id, label: item.valuename }];
-        }
-      });
-      await setFieldsValue({ ...data.record });
+    const all = data.record.alarmList || [];
+    warnList.value = all.filter((i) => String(i.monitorType) === '2') || [getEmptyWarnRow()];
+    controlList.value = all.filter((i) => String(i.monitorType) === '1') || [getEmptyControlRow()];
+    await nextTick();
+    for (let i = 0; i < warnList.value.length; i++) {
+      const form = warnFormRefs.value[i];
+      const row = warnList.value[i];
+      if (form && row) await form.setFieldsValue(row);
+    }
+    for (let i = 0; i < controlList.value.length; i++) {
+      const form = controlFormRefs.value[i];
+      const row = controlList.value[i];
+      if (form && row) await form.setFieldsValue(row);
     }
   }
 });
 
-// 提交
-// 提交
+// ✅ 提交修复
 async function onSubmit() {
   setModalProps({ confirmLoading: true });
   try {
     const baseForm = await getFieldsValue();
 
-    // 获取预警数据
-    const warnValues = [];
-    for (const form of warnFormRefs.value) {
-      if (form) {
-        const val = await form.getFieldsValue();
-        delete val.pointOptions;
-        if (val.strtype) {
-          val.deviceType = val.strtype;
-        }
-        delete val.strtype;
-        const deviceId = Array.isArray(val.deviceId) ? val.deviceId[0] : val.deviceId || '';
-        if (!deviceId) continue;
-        warnValues.push({
-          ...val,
-          monitorType: 2,
-          deviceId: deviceId,
-        });
-      }
+    // 读取预警行 —— 没有 monitorId 则过滤不提交
+    const warnData = [];
+    for (let i = 0; i < warnFormRefs.value.length; i++) {
+      const form = warnFormRefs.value[i];
+      if (!form) continue;
+      const val = await form.getFieldsValue();
+      delete val.pointOptions;
+      if (val.strtype) val.deviceType = val.strtype;
+      delete val.strtype;
+      if (!val.monitorId) continue;
+
+      warnData.push({ ...val, monitorType: 2 });
     }
 
-    // 获取控制数据
-    const controlValues = [];
-    for (const form of controlFormRefs.value) {
-      if (form) {
-        const val = await form.getFieldsValue();
-        delete val.pointOptions;
+    // 读取控制行 —— 没有 monitorId 则过滤不提交
+    const controlData = [];
+    for (let i = 0; i < controlFormRefs.value.length; i++) {
+      const form = controlFormRefs.value[i];
+      if (!form) continue;
+      const val = await form.getFieldsValue();
+      delete val.pointOptions;
+      if (val.strtype) val.deviceType = val.strtype;
+      delete val.strtype;
 
-        if (val.strtype) {
-          val.deviceType = val.strtype;
-        }
-        delete val.strtype;
-        const deviceId = Array.isArray(val.deviceId) ? val.deviceId[0] : val.deviceId || '';
-        if (!deviceId) continue;
+      // ✅ 关键:没有 monitorId 就跳过,不提交
+      if (!val.monitorId) continue;
 
-        controlValues.push({
-          ...val,
-          monitorType: 1,
-          deviceId: deviceId,
-        });
-      }
+      controlData.push({ ...val, monitorType: 1 });
     }
 
-    const submitData = {
+    const params = {
       ...baseForm,
-      alarmList: [...warnValues, ...controlValues],
+      alarmList: [...warnData, ...controlData],
     };
 
-    if (!isUpdate.value) {
-      emit('add', submitData);
-    } else {
-      emit('update', submitData);
-    }
-
+    isUpdate.value ? emit('update', params) : emit('add', params);
     closeModal();
-  } catch (error) {
-    console.error('提交异常:', error);
-    message.error('提交失败,请检查表单');
+  } catch (err) {
+    console.error(err);
+    message.error('提交失败');
   } finally {
     setModalProps({ confirmLoading: false });
   }
@@ -293,38 +254,35 @@ async function onSubmit() {
 <style lang="less" scoped>
 @import '/@/design/theme.less';
 
-.setting-form {
-  padding: 0 20px;
-}
-
 .setting-form__wrapper {
   width: 49%;
   color: #fff;
   border-top: 3px solid @vent-gas-primary-text;
   background-color: @vent-gas-primary-trasparent-bg;
-  padding: 10px 10px 0 10px;
-  margin-bottom: 10px;
+  padding: 10px;
 }
 
 .setting-form__button {
-  display: block;
   width: 40%;
   margin: 10px auto;
   color: @vent-gas-primary-text;
   border-color: @vent-gas-primary-text;
-  background-color: transparent !important;
+  background: transparent !important;
 }
+
 .double-form__layout {
   display: flex;
   gap: 16px;
   margin-top: 16px;
 }
+
 .panel-title {
   margin: 0 0 10px;
   font-size: 14px;
   font-weight: bold;
   color: #fafafa;
 }
+
 .form-row {
   border: 1px solid #2e7695;
   display: flex;

+ 2 - 2
src/views/vent/deviceManager/comment/warningTabel/indexLeather.vue

@@ -91,8 +91,8 @@ async function handleDelete(record) {
 
 async function onSubmit(values) {
   const params = { ...values };
-  if (params.pNum) {
-    params.partNum = params.pNum;
+  if (params.pnum) {
+    params.partNum = params.pnum;
   }
 
   const isUpdate = !!params.id;

+ 12 - 4
src/views/vent/home/configurable/belt/configurable.api.ts

@@ -1,8 +1,4 @@
-import { floor, isArray, random, slice } from 'lodash-es';
 import { defHttp } from '/@/utils/http/axios';
-import { get } from '../../billboard/utils';
-import { useGlobSetting } from '/@/hooks/setting';
-import { reactive } from 'vue';
 import _ from 'lodash';
 
 enum Api {
@@ -21,6 +17,14 @@ enum Api {
   getWarnInfo = '/ventanaly-device/monitor/disaster/findAlarmsBySystemAndMonitorType',
   // 获取人员定位信息
   findStaffInfoBySystem = '/ventanaly-device/monitor/disaster/findStaffInfoBySystem',
+  // 获取皮带巷信息
+  getLeatherInfo = '/modelreq/safety/ventanalyManageSystem/list',
+  // 操作历史
+  baseList = '/safety/ventanalyGate/list',
+  // 设备历史
+  deviceHistory = '/modelreq/safety/ventanalyMonitorData/listdays',
+  // 获取皮带巷下设备
+  getDeviceData = '/ventanaly-device/safety/managesysAlarmInfo/getDevicesInfoBySysId',
 }
 export const getSystem = (params) => defHttp.post({ url: Api.getSystem, params });
 export const getMonitorAndAlertBelt = (params) => defHttp.post({ url: Api.monitorAndAlertBelt, params });
@@ -31,3 +35,7 @@ export const getDevice = (params) => defHttp.post({ url: Api.getDevice, params }
 export const getWarnResult = (params) => defHttp.post({ url: Api.getWarnResult, params });
 export const getWarnInfo = (params) => defHttp.post({ url: Api.getWarnInfo, params });
 export const getStaffInfo = (params) => defHttp.post({ url: Api.findStaffInfoBySystem, params });
+export const getLeatherInfo = (params) => defHttp.get({ url: Api.getLeatherInfo, params });
+export const getTableList = (params) => defHttp.get({ url: Api.baseList, params });
+export const getDeviceHistoryApi = (params) => defHttp.get({ url: Api.deviceHistory, params });
+export const getDeviceData = (params) => defHttp.post({ url: Api.getDeviceData, params });

+ 190 - 102
src/views/vent/home/configurable/belt/history.vue

@@ -2,155 +2,243 @@
   <div class="company-home">
     <div class="border">
       <customHeader>矿井全域皮带巷三级防灭火系统</customHeader>
-      <SubApp />
+      <!-- Tab 切换 -->
+      <div class="tab-box">
+        <div class="tab-item" :class="{ active: activeTab === 'device' }" @click="switchTab('device')"> 设备历史数据 </div>
+        <div class="tab-item" :class="{ active: activeTab === 'operate' }" @click="switchTab('operate')"> 操作历史 </div>
+      </div>
+
       <div class="box-container">
-        <BasicTable ref="historyTable" @register="registerTable" :scroll="{ x: 1000, y: 500 }" />
+        <!-- 设备历史 -->
+        <div v-if="activeTab === 'device'" key="device">
+          <BasicTable ref="historyTable" @register="registerTable" :data-source="dataSource" :scroll="{ x: 1000, y: 500 }" />
+        </div>
+
+        <!-- 操作历史 -->
+        <div v-if="activeTab === 'operate'" key="operate">
+          <BasicTable ref="operateTable" @register="registerOperateTable" :data-source="ctrlSource" :scroll="{ x: 1000, y: 500 }" />
+        </div>
       </div>
     </div>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, onMounted, nextTick } from 'vue';
 import { BasicTable } from '/@/components/Table';
 import { useListPage } from '/@/hooks/system/useListPage';
-import { defHttp } from '/@/utils/http/axios';
 import dayjs from 'dayjs';
 import customHeader from './components/customHeader-belt.vue';
-// 表格列配置(你可以随便加字段)
-const columns = ref([
+import { getLeatherInfo, getTableList, getDeviceHistoryApi, getDeviceData } from './configurable.api';
+
+const activeTab = ref('device');
+// 设备下拉选项
+const deviceOptions = ref<any>([]);
+const deviceDataOptions = ref<any>([]);
+const dataSource = ref([]);
+const ctrlSource = ref([]);
+// 工具方法
+function resetFormParam() {
+  const formData = getForm().getFieldsValue();
+  const pagination = getPaginationRef();
+  return {
+    pageNum: pagination.current,
+    pageSize: pagination.pageSize,
+    column: 'createTime',
+    startTime: formData.startTime,
+    endTime: formData.endTime,
+    deviceId: formData.gdeviceids,
+    strtype: 'sys_Leather',
+    sysId: formData.id,
+  };
+}
+
+// 获取皮带巷列表
+async function getDeviceList() {
+  const res = await getLeatherInfo({
+    strtype: 'sys_Leather',
+    devicekind: 'managesys',
+    pageNo: 1,
+    pageSize: 999,
+  });
+  deviceOptions.value = res.records.map((item) => ({
+    label: item.systemname,
+    value: item.id,
+  }));
+}
+// 获取皮带巷下设备信息
+async function getDevice(id) {
+  const res = await getDeviceData({ sysId: id });
+  deviceDataOptions.value = res.map((item) => ({
+    label: item.deviceName,
+    value: item.deviceId,
+  }));
+}
+const getSchemas = () => [
   {
-    title: '设备名称',
-    dataIndex: 'deviceName',
-    width: 180,
+    field: 'startTime',
+    label: '开始时间',
+    component: 'DatePicker',
+    defaultValue: dayjs().subtract(1, 'day').startOf('day'),
+    componentProps: {
+      showTime: true,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    colProps: { span: 4 },
   },
   {
-    title: '设备位置',
-    dataIndex: 'devicePos',
-    width: 180,
+    field: 'endTime',
+    label: '结束时间',
+    component: 'DatePicker',
+    defaultValue: dayjs(),
+    componentProps: {
+      showTime: true,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    colProps: { span: 4 },
   },
   {
-    title: '监测值',
-    dataIndex: 'monitorValue',
-    width: 120,
+    field: 'id',
+    label: '皮带巷',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择皮带巷',
+      showSearch: true,
+      options: deviceOptions,
+      onChange: (e, option) => {
+        getDevice(option.value);
+      },
+    },
+    required: true,
+    colProps: { span: 4 },
   },
   {
-    title: '采集时间',
-    dataIndex: 'createTime',
-    width: 200,
+    field: 'gdeviceids',
+    label: '设备名称',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择设备',
+      showSearch: true,
+      options: deviceDataOptions,
+      onChange: (e, option) => {
+        getHistoryData();
+      },
+    },
+    required: true,
+    colProps: { span: 4 },
   },
+];
+
+//设备历史
+const deviceColumns = ref([
+  { title: '设备名称', dataIndex: 'gdevicename', width: 180 },
+  { title: '设备位置', dataIndex: 'devicePos', width: 180 },
+  { title: '监测值', dataIndex: 'monitorValue', width: 120 },
+  { title: '采集时间', dataIndex: 'gcreatetime', width: 200 },
 ]);
 
-// 查询接口 模拟
-const getHistoryDataApi = (params) =>
-  defHttp.post({
-    url: '/monitor/device/history',
-    params,
-  });
+async function getHistoryData() {
+  const params = resetFormParam();
+  const res = await getDeviceHistoryApi(params);
+  dataSource.value = res.records;
+}
 
-// 表格注册
-const { tableContext } = useListPage({
+const { tableContext: deviceTableContext } = useListPage({
   tableProps: {
-    api: getHistoryDataApi, // 绑定查询接口
-    columns: columns.value, // 绑定表格列
+    api: async (params) => {
+      const res = await getDeviceHistoryApi(params);
+      return {
+        total: res.total || 0,
+        items: res.records || [],
+      };
+    },
+    columns: deviceColumns.value,
     showIndexColumn: true,
     bordered: true,
     size: 'small',
-
-    // 查询表单配置
     formConfig: {
       labelWidth: 80,
-      showSubmitButton: true, // 显示查询按钮
-      showResetButton: true, // 显示重置按钮
-      schemas: [
-        // 时间范围
-        {
-          field: 'beginTime',
-          label: '开始时间',
-          component: 'DatePicker',
-          defaultValue: dayjs().subtract(1, 'day').startOf('day'),
-          componentProps: {
-            showTime: true,
-            valueFormat: 'YYYY-MM-DD HH:mm:ss',
-          },
-          colProps: { span: 6 },
-        },
-        {
-          field: 'endTime',
-          label: '结束时间',
-          component: 'DatePicker',
-          defaultValue: dayjs(),
-          componentProps: {
-            showTime: true,
-            valueFormat: 'YYYY-MM-DD HH:mm:ss',
-          },
-          colProps: { span: 6 },
-        },
-        {
-          field: 'deviceName',
-          label: '皮带巷',
-          component: 'Select',
-          componentProps: { placeholder: '请选择皮带巷' },
-          colProps: { span: 6 },
-        },
-        // 设备名称搜索
-        {
-          field: 'deviceName',
-          label: '设备名称',
-          component: 'Select',
-          componentProps: { placeholder: '请选择设备' },
-          colProps: { span: 6 },
-        },
-      ],
+      schemas: getSchemas(),
+      showAdvancedButton: false,
+      autoAdvancedCol: 99,
+      alwaysShowLines: 99,
     },
+    pagination: { pageSize: 20 },
+  },
+});
+const [registerTable, { getForm, getPaginationRef }] = deviceTableContext;
+
+//操作历史
+const operateColumns = ref([
+  { title: '操作人', dataIndex: 'operateUser', width: 160 },
+  { title: '操作内容', dataIndex: 'operateContent', width: 300 },
+  { title: '操作时间', dataIndex: 'operateTime', width: 200 },
+  { title: '设备/位置', dataIndex: 'deviceName', width: 180 },
+]);
 
-    // 分页
-    pagination: {
-      pageSize: 20,
-      showSizeChanger: false,
+const { tableContext: operateTableContext } = useListPage({
+  tableProps: {
+    api: getTableList,
+    columns: operateColumns.value,
+    showIndexColumn: true,
+    bordered: true,
+    size: 'small',
+    formConfig: {
+      labelWidth: 80,
+      schemas: getSchemas(),
+      showAdvancedButton: false,
+      autoAdvancedCol: 99,
+      alwaysShowLines: 99,
     },
+    pagination: { pageSize: 20 },
   },
 });
+const [registerOperateTable] = operateTableContext;
+
+//Tab 切换
+function switchTab(tab) {
+  activeTab.value = tab;
+}
 
-const [registerTable] = tableContext;
+//初始化
+onMounted(() => {
+  getDeviceList();
+});
 </script>
 
 <style scoped lang="less">
-.spray-wrapper {
+.company-home {
   width: 100%;
   height: 100%;
-  background-image: url('/@/assets/images/beltFire/baseMap.png');
+  color: #fff;
+  background: url('/@/assets/images/beltFire/baseMap.png') no-repeat center;
   background-size: cover;
-}
-
-#spray3D {
-  pointer-events: all;
-}
 
-.spray-wrapper :deep(.list-item_L .list-item__icon_L) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-icon-file.png');
-}
-.spray-wrapper :deep(.list-item_N:nth-child(1)) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n5.png');
-}
-.spray-wrapper :deep(.list-item_N:nth-child(2)) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n6.png');
-}
-.company-home {
-  background: url('/@/assets/images/beltFire/baseMap.png') no-repeat center;
-  width: 100%;
-  height: 100%;
-  color: @white;
-  position: relative;
   .border {
     width: 100%;
     height: 94%;
     background: url('/@/assets/images/beltFire/mainbj.png') no-repeat;
     background-size: 100% 100%;
+    padding: 20px;
+    box-sizing: border-box;
     margin-top: 50px;
-    .test {
-      background: url('./test.png') no-repeat;
-      background-size: 100% 100%;
+  }
+
+  .tab-box {
+    display: flex;
+    gap: 4px;
+    margin-bottom: 16px;
+
+    .tab-item {
+      padding: 8px 20px;
+      cursor: pointer;
+      border-radius: 6px;
+
+      &.active {
+        background: #1890ff;
+        color: #fff;
+        font-weight: bold;
+      }
     }
   }
 }

+ 41 - 2
src/views/vent/home/configurable/components/belt/ComplexList1Belt.vue

@@ -1,6 +1,21 @@
 <template>
   <div class="list flex items-center" :class="`list_${type}`">
     <div class="flex-grow" :class="`list_wrapper_${type}`">
+      <!-- 全矿井 固定在最顶部 -->
+      <div v-if="showAllMineItem" class="list-item" :class="`list-item_${type}`">
+        <div
+          style="cursor: pointer"
+          @click="handleItemClick(allMineItem)"
+          :class="[`list-item__content_${type}`, getBgClass(allMineItem.value), { active: allMineItem.id === activeId }]"
+        >
+          <div class="list-item__label">全矿井</div>
+          <div class="list-item__value" :class="`list-item__value_${type}`">
+            {{ getAlertName(allMineItem.value) }}
+          </div>
+        </div>
+      </div>
+
+      <!-- 原列表 -->
       <div v-for="(item, i) in listConfig" :key="`customlist${i}`" class="list-item" :class="`list-item_${type}`">
         <div
           v-for="(ctx, j) in item.contents"
@@ -10,7 +25,9 @@
           :class="[`list-item__content_${type}`, getBgClass(ctx.value), { active: ctx.id === activeId }]"
         >
           <div class="list-item__label"> {{ ctx.label }}</div>
-          <div class="list-item__value" :class="`list-item__value_${type}`"> {{ getAlertName(ctx.value) }} </div>
+          <div class="list-item__value" :class="`list-item__value_${type}`">
+            {{ getAlertName(ctx.value) }}
+          </div>
         </div>
       </div>
     </div>
@@ -18,7 +35,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref } from 'vue';
+import { onMounted, ref, computed } from 'vue';
 import { getFormattedText } from '../../hooks/helper';
 
 const props = withDefaults(
@@ -57,6 +74,27 @@ const alarmMap: Record<string, string> = {
   '104': '红色预警',
 };
 
+// 获取第一条真实数据
+const firstContent = computed(() => {
+  if (!props.listConfig.length) return null;
+  const firstItem = props.listConfig[0];
+  if (!firstItem.contents?.length) return null;
+  return firstItem.contents[0];
+});
+
+// 全矿井项(自动继承第一条数据的 value)
+const allMineItem = computed(() => ({
+  id: 'allMine',
+  label: '全矿井',
+  value: firstContent.value?.value || '0',
+  color: firstContent.value?.color,
+  info: firstContent.value?.info,
+}));
+
+// 是否显示全矿井(有数据才显示)
+const showAllMineItem = computed(() => !!firstContent.value);
+// ==============================================
+
 // 获取背景样式
 const getBgClass = (riskLevel: string) => {
   // 统一处理 null  0
@@ -89,6 +127,7 @@ const getAlertName = (riskLevel: string | number | null) => {
   const key = String(riskLevel);
   return alarmMap[key] || '正常';
 };
+
 onMounted(() => {});
 </script>
 

+ 639 - 569
src/views/vent/monitorManager/comment/HistoryTable.vue

@@ -47,632 +47,702 @@
 </template>
 
 <script lang="ts" setup>
-  //ts语法
-  import { watchEffect, ref, watch, defineExpose, inject, nextTick, onMounted, computed } from 'vue';
-  import { FormSchema } from '/@/components/Form/index';
-  import { BasicTable } from '/@/components/Table';
-  import { useListPage } from '/@/hooks/system/useListPage';
-  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
-  import { defHttp } from '/@/utils/http/axios';
-  import dayjs from 'dayjs';
-  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
-  import { render } from '/@/utils/common/renderUtils';
-  import { useMethods } from '/@/hooks/system/useMethods';
-  import BarAndLine from '/@/components/chart/BarAndLine.vue';
-  import { getDictItemsByCode } from '/@/utils/dict';
-  import { get } from 'lodash-es';
-
-  const globalConfig = inject('globalConfig');
-  const props = defineProps({
-    columnsType: {
-      type: String,
-    },
-    columns: {
-      type: Array,
-      // required: true,
-      default: () => [],
-    },
-    deviceType: {
-      type: String,
-      required: true,
-    },
-    deviceListApi: {
-      type: Function,
-    },
-    deviceArr: {
-      type: Array,
-      // required: true,
-      default: () => [],
-    },
-    designScope: {
-      type: String,
-    },
-    sysId: {
-      type: String,
-    },
-    deviceId: {
-      type: String,
-    },
-    scroll: {
-      type: Object,
-      default: { y: 0 },
-    },
-    formSchemas: {
-      type: Array<FormSchema>,
-      default: () => [],
-    },
-    /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
-    onlyBounedDevices: {
-      type: Boolean,
-      default: false,
-    },
-    showHistoryCurve: {
-      type: Boolean,
-      default: false,
-    },
+//ts语法
+import { watchEffect, ref, watch, defineExpose, inject, nextTick, onMounted, computed } from 'vue';
+import { FormSchema } from '/@/components/Form/index';
+import { BasicTable } from '/@/components/Table';
+import { useListPage } from '/@/hooks/system/useListPage';
+import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+import { defHttp } from '/@/utils/http/axios';
+import dayjs from 'dayjs';
+import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+import { render } from '/@/utils/common/renderUtils';
+import { useMethods } from '/@/hooks/system/useMethods';
+import BarAndLine from '/@/components/chart/BarAndLine.vue';
+import { getDictItemsByCode } from '/@/utils/dict';
+import { get } from 'lodash-es';
+import { usePermission } from '/@/hooks/web/usePermission';
+const { hasPermission } = usePermission();
+const globalConfig = inject('globalConfig');
+const props = defineProps({
+  columnsType: {
+    type: String,
+  },
+  columns: {
+    type: Array,
+    // required: true,
+    default: () => [],
+  },
+  deviceType: {
+    type: String,
+    required: true,
+  },
+  deviceListApi: {
+    type: Function,
+  },
+  deviceArr: {
+    type: Array,
+    // required: true,
+    default: () => [],
+  },
+  designScope: {
+    type: String,
+  },
+  sysId: {
+    type: String,
+  },
+  deviceId: {
+    type: String,
+  },
+  scroll: {
+    type: Object,
+    default: { y: 0 },
+  },
+  formSchemas: {
+    type: Array<FormSchema>,
+    default: () => [],
+  },
+  /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
+  onlyBounedDevices: {
+    type: Boolean,
+    default: false,
+  },
+  showHistoryCurve: {
+    type: Boolean,
+    default: false,
+  },
+});
+const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
+const historyTable = ref();
+const loading = ref(false);
+const stationType = ref('plc1');
+const dataSource = ref([]);
+const intervalMap = new Map([
+  ['1', '1s'],
+  ['2', '5s'],
+  ['3', '10s'],
+  ['4', '30s'],
+  ['5', '1m'],
+  ['6', '10m'],
+  ['7', '30m'],
+  ['8', '1h'],
+  ['9', '1d'],
+]);
+const getExportXlsUrl = () => {
+  if (stationType.value !== 'redis') {
+    return '/safety/ventanalyMonitorData/export/historydata';
+  } else {
+    return '/monitor/history/exportHistoryData';
+  }
+};
+const emit = defineEmits(['change']);
+const hasMultipleChoice = hasPermission('windrect:hasMultipleChoice');
+const historyType = ref('');
+const deviceKide = ref('');
+const columns = ref([]);
+let deviceOptions = ref([]);
+const deviceTypeStr = ref('');
+const deviceTypeName = ref('');
+const deviceType = ref('');
+const chartsColumns = ref([]);
+loading.value = true;
+
+const selectedOption = computed<Record<string, any> | undefined>(() => {
+  let idval: string | undefined = getForm()?.getFieldsValue()?.gdeviceids;
+  if (VENT_PARAM.historyIsMultiple && idval) {
+    const arr = idval.split(',');
+    idval = arr[arr.length - 1];
+  }
+  return deviceOptions.value.find((e: any) => {
+    return e.value === idval;
   });
-  const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
-  const historyTable = ref();
-  const loading = ref(false);
-  const stationType = ref('plc1');
-  const dataSource = ref([]);
-  const intervalMap = new Map([
-    ['1', '1s'],
-    ['2', '5s'],
-    ['3', '10s'],
-    ['4', '30s'],
-    ['5', '1m'],
-    ['6', '10m'],
-    ['7', '30m'],
-    ['8', '1h'],
-    ['9', '1d'],
-  ]);
-
-  const getExportXlsUrl = () => {
-    if (stationType.value !== 'redis') {
-      return '/safety/ventanalyMonitorData/export/historydata';
-    } else {
-      return '/monitor/history/exportHistoryData';
+});
+
+watch(
+  () => {
+    return props.columnsType;
+  },
+  async (newVal) => {
+    if (!newVal) return;
+    deviceKide.value = newVal;
+    if (historyTable.value) {
+      getForm().resetFields();
+      // getForm().updateSchema();
+      // getForm();
     }
-  };
-  const emit = defineEmits(['change']);
-
-  const historyType = ref('');
-  const deviceKide = ref('');
-  const columns = ref([]);
-  let deviceOptions = ref([]);
-  const deviceTypeStr = ref('');
-  const deviceTypeName = ref('');
-  const deviceType = ref('');
-  const chartsColumns = ref([]);
-  loading.value = true;
-
-  const selectedOption = computed<Record<string, any> | undefined>(() => {
-    let idval: string | undefined = getForm()?.getFieldsValue()?.gdeviceids;
-    if (VENT_PARAM.historyIsMultiple && idval) {
-      const arr = idval.split(',');
-      idval = arr[arr.length - 1];
-    }
-    return deviceOptions.value.find((e: any) => {
-      return e.value === idval;
+    dataSource.value = [];
+    // const column = getTableHeaderColumns(newVal.includes('_history') ? newVal : newVal + '_history');
+    // if (column && column.length < 1) {
+    //   const arr = newVal.split('_');
+    //   console.log('历史记录列表表头------------>', arr[0] + '_monitor');
+    //   columns.value = getTableHeaderColumns(arr[0] + '_history');
+    //   if (columns.value.length < 1) {
+    //     if (historyType.value) {
+    //       columns.value = getTableHeaderColumns(historyType.value + '_history');
+    //     }
+    //   }
+    // } else {
+    //   columns.value = column;
+    // }
+    await getDeviceList();
+    nextTick(() => {
+      getDataSource();
     });
-  });
 
-  watch(
-    () => {
-      return props.columnsType;
-    },
-    async (newVal) => {
-      if (!newVal) return;
-      deviceKide.value = newVal;
-      if (historyTable.value) {
-        getForm().resetFields();
-        // getForm().updateSchema();
-        // getForm();
-      }
-      dataSource.value = [];
-      // const column = getTableHeaderColumns(newVal.includes('_history') ? newVal : newVal + '_history');
-      // if (column && column.length < 1) {
-      //   const arr = newVal.split('_');
-      //   console.log('历史记录列表表头------------>', arr[0] + '_monitor');
-      //   columns.value = getTableHeaderColumns(arr[0] + '_history');
-      //   if (columns.value.length < 1) {
-      //     if (historyType.value) {
-      //       columns.value = getTableHeaderColumns(historyType.value + '_history');
-      //     }
-      //   }
-      // } else {
-      //   columns.value = column;
-      // }
-      await getDeviceList();
-      nextTick(() => {
-        getDataSource();
-      });
-
-      if (historyTable.value) reload();
-    },
-    {
-      immediate: true,
-    }
-  );
+    if (historyTable.value) reload();
+  },
+  {
+    immediate: true,
+  }
+);
+
+watch(historyType, (type) => {
+  if (!type) return;
+  // if (historyTable.value) getForm().resetFields()
+
+  const column = getTableHeaderColumns(type.includes('_history') ? type : type + '_history');
+  if (column && column.length < 1) {
+    const arr = type.split('_');
+    columns.value = getTableHeaderColumns(arr[0] + '_history');
+  } else {
+    columns.value = column;
+  }
+  setColumns(columns.value);
+});
+
+const showCurve = ref(false);
+
+// 是否显示历史曲线,在devices_shows_history_curve字典里可以配置哪些设备类型需要显示曲线
+// 字典内的字段可以是前缀,例如fanlocal之于fanlocal_normal
+// 安全监控设备需要更多的配置,除去配置safetymonitor,还需要配置哪些安全监控设备需要曲线
+// 因此可以配置例如A1001的dataTypeName代码(可以查看真实数据参考)
+function calcShowCurveValue() {
+  const historyCurveDicts = getDictItemsByCode('devices_shows_history_curve') || [];
+  const findDict = (str) => historyCurveDicts.some(({ value }) => str.startsWith(value));
+
+  if (!props.showHistoryCurve) return false;
+  const dt = props.deviceType; // 依赖项
+
+  if (!findDict(dt)) return false;
+  if (!dt.startsWith('safetymonitor')) return true;
+
+  // 和字典的设备类型匹配后,如果是安全监控设备,需要额外的匹配安全监控类型
+  const dtns = get(selectedOption.value, 'readData.dataTypeName', ''); // 依赖项
+  return findDict(dtns);
+}
+
+function initHistoryCurveColumns() {
+  if (!props.showHistoryCurve) return;
+  const arr = historyType.value.split('_');
+  // 没错,又是安全监控。安全监控的单位无法一次定好,所以根据返回的数据协定单位
+  if (props.deviceType.startsWith('safetymonitor')) {
+    chartsColumns.value = getTableHeaderColumns(arr[0] + '_chart').map((e) => {
+      const unit = get(selectedOption.value, 'readData.unit', e.unit);
+      return {
+        ...e,
+        unit: unit,
+        seriesName: unit,
+      };
+    });
+  } else {
+    chartsColumns.value = getTableHeaderColumns(arr[0] + '_chart');
+  }
+}
+
+const tableScroll = computed(() => {
+  if (props.scroll.y && showCurve.value) return { y: props.scroll.y - 450 };
+  if (props.scroll.y) return { y: props.scroll.y - 100 };
+  return {};
+});
+
+// watch(stationType, (type) => {
+//   if (type) {
+//     nextTick(() => {
+//       getDataSource();
+//     });
+//   }
+// });
+
+watch(
+  () => props.deviceId,
+  async () => {
+    await getForm().setFieldsValue({});
+    await getDeviceList();
+  }
+);
+
+/** 获取可供查询历史数据的设备列表 */
+async function getDeviceList() {
+  // if (props.deviceType.split('_')[1] && props.deviceType.split('_')[1] === 'history') return;
+  let result;
+  let response;
+  if (props.onlyBounedDevices) {
+    response = await getDeviceListApi({
+      systemID: props.sysId,
+      devicetype: 'sys',
+    }).then(({ msgTxt }) => {
+      return { msgTxt: msgTxt.filter((e) => e.type === props.deviceType) };
+    });
+  } else if (props.sysId) {
+    response = await getDeviceListApi({
+      sysId: props.sysId,
+      devicetype: props.deviceType.startsWith('vehicle') ? 'location_normal' : props.deviceType,
+      pageSize: 10000,
+    });
+  } else if (props.deviceListApi) {
+    response = await props.deviceListApi();
+  } else {
+    response = await getDeviceListApi({ devicetype: props.deviceType, pageSize: 10000 });
+  }
 
-  watch(historyType, (type) => {
-    if (!type) return;
-    // if (historyTable.value) getForm().resetFields()
+  // 处理不同格式的数据
+  if (response['records'] && response['records'].length > 0) {
+    result = response['records'];
+  } else if (response['msgTxt'] && response['msgTxt'][0] && response['msgTxt'][0]['datalist']) {
+    result = response['msgTxt'][0]['datalist'];
+  }
+  if (response['msgTxt'] && response['msgTxt'][0]) {
+    deviceTypeName.value = response['msgTxt'][0]['typeName'];
+    deviceType.value = response['msgTxt'][0]['type'];
+  }
 
-    const column = getTableHeaderColumns(type.includes('_history') ? type : type + '_history');
-    if (column && column.length < 1) {
-      const arr = type.split('_');
-      columns.value = getTableHeaderColumns(arr[0] + '_history');
+  if (result) {
+    deviceOptions.value = [];
+    deviceOptions.value = result.map((item, index) => {
+      return {
+        label: item['strinstallpos'],
+        value: item['id'] || item['deviceID'],
+        strtype: item['strtype'] || item['deviceType'],
+        strinstallpos: item['strinstallpos'],
+        devicekind: item['devicekind'],
+        stationtype: item['stationtype'],
+        readData: item['readData'],
+      };
+    });
+    if (hasMultipleChoice) {
+      deviceOptions.value.unshift({
+        label: '全部',
+        value: 'ALL',
+        strtype: deviceOptions.value[0]?.strtype || '',
+        stationtype: deviceOptions.value[0]?.stationtype || 'plc1',
+      });
+    }
+    stationType.value = deviceOptions.value[0]['stationtype'];
+    if (props.deviceType.startsWith('vehicle')) {
+      historyType.value = 'vehicle';
     } else {
-      columns.value = column;
+      historyType.value = deviceOptions.value[0]['strtype'] || deviceOptions.value[0]['devicekind'];
     }
-    setColumns(columns.value);
-  });
 
-  const showCurve = ref(false);
-
-  // 是否显示历史曲线,在devices_shows_history_curve字典里可以配置哪些设备类型需要显示曲线
-  // 字典内的字段可以是前缀,例如fanlocal之于fanlocal_normal
-  // 安全监控设备需要更多的配置,除去配置safetymonitor,还需要配置哪些安全监控设备需要曲线
-  // 因此可以配置例如A1001的dataTypeName代码(可以查看真实数据参考)
-  function calcShowCurveValue() {
-    const historyCurveDicts = getDictItemsByCode('devices_shows_history_curve') || [];
-    const findDict = (str) => historyCurveDicts.some(({ value }) => str.startsWith(value));
-
-    if (!props.showHistoryCurve) return false;
-    const dt = props.deviceType; // 依赖项
-
-    if (!findDict(dt)) return false;
-    if (!dt.startsWith('safetymonitor')) return true;
-
-    // 和字典的设备类型匹配后,如果是安全监控设备,需要额外的匹配安全监控类型
-    const dtns = get(selectedOption.value, 'readData.dataTypeName', ''); // 依赖项
-    return findDict(dtns);
+    /** 此处使用nextTick是由于可能表单暂未更新,而下面的方法依赖表单项 */
+    nextTick(() => {
+      showCurve.value = calcShowCurveValue();
+      initHistoryCurveColumns();
+    });
   }
-
-  function initHistoryCurveColumns() {
-    if (!props.showHistoryCurve) return;
-    const arr = historyType.value.split('_');
-    // 没错,又是安全监控。安全监控的单位无法一次定好,所以根据返回的数据协定单位
-    if (props.deviceType.startsWith('safetymonitor')) {
-      chartsColumns.value = getTableHeaderColumns(arr[0] + '_chart').map((e) => {
-        const unit = get(selectedOption.value, 'readData.unit', e.unit);
-        return {
-          ...e,
-          unit: unit,
-          seriesName: unit,
-        };
-      });
+  if (VENT_PARAM.historyIsMultiple) {
+    await getForm().setFieldsValue({
+      gdeviceids: [props.deviceId ? props.deviceId : deviceOptions.value[1] ? deviceOptions.value[1]['value'] : ''],
+    });
+    await getForm().updateSchema({
+      field: 'gdeviceids',
+      componentProps: {
+        mode: 'multiple',
+        maxTagCount: 'responsive',
+      },
+    });
+  } else {
+    await getForm().setFieldsValue({
+      gdeviceids: props.deviceId ? props.deviceId : deviceOptions.value[1] ? deviceOptions.value[1]['value'] : '',
+    });
+    await getForm().updateSchema({
+      field: 'gdeviceids',
+    });
+  }
+}
+
+function resetFormParam() {
+  const formData = getForm().getFieldsValue();
+  const pagination = getPaginationRef();
+  formData['pageNo'] = pagination['current'];
+  formData['pageSize'] = pagination['pageSize'];
+  formData['column'] = 'createTime';
+
+  // ===================== 核心:最终完美处理 =====================
+  let gdeviceids = formData['gdeviceids'];
+  let realDeviceIds = [];
+
+  // 1. 统一转成数组
+  if (gdeviceids) {
+    if (Array.isArray(gdeviceids)) {
+      realDeviceIds = gdeviceids;
     } else {
-      chartsColumns.value = getTableHeaderColumns(arr[0] + '_chart');
+      realDeviceIds = String(gdeviceids)
+        .split(',')
+        .map((i) => i.trim());
     }
   }
 
-  const tableScroll = computed(() => {
-    if (props.scroll.y && showCurve.value) return { y: props.scroll.y - 450 };
-    if (props.scroll.y) return { y: props.scroll.y - 100 };
-    return {};
-  });
-
-  // watch(stationType, (type) => {
-  //   if (type) {
-  //     nextTick(() => {
-  //       getDataSource();
-  //     });
-  //   }
-  // });
-
-  watch(
-    () => props.deviceId,
-    async () => {
-      await getForm().setFieldsValue({});
-      await getDeviceList();
-    }
-  );
-
-  /** 获取可供查询历史数据的设备列表 */
-  async function getDeviceList() {
-    // if (props.deviceType.split('_')[1] && props.deviceType.split('_')[1] === 'history') return;
-    let result;
-    let response;
-    if (props.onlyBounedDevices) {
-      response = await getDeviceListApi({
-        systemID: props.sysId,
-        devicetype: 'sys',
-      }).then(({ msgTxt }) => {
-        return { msgTxt: msgTxt.filter((e) => e.type === props.deviceType) };
-      });
-    } else if (props.sysId) {
-      response = await getDeviceListApi({
-        sysId: props.sysId,
-        devicetype: props.deviceType.startsWith('vehicle') ? 'location_normal' : props.deviceType,
-        pageSize: 10000,
-      });
-    } else if (props.deviceListApi) {
-      response = await props.deviceListApi();
-    } else {
-      response = await getDeviceListApi({ devicetype: props.deviceType, pageSize: 10000 });
-    }
+  // 2. 过滤 ALL、空值
+  realDeviceIds = realDeviceIds.filter((id) => id && id !== 'ALL' && id !== '');
 
-    // 处理不同格式的数据
-    if (response['records'] && response['records'].length > 0) {
-      result = response['records'];
-    } else if (response['msgTxt'] && response['msgTxt'][0] && response['msgTxt'][0]['datalist']) {
-      result = response['msgTxt'][0]['datalist'];
-    }
-    if (response['msgTxt'] && response['msgTxt'][0]) {
-      deviceTypeName.value = response['msgTxt'][0]['typeName'];
-      deviceType.value = response['msgTxt'][0]['type'];
-    }
+  // 3. 转成逗号分隔字符串(后端要的格式)
+  const deviceIdStr = realDeviceIds.join(',');
 
-    if (result) {
-      deviceOptions.value = [];
-      deviceOptions.value = result.map((item, index) => {
-        return {
-          label: item['strinstallpos'],
-          value: item['id'] || item['deviceID'],
-          strtype: item['strtype'] || item['deviceType'],
-          strinstallpos: item['strinstallpos'],
-          devicekind: item['devicekind'],
-          stationtype: item['stationtype'],
-          readData: item['readData'],
-        };
-      });
+  // ===================== 你的业务 =====================
+  if (stationType.value !== 'redis' && deviceOptions.value[0]) {
+    formData['strtype'] = '*';
 
-      stationType.value = deviceOptions.value[0]['stationtype'];
-      if (props.deviceType.startsWith('vehicle')) {
-        historyType.value = 'vehicle';
-      } else {
-        historyType.value = deviceOptions.value[0]['strtype'] || deviceOptions.value[0]['devicekind'];
-      }
-
-      /** 此处使用nextTick是由于可能表单暂未更新,而下面的方法依赖表单项 */
-      nextTick(() => {
-        showCurve.value = calcShowCurveValue();
-        initHistoryCurveColumns();
-      });
+    if (props.sysId) {
+      formData['sysId'] = props.sysId;
     }
-    if (VENT_PARAM.historyIsMultiple) {
-      await getForm().setFieldsValue({
-        gdeviceids: [props.deviceId ? props.deviceId : deviceOptions.value[0] ? deviceOptions.value[0]['value'] : ''],
-      });
-      await getForm().updateSchema({
-        field: 'gdeviceids',
-        componentProps: {
-          mode: 'multiple',
-          maxTagCount: 'responsive',
-        },
-      });
-    } else {
-      await getForm().setFieldsValue({
-        gdeviceids: props.deviceId ? props.deviceId : deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '',
-      });
-      await getForm().updateSchema({
-        field: 'gdeviceids',
-      });
-    }
-  }
 
-  function resetFormParam() {
-    const formData = getForm().getFieldsValue();
-    const pagination = getPaginationRef();
-    formData['pageNo'] = pagination['current'];
-    formData['pageSize'] = pagination['pageSize'];
-    formData['column'] = 'createTime';
-    if (stationType.value !== 'redis' && deviceOptions.value[0]) {
-      formData['strtype'] = deviceTypeStr.value
-        ? deviceTypeStr.value
-        : deviceOptions.value[0]['strtype']
-        ? deviceOptions.value[0]['strtype']
-        : props.deviceType + '*';
-      if (props.sysId) {
-        formData['sysId'] = props.sysId;
-      }
-      return formData;
-    } else {
-      const params = {
-        pageNum: pagination['current'],
-        pageSize: pagination['pageSize'],
-        column: pagination['createTime'],
-        startTime: formData['ttime_begin'],
-        endTime: formData['ttime_end'],
-        deviceId: formData['gdeviceids'],
-        strtype: props.deviceType + '*',
-        sysId: props.sysId,
-        interval: intervalMap.get(formData['skip']) ? intervalMap.get(formData['skip']) : '1h',
-        isEmployee: props.deviceType.startsWith('vehicle') ? false : true,
-      };
-      return params;
-    }
+    // 字符串格式给后端
+    formData['gdeviceids'] = deviceIdStr;
+    return formData;
+  } else {
+    const params = {
+      pageNum: pagination['current'],
+      pageSize: pagination['pageSize'],
+      column: pagination['createTime'],
+      startTime: formData['ttime_begin'],
+      endTime: formData['ttime_end'],
+      deviceId: deviceIdStr, // 字符串!
+      strtype: '*',
+      sysId: props.sysId,
+      interval: intervalMap.get(formData['skip']) || '1h',
+      isEmployee: props.deviceType.startsWith('vehicle') ? false : true,
+    };
+    return params;
   }
-
-  async function getDataSource() {
-    dataSource.value = [];
-    setLoading(true);
-    const params = await resetFormParam();
-    if (stationType.value !== 'redis') {
-      const result = await defHttp.get({ url: '/safety/ventanalyMonitorData/listdays', params: params });
-      setPagination({ total: Math.abs(result['datalist']['total']) || 0 });
-      if (result['datalist']['records'].length > 0) {
-        dataSource.value = result['datalist']['records'].map((item: any) => {
-          return Object.assign(item, item['readData']);
-        });
-      } else {
-        dataSource.value = [];
-      }
+}
+async function getDataSource() {
+  dataSource.value = [];
+  setLoading(true);
+  const params = await resetFormParam();
+  if (stationType.value !== 'redis') {
+    const result = await defHttp.get({ url: '/safety/ventanalyMonitorData/listdays', params: params });
+    setPagination({ total: Math.abs(result['datalist']['total']) || 0 });
+    if (result['datalist']['records'].length > 0) {
+      dataSource.value = result['datalist']['records'].map((item: any) => {
+        return Object.assign(item, item['readData']);
+      });
     } else {
-      const result = await defHttp.post({ url: '/monitor/history/getHistoryData', params: params });
-      setPagination({ total: Math.abs(result['total']) || 0 });
-      dataSource.value = result['records'] || [];
+      dataSource.value = [];
     }
-    setLoading(false);
+  } else {
+    const result = await defHttp.post({ url: '/monitor/history/getHistoryData', params: params });
+    setPagination({ total: Math.abs(result['total']) || 0 });
+    dataSource.value = result['records'] || [];
   }
-
-  // 列表页面公共参数、方法
-  const { tableContext, onExportXls, onExportXlsPost } = useListPage({
-    tableProps: {
-      // api: list,
-      columns: props.columnsType ? columns : (props.columns as any[]),
-      canResize: true,
-      showTableSetting: false,
-      showActionColumn: false,
-      bordered: false,
-      size: 'small',
-      showIndexColumn: true,
-      tableLayout: 'auto',
-      formConfig: {
-        labelAlign: 'left',
-        labelWidth: 80,
-        showAdvancedButton: false,
-        showSubmitButton: false,
-        showResetButton: false,
-        baseColProps: {
-          xs: 24,
-          sm: 24,
-          md: 24,
-          lg: 9,
-          xl: 7,
-          xxl: 4,
-        },
-        schemas:
-          props.formSchemas.length > 0
-            ? props.formSchemas
-            : [
-                {
-                  field: 'ttime_begin',
-                  label: '开始时间',
-                  component: 'DatePicker',
-                  defaultValue: dayjs().startOf('date'),
-                  required: true,
-                  componentProps: {
-                    showTime: true,
-                    valueFormat: 'YYYY-MM-DD HH:mm:ss',
-                    getPopupContainer: getAutoScrollContainer,
-                  },
-                  colProps: {
-                    span: 4,
-                  },
+  setLoading(false);
+}
+
+// 列表页面公共参数、方法
+const { tableContext, onExportXls, onExportXlsPost } = useListPage({
+  tableProps: {
+    // api: list,
+    columns: props.columnsType ? columns : (props.columns as any[]),
+    canResize: true,
+    showTableSetting: false,
+    showActionColumn: false,
+    bordered: false,
+    size: 'small',
+    showIndexColumn: true,
+    tableLayout: 'auto',
+    formConfig: {
+      labelAlign: 'left',
+      labelWidth: 80,
+      showAdvancedButton: false,
+      showSubmitButton: false,
+      showResetButton: false,
+      baseColProps: {
+        xs: 24,
+        sm: 24,
+        md: 24,
+        lg: 9,
+        xl: 7,
+        xxl: 4,
+      },
+      schemas:
+        props.formSchemas.length > 0
+          ? props.formSchemas
+          : [
+              {
+                field: 'ttime_begin',
+                label: '开始时间',
+                component: 'DatePicker',
+                defaultValue: dayjs().startOf('date'),
+                required: true,
+                componentProps: {
+                  showTime: true,
+                  valueFormat: 'YYYY-MM-DD HH:mm:ss',
+                  getPopupContainer: getAutoScrollContainer,
+                },
+                colProps: {
+                  span: 4,
                 },
-                {
-                  field: 'ttime_end',
-                  label: '结束时间',
-                  component: 'DatePicker',
-                  defaultValue: dayjs(),
-                  required: true,
-                  componentProps: {
-                    showTime: true,
-                    valueFormat: 'YYYY-MM-DD HH:mm:ss',
-                    getPopupContainer: getAutoScrollContainer,
-                  },
-                  colProps: {
-                    span: 4,
-                  },
+              },
+              {
+                field: 'ttime_end',
+                label: '结束时间',
+                component: 'DatePicker',
+                defaultValue: dayjs(),
+                required: true,
+                componentProps: {
+                  showTime: true,
+                  valueFormat: 'YYYY-MM-DD HH:mm:ss',
+                  getPopupContainer: getAutoScrollContainer,
                 },
-                {
-                  label: computed(() => `${deviceKide.value.startsWith('location') ? '查询人员' : '查询设备'}`),
-                  field: 'gdeviceids',
-                  component: 'Select',
-                  required: true,
-                  componentProps: {
+                colProps: {
+                  span: 4,
+                },
+              },
+              {
+                label: computed(() => `${deviceKide.value.startsWith('location') ? '查询人员' : '查询设备'}`),
+                field: 'gdeviceids',
+                component: 'Select',
+                required: true,
+                componentProps: computed(() => {
+                  const isMultiple = hasMultipleChoice;
+                  const ALL_VALUE = 'ALL';
+
+                  return {
                     showSearch: true,
+                    mode: isMultiple ? 'multiple' : undefined,
+                    multiple: isMultiple,
+                    optionType: isMultiple ? 'checkbox' : undefined,
                     filterOption: (input: string, option: any) => {
                       return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
                     },
                     options: deviceOptions,
-                    onChange: (e, option) => {
-                      if (option && (option['strinstallpos'] || option['strtype'] || option['devicekind'])) {
-                        historyType.value = option['strtype'] || option['devicekind'];
+
+                    onChange: (valueList, selectedOptions) => {
+                      const form = getForm();
+                      if (!isMultiple) {
+                        const lastOption = selectedOptions;
+                        if (lastOption) {
+                          historyType.value = lastOption.strtype || lastOption.devicekind;
+                          deviceTypeStr.value = lastOption.strtype;
+                          stationType.value = lastOption.stationtype;
+                        }
+                        nextTick(() => {
+                          showCurve.value = calcShowCurveValue();
+                          initHistoryCurveColumns();
+                          getDataSource();
+                        });
+                        return;
                       }
-                      if (option['strtype']) {
-                        deviceTypeStr.value = option['strtype'];
+                      const userSelectedIds = valueList.filter((id) => id !== ALL_VALUE);
+                      const allRealIds = deviceOptions.value.filter((o) => o.value !== ALL_VALUE).map((o) => o.value);
+                      const justClickedAll = valueList.at(-1) === ALL_VALUE;
+                      const isAllChecked = valueList.includes(ALL_VALUE);
+
+                      // 1. 手动点了【全部】→ 全选
+                      if (justClickedAll) {
+                        form.setFieldsValue({
+                          gdeviceids: [ALL_VALUE, ...allRealIds],
+                        });
+                      } else if (isAllChecked && userSelectedIds.length < allRealIds.length) {
+                        form.setFieldsValue({
+                          gdeviceids: userSelectedIds,
+                        });
+                      } else if (!isAllChecked && userSelectedIds.length === allRealIds.length && allRealIds.length > 0) {
+                        form.setFieldsValue({
+                          gdeviceids: [ALL_VALUE, ...userSelectedIds],
+                        });
+                      }
+                      const realOptions = selectedOptions?.filter((o) => o.value !== ALL_VALUE);
+                      const lastOption = realOptions?.at(-1);
+                      if (lastOption) {
+                        historyType.value = lastOption.strtype || lastOption.devicekind;
+                        deviceTypeStr.value = lastOption.strtype;
+                        stationType.value = lastOption.stationtype;
                       }
-                      stationType.value = option['stationtype'];
                       nextTick(() => {
                         showCurve.value = calcShowCurveValue();
                         initHistoryCurveColumns();
                         getDataSource();
                       });
                     },
-                  },
-                  colProps: {
-                    span: 5,
-                  },
+                  };
+                }),
+                colProps: { span: 5 },
+              },
+              {
+                label: '间隔时间',
+                field: 'skip',
+                component: 'Select',
+                defaultValue: '8',
+                componentProps: {
+                  options: [
+                    {
+                      label: '1秒',
+                      value: '1',
+                    },
+                    {
+                      label: '5秒',
+                      value: '2',
+                    },
+                    {
+                      label: '10秒',
+                      value: '3',
+                    },
+                    {
+                      label: '30秒',
+                      value: '4',
+                    },
+                    {
+                      label: '1分钟',
+                      value: '5',
+                    },
+                    {
+                      label: '10分钟',
+                      value: '6',
+                    },
+                    {
+                      label: '30分钟',
+                      value: '7',
+                    },
+                    {
+                      label: '1小时',
+                      value: '8',
+                    },
+                    {
+                      label: '1天',
+                      value: '9',
+                    },
+                  ],
                 },
-                {
-                  label: '间隔时间',
-                  field: 'skip',
-                  component: 'Select',
-                  defaultValue: '8',
-                  componentProps: {
-                    options: [
-                      {
-                        label: '1秒',
-                        value: '1',
-                      },
-                      {
-                        label: '5秒',
-                        value: '2',
-                      },
-                      {
-                        label: '10秒',
-                        value: '3',
-                      },
-                      {
-                        label: '30秒',
-                        value: '4',
-                      },
-                      {
-                        label: '1分钟',
-                        value: '5',
-                      },
-                      {
-                        label: '10分钟',
-                        value: '6',
-                      },
-                      {
-                        label: '30分钟',
-                        value: '7',
-                      },
-                      {
-                        label: '1小时',
-                        value: '8',
-                      },
-                      {
-                        label: '1天',
-                        value: '9',
-                      },
-                    ],
-                  },
-                  colProps: {
-                    span: 3,
-                  },
+                colProps: {
+                  span: 3,
                 },
-              ],
-        // fieldMapToTime: [['tickectDate', ['ttime_begin', 'ttime_end'], '']],
-      },
-      // fetchSetting: {
-      //   listField: 'datalist',
-      //   totalField: 'datalist.total',
-      // },
-      pagination: {
-        current: 1,
-        pageSize: 10,
-        pageSizeOptions: ['10', '30', '50', '100'],
-        showQuickJumper: false,
-      },
-      beforeFetch() {
-        const newParams = { ...resetFormParam() };
-        return newParams;
-      },
-      // afterFetch(result) {
-      //   const resultItems = result['records'];
-      //   resultItems.map((item) => {
-      //     Object.assign(item, item['readData']);
-      //   });
-      //   console.log('result---------------->', result);
-      //   return resultItems;
-      // },
+              },
+            ],
+      // fieldMapToTime: [['tickectDate', ['ttime_begin', 'ttime_end'], '']],
     },
-    exportConfig: {
-      name: '设备历史列表',
-      url: getExportXlsUrl,
+    // fetchSetting: {
+    //   listField: 'datalist',
+    //   totalField: 'datalist.total',
+    // },
+    pagination: {
+      current: 1,
+      pageSize: 10,
+      pageSizeOptions: ['10', '30', '50', '100'],
+      showQuickJumper: false,
     },
-  });
-
-  //注册table数据
-  const [registerTable, { reload, setLoading, getForm, setColumns, getPaginationRef, setPagination }] = tableContext;
+    beforeFetch() {
+      const newParams = { ...resetFormParam() };
+      return newParams;
+    },
+    // afterFetch(result) {
+    //   const resultItems = result['records'];
+    //   resultItems.map((item) => {
+    //     Object.assign(item, item['readData']);
+    //   });
+    //   console.log('result---------------->', result);
+    //   return resultItems;
+    // },
+  },
+  exportConfig: {
+    name: '设备历史列表',
+    url: getExportXlsUrl,
+  },
+});
+
+//注册table数据
+const [registerTable, { reload, setLoading, getForm, setColumns, getPaginationRef, setPagination }] = tableContext;
+
+function onExportXlsFn() {
+  const params = resetFormParam();
+  // 判断时间间隔和查询时间区间,数据量下载大时进行提示
+  if (stationType.value !== 'redis') {
+    return onExportXls(params);
+  } else {
+    return onExportXlsPost(params);
+  }
+}
 
-  function onExportXlsFn() {
-    const params = resetFormParam();
-    // 判断时间间隔和查询时间区间,数据量下载大时进行提示
-    if (stationType.value !== 'redis') {
-      return onExportXls(params);
-    } else {
-      return onExportXlsPost(params);
-    }
+watchEffect(() => {
+  if (historyTable.value && dataSource) {
+    const data = dataSource.value || [];
+    emit('change', data);
   }
+});
 
-  watchEffect(() => {
-    if (historyTable.value && dataSource) {
-      const data = dataSource.value || [];
-      emit('change', data);
-    }
-  });
+onMounted(async () => {
+  await getDeviceList();
+  if (deviceOptions.value[0]) {
+    nextTick(async () => {
+      await getDataSource();
+    });
+  }
 
-  onMounted(async () => {
-    await getDeviceList();
+  watch([() => getPaginationRef()['current'], () => getPaginationRef()['pageSize']], async () => {
     if (deviceOptions.value[0]) {
-      nextTick(async () => {
-        await getDataSource();
-      });
-    }
-
-    watch([() => getPaginationRef()['current'], () => getPaginationRef()['pageSize']], async () => {
       if (deviceOptions.value[0]) {
-        if (deviceOptions.value[0]) {
-          await getDataSource();
-        }
+        await getDataSource();
       }
-    });
+    }
   });
-  defineExpose({ setLoading });
+});
+defineExpose({ setLoading });
 </script>
 
 <style scoped lang="less">
-  @import '/@/design/theme.less';
-
-  :deep(.@{ventSpace}-table-body) {
-    height: auto !important;
-  }
-  :deep(.zxm-picker) {
-    height: 30px !important;
-  }
-  .history-table {
-    width: 100%;
-    :deep(.jeecg-basic-table-form-container) {
-      .@{ventSpace}-form {
-        padding: 0 !important;
-        border: none !important;
-        margin-bottom: 0 !important;
-        .@{ventSpace}-picker,
-        .@{ventSpace}-select-selector {
-          width: 100% !important;
-          height: 100%;
-          background: #00000017;
-          border: 1px solid #b7b7b7;
-          input,
-          .@{ventSpace}-select-selection-item,
-          .@{ventSpace}-picker-suffix {
-            color: #fff;
-          }
-          .@{ventSpace}-select-selection-placeholder {
-            color: #ffffffaa;
-          }
+@import '/@/design/theme.less';
+
+:deep(.@{ventSpace}-table-body) {
+  height: auto !important;
+}
+:deep(.zxm-picker) {
+  height: 30px !important;
+}
+.history-table {
+  width: 100%;
+  :deep(.jeecg-basic-table-form-container) {
+    .@{ventSpace}-form {
+      padding: 0 !important;
+      border: none !important;
+      margin-bottom: 0 !important;
+      .@{ventSpace}-picker,
+      .@{ventSpace}-select-selector {
+        width: 100% !important;
+        height: 100%;
+        background: #00000017;
+        border: 1px solid #b7b7b7;
+        input,
+        .@{ventSpace}-select-selection-item,
+        .@{ventSpace}-picker-suffix {
+          color: #fff;
+        }
+        .@{ventSpace}-select-selection-placeholder {
+          color: #ffffffaa;
         }
-      }
-      .@{ventSpace}-table-title {
-        min-height: 0 !important;
       }
     }
-    .pagination-box {
-      display: flex;
-      justify-content: flex-end;
-      align-items: center;
-      .page-num {
-        border: 1px solid #0090d8;
-        padding: 4px 8px;
-        margin-right: 5px;
-        color: #0090d8;
-      }
-      .btn {
-        margin-right: 10px;
-      }
+    .@{ventSpace}-table-title {
+      min-height: 0 !important;
     }
   }
-
-  .history-chart {
-    background-color: #0090d822;
-    margin: 0 10px;
+  .pagination-box {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    .page-num {
+      border: 1px solid #0090d8;
+      padding: 4px 8px;
+      margin-right: 5px;
+      color: #0090d8;
+    }
+    .btn {
+      margin-right: 10px;
+    }
   }
+}
+
+.history-chart {
+  background-color: #0090d822;
+  margin: 0 10px;
+}
 </style>