Quellcode durchsuchen

Merge branch 'master' of http://39.97.59.228:8013/hrx/goaf-monitoring-system

lxh vor 5 Monaten
Ursprung
Commit
62ea7c9c5b

+ 9 - 0
src/api/sys/menu.ts

@@ -99,3 +99,12 @@ export function getBackMenuAndPerms() {
 //     defHttp.get({ url: Api.SwitchVue3Menu });
 //   });
 // };
+
+export function getEnfMineTree() {
+  return defHttp.get<EnfMineTreeItem[]>(
+    { url: Api.getEnfMineTreeData },
+    {
+      isTransformResponse: false,
+    }
+  );
+}

+ 6 - 8
src/components/Form/src/componentMap.ts

@@ -73,14 +73,14 @@ import JAddInput from './jeecg/components/JAddInput.vue';
 import { Time } from '/@/components/Time';
 import JRangeNumber from './jeecg/components/JRangeNumber.vue';
 import UserSelect from './jeecg/components/userSelect/index.vue';
-import JRangeDate from './jeecg/components/JRangeDate.vue'
-import JRangeTime from './jeecg/components/JRangeTime.vue'
-import JInputSelect from './jeecg/components/JInputSelect.vue'
+import JRangeDate from './jeecg/components/JRangeDate.vue';
+import JRangeTime from './jeecg/components/JRangeTime.vue';
+import JInputSelect from './jeecg/components/JInputSelect.vue';
 import RoleSelectInput from './jeecg/components/roleSelect/RoleSelectInput.vue';
 import JSelectUserByDeptPost from './jeecg/components/JSelectUserByDeptPost.vue';
 import JDatePickerMultiple from './jeecg/components/JDatePickerMultiple.vue';
-import {DatePickerInFilter, CascaderPcaInFilter} from "@/components/InFilter";
-
+import { DatePickerInFilter, CascaderPcaInFilter } from '@/components/InFilter';
+import formConfig from './jeecg/components/formCard/formConfig.vue';
 const componentMap = new Map<ComponentType, Component>();
 
 componentMap.set('Time', Time);
@@ -178,9 +178,7 @@ componentMap.set('RoleSelect', RoleSelectInput);
 componentMap.set('JInputSelect', JInputSelect);
 componentMap.set('JSelectDepartPost', JSelectDepartPost);
 componentMap.set('JSelectUserByDeptPost', JSelectUserByDeptPost);
-
-
-
+componentMap.set('formConfig', formConfig);
 export function add(compName: ComponentType, component: Component) {
   componentMap.set(compName, component);
 }

+ 135 - 0
src/components/Form/src/jeecg/components/formCard/formConfig.vue

@@ -0,0 +1,135 @@
+<template>
+  <a-form>
+    <a-row class="form-row">
+      <div class="custom-cascader">
+        <!-- 执法处 -->
+        <a-select v-model:value="pca.lawDept" placeholder="请选择执法处" style="width: 180px; margin-right: 8px" @change="handleLawDeptChange">
+          <a-select-option v-for="item in lawDeptOptions" :key="item.value" :value="item.value">
+            {{ item.label }}
+          </a-select-option>
+        </a-select>
+        <a-select
+          v-model:value="pca.area"
+          placeholder="请选择区域"
+          style="width: 180px; margin-right: 8px"
+          @change="handleAreaChange"
+          :disabled="!pca.lawDept"
+        >
+          <a-select-option v-for="item in areaOptions" :key="item.value" :value="item.value">
+            {{ item.label }}
+          </a-select-option>
+        </a-select>
+        <!-- 选择煤矿 -->
+        <a-select v-model:value="pca.position" placeholder="请选择煤矿" style="width: 180px" :disabled="!pca.area">
+          <a-select-option v-for="item in positionOptions" :key="item.value" :value="item.value">
+            {{ item.label }}
+          </a-select-option>
+        </a-select>
+      </div>
+    </a-row>
+  </a-form>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, onMounted, nextTick, watch } from 'vue';
+// 替换为你的实际接口请求函数
+import { getEnfMineTreeData } from './mineData.api';
+const props = defineProps({
+  value: {
+    type: Object,
+    default: () => ({
+      lawDept: '', // 执法处ID
+      area: '', // 区域ID
+      mineCode: '', // 煤矿编码
+    }),
+  },
+});
+const emit = defineEmits(['change', 'update:value', 'update:lawDept', 'update:area', 'update:position']);
+const pca = reactive({
+  lawDept: '', // 执法处
+  area: '', // 区域
+  position: '', // 具体位置
+});
+
+// 下拉选项列表
+const lawDeptOptions = ref<Array<{ label: string; value: string | number }>>([]);
+const areaOptions = ref<Array<{ label: string; value: string | number }>>([]);
+const positionOptions = ref<Array<{ label: string; value: string | number }>>([]);
+const rawLawDeptData = ref<any[]>([]); // 保存原始执法处数据以备后续查找
+// // 初始化加载执法处列表
+const initLawDeptList = async () => {
+  // 调用获取执法处列表的接口
+  try {
+    const res = await getEnfMineTreeData();
+    rawLawDeptData.value = res; // 保存原始数据以备后续查找
+    lawDeptOptions.value = res.map((item) => ({
+      label: item.departName, // 显示执法处名称
+      value: item.id, // 绑定执法处ID
+    }));
+  } catch (error) {
+    console.error('加载执法处列表失败:', error);
+  }
+};
+
+// // 选择区域后加载对应具体位置
+const handleLawDeptChange = async (depId: string | number) => {
+  pca.area = '';
+  pca.position = '';
+  areaOptions.value = [];
+  positionOptions.value = [];
+
+  // 2. 找到选中的执法处原始数据
+  const currentDept = rawLawDeptData.value.find((item) => item.id === depId);
+  if (!currentDept) {
+    console.warn('未找到该执法处数据');
+    return;
+  }
+  console.log(currentDept, '1111111');
+  // 3. 核心:绑定对应childDepart的label=departName,value=id
+  if (currentDept.childDepart && currentDept.childDepart.length > 0) {
+    areaOptions.value = currentDept.childDepart.map((child) => ({
+      label: child.departName, // 显示区域名称
+      value: child.id, // 绑定区域ID
+    }));
+    console.log(`执法处【${currentDept.departName}】对应的区域:`, areaOptions.value);
+  } else {
+    console.warn(`执法处【${currentDept.departName}】暂无下属区域`);
+  }
+};
+// const handleAreaChange = async (areaId: string | number) => {
+//   pca.position = '';
+//   positionOptions.value = [];
+//   // 1. 找到选中的执法处原始数据
+//   const currentDept = rawLawDeptData.value.find((item) => item.id === pca.lawDept);
+//   if (!currentDept) {
+//     console.warn('未找到该执法处数据');
+//     return;
+//   }
+//   // 2. 在执法处的childDepart中找到选中的区域数据
+//   const currentArea = currentDept.childDepart.find((area) => area.id === areaId);
+//   if (!currentArea) {
+//     console.warn('未找到该区域数据');
+//     return;
+//   }
+// };
+
+// 页面初始化时加载执法处列表
+onMounted(() => {
+  nextTick(async () => {
+    await initLawDeptList();
+  });
+});
+</script>
+
+<style scoped>
+.custom-cascader {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+/* 可选:调整选择框样式,和原JAreaSelect保持一致 */
+.custom-cascader :deep(.ant-select) {
+  border-radius: 4px;
+}
+</style>

+ 15 - 0
src/components/Form/src/jeecg/components/formCard/mineData.api.ts

@@ -0,0 +1,15 @@
+import { defHttp } from '/@/utils/http/axios';
+enum Api {
+  getEnfMineTreeData = '/jeecg-system/sys/user/getEnfMineTreeData',
+}
+//获取执法处以及区域数据
+export const getEnfMineTreeData = () =>
+  defHttp.get({
+    url: Api.getEnfMineTreeData,
+  });
+//根据区域ID获取煤矿列表
+// export const getProvinceAlarmHistory = (params) =>
+//   defHttp.post({
+//     url: Api.getProvinceAlarmHistory,
+//     params,
+//   });

+ 1 - 1
src/components/Form/src/types/index.ts

@@ -82,6 +82,7 @@ export interface ColEx {
 }
 
 export type ComponentType =
+  | 'formConfig'
   | 'Input'
   | 'InputGroup'
   | 'InputPassword'
@@ -162,4 +163,3 @@ export type ComponentType =
   | 'JRangeNumber'
   | 'JLinkTableCard'
   | 'JInputSelect';
-

+ 1 - 1
src/components/Loading/src/Loading.vue

@@ -57,7 +57,7 @@
     height: 100%;
     justify-content: center;
     align-items: center;
-    background-color: rgba(240, 242, 245, 0.4);
+    background-color: rgba(240, 242, 245);
 
     &.absolute {
       position: absolute;

+ 0 - 1
src/components/Table/src/BasicTable.vue

@@ -14,7 +14,6 @@
       <template #[item]="data" v-for="item in ['formTitle', 'resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']">
         <slot :name="item" v-bind="data || {}"></slot>
       </template>
-      <component :is="getBindValues.formTitle"></component>
       <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
         <slot :name="item" v-bind="data || {}"></slot>
       </template>

+ 6 - 0
src/design/index.less

@@ -327,3 +327,9 @@ html[data-theme='dark'] {
     color: red !important;
   }
 }
+
+.action-btn {
+  cursor: pointer;
+  border: none;
+  // padding: 4px;
+}

+ 6 - 0
src/views/analysis/warningAnalysis/airLeakStatus/airLeak.api.ts

@@ -5,6 +5,7 @@ enum Api {
   getMineData = '/province/mineData/getMineData',
   getGoafData = '/province/device/getGoafData',
   getProvinceAlarmHistory = '/province/device/getProvinceAlarmHistory',
+  getEnfMineTreeData = '/jeecg-system/sys/user/getEnfMineTreeData',
 }
 //查询煤矿列表
 export const getMineData = (params) =>
@@ -30,3 +31,8 @@ export const getProvinceAlarm = (params) =>
     url: Api.getProvinceAlarm,
     params,
   });
+//获取执法处以及区域数据
+export const getEnfMineTreeData = () =>
+  defHttp.get({
+    url: Api.getEnfMineTreeData,
+  });

+ 21 - 81
src/views/analysis/warningAnalysis/airLeakStatus/index.vue

@@ -43,6 +43,8 @@ import { Tabs, TabPane } from 'ant-design-vue';
 import MiniBoard from '/@/components/Configurable/detail/MiniBoard.vue';
 import { SvgIcon } from '/@/components/Icon';
 import { getMineData, getProvinceAlarm, getGoafData, getProvinceAlarmHistory } from './airLeak.api';
+import formConfig from '/@/components/Form/src/jeecg/components/formCard/formConfig.vue';
+import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue';
 // 引入模拟数据
 import { columns, boardData, searchFormSchema, historicalMinesData } from './airLeakStatus.data';
 
@@ -61,95 +63,21 @@ const historyData = ref([]);
 const [registerTable] = useTable({
   dataSource: minesData,
   title: '密闭漏风状态判定',
+  api: getProvinceAlarm,
   columns,
   formConfig: {
     labelWidth: 120,
     schemas: [
       {
-        label: '查询煤矿',
-        field: 'mineCode',
-        component: 'Select',
-        defaultValue: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '',
-        componentProps: {
-          showSearch: true,
-          filterOption: (input: string, option: any) => {
-            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
-          },
-          options: deviceOptions,
-          onChange: async (e, option) => {
-            await fetchAlarmData(e);
-          },
-        },
-        colProps: {
-          span: 6,
-        },
-      },
-      {
-        field: 'mineName',
-        label: '所属执法处',
-        component: 'Select',
-        componentProps: {
-          options: [
-            { label: '执法一处', value: '0' },
-            { label: '执法二处', value: '1' },
-            { label: '执法三处', value: '2' },
-          ],
-        },
-        colProps: { span: 6 },
-      },
-      {
-        field: 'mineName',
-        label: '所属区域',
-        component: 'Select',
-        componentProps: {
-          options: [
-            { label: '执法一处', value: '0' },
-            { label: '执法二处', value: '1' },
-            { label: '执法三处', value: '2' },
-          ],
-        },
-        colProps: { span: 6 },
-      },
-      {
-        field: 'mineName',
-        label: '煤矿名称',
-        component: 'Input',
-        colProps: { span: 6 },
-      },
-      {
-        field: 'mineNameAbbr',
-        label: '煤矿简称',
-        component: 'Input',
-        colProps: { span: 6 },
-      },
-      {
-        field: 'productStatus',
-        label: '生产状态',
-        component: 'Select',
-        componentProps: {
-          options: [
-            { label: '拟建矿井', value: '0' },
-            { label: '正常生产矿井', value: '1' },
-            { label: '长期停产矿井', value: '2' },
-          ],
-        },
-        colProps: { span: 6 },
-      },
-      {
-        field: 'riskLevel',
-        label: '自燃情况',
-        component: 'Select',
-        componentProps: {
-          options: [
-            { label: 'Ⅰ类容易自燃', value: '0' },
-            { label: 'Ⅱ类自燃', value: '1' },
-            { label: 'Ⅲ类不易自燃', value: '2' },
-          ],
-        },
-        colProps: { span: 6 },
+        label: '',
+        field: 'mineCode', // 对应组件的value.mineCode(最终传给Table的查询参数)
+        component: 'formConfig', // 自定义组件名
+        componentProps: {},
+        rules: [],
       },
     ],
     showAdvancedButton: false,
+    schemaGroupNames: ['常规查询'],
   },
   pagination: false,
   striped: false,
@@ -313,9 +241,15 @@ const getGoafDataList = async (mineId) => {
     };
   });
 };
+const getEnfMineTreeData = async () => {
+  const res = await getEnfMineTreeData();
+  console.log(res, '1111111');
+  // deviceOptions.value = res;
+};
 onMounted(() => {
   // 页面挂载时的逻辑
   getMineDataList();
+  getEnfMineTreeData();
 });
 </script>
 
@@ -344,4 +278,10 @@ onMounted(() => {
   width: 16px;
   height: 16px;
 }
+:deep(.jeecg-basic-table-form-container .ant-form) {
+  border: none !important;
+}
+:where(.css-dev-only-do-not-override-x9w3vz).ant-form-item .ant-form-item-label {
+  margin-top: 10px !important;
+}
 </style>

+ 1 - 0
src/views/analysis/warningAnalysis/autoFireAnalysis/index.vue

@@ -162,6 +162,7 @@ const [registerTable] = useTable({
       },
     ],
     showAdvancedButton: false,
+    schemaGroupNames: ['常规查询', '高级查询'],
   },
   pagination: false,
   striped: false,

+ 102 - 0
src/views/system/algorithm/algorithm.api.ts

@@ -0,0 +1,102 @@
+import { defHttp } from '/@/utils/http/axios';
+import { isNil } from 'lodash-es';
+import { getEnfMineTree } from '/@/api/sys/menu';
+import { message } from 'ant-design-vue';
+
+enum Api {
+  getMineData = '/province/mineData/getMineData',
+  getCoalSeam = '/province/mineData/getCoalSeam',
+  getCoalSeamAlarmRule = '/province/alarm/getCoalSeamAlarmRule',
+  updateCoalSeamAlarmRule = '/province/alarm/updateCoalSeamAlarmRule',
+  addCoalSeamAlarmRule = '/province/alarm/addCoalSeamAlarmRule',
+  deleteCoalSeamAlarmRule = '/province/alarm/deleteCoalSeamAlarmRule',
+  getGoafList = '/province/device/getGoafList',
+  getGoafDataLimit = '/province/alarm/getGoafDataLimit',
+  addGoafDataLimit = '/province/alarm/addGoafDataLimit',
+  updateGoafDataLimit = '/province/alarm/updateGoafDataLimit',
+  deleteGoafDataLimit = '/province/alarm/deleteGoafDataLimit',
+}
+
+export function getMineData(params: any) {
+  return defHttp.post({ url: Api.getMineData, params });
+}
+export function getCoalSeam(params: any) {
+  return defHttp.post({ url: Api.getCoalSeam, params });
+}
+export function getCoalSeamAlarmRule(params: any) {
+  return defHttp.post({ url: Api.getCoalSeamAlarmRule, params });
+}
+export function updateCoalSeamAlarmRule(params: any) {
+  return defHttp.post({ url: Api.updateCoalSeamAlarmRule, params }).then(() => {
+    message.success('修改成功');
+  });
+}
+export function addCoalSeamAlarmRule(params: any) {
+  return defHttp.post({ url: Api.addCoalSeamAlarmRule, params });
+}
+export function deleteCoalSeamAlarmRule(params: any) {
+  return defHttp.post({ url: Api.deleteCoalSeamAlarmRule, params });
+}
+export function getGoafList(params: any) {
+  return defHttp.post({ url: Api.getGoafList, params });
+}
+export function getGoafDataLimit(params: any) {
+  return defHttp.post({ url: Api.getGoafDataLimit, params }, { joinParamsToUrl: true });
+}
+export function addGoafDataLimit(params: any) {
+  return defHttp.post({ url: Api.addGoafDataLimit, params });
+}
+export function updateGoafDataLimit(params: any) {
+  return defHttp.post({ url: Api.updateGoafDataLimit, params });
+}
+export function deleteGoafDataLimit(params: any) {
+  return defHttp.post({ url: Api.deleteGoafDataLimit, params });
+}
+
+export function getMineTree(params: { searchValue: string }) {
+  return getEnfMineTree().then((tree) => {
+    // 头一个真正的叶节点
+    let firstLeafKey;
+    // 它的父节点
+    let firstParentKey;
+
+    const transformKeys = (arr: any[]) => {
+      return arr.map((r) => {
+        const isLeaf = isNil(r.childDepart);
+
+        // 因为接口返回只有两层所以简单处理firstParentKey
+        if (isLeaf && !firstLeafKey) {
+          firstLeafKey = r.id;
+          firstParentKey = r.parentId;
+        }
+
+        return {
+          title: r.departName,
+          key: r.id,
+          isLeaf: isLeaf,
+          children: isLeaf ? undefined : transformKeys(r.childDepart),
+        };
+      });
+    };
+
+    const filterTreeNodes = (nodes: any[], value: string) => {
+      if (!value) return nodes;
+      return nodes.filter((t) => {
+        // 如果此次匹配成功,那么该节点应返回
+        if (t.title.includes(value)) {
+          return true;
+        }
+        // 如果没有则看看它的子节点是否有匹配的
+        if (t.children) {
+          return filterTreeNodes(t.children, value).length > 0;
+        }
+      });
+    };
+
+    return {
+      tree: filterTreeNodes(transformKeys(tree), params.searchValue),
+      firstLeafKey,
+      firstParentKey,
+    };
+  });
+}

+ 459 - 44
src/views/system/algorithm/algorithm.data.ts

@@ -1,20 +1,34 @@
+import { h } from 'vue';
 import { FormSchema } from '/@/components/Form';
 import { BasicColumn } from '/@/components/Table';
-import { getDepartName } from '@/utils/common/compUtils';
+import { Tag } from 'ant-design-vue';
+import { options } from '../../monitor/mynews/XssWhiteList';
+
+const goafAlarmOptions = [
+  { value: 'ch4Val', label: '甲烷' },
+  { value: 'o2Val', label: '氧气' },
+  { value: 'coVal', label: '一氧化碳' },
+  { value: 'co2Val', label: '二氧化碳' },
+  { value: 'c2h4Val', label: '乙烯' },
+  { value: 'c2h2Val', label: '乙炔' },
+  { value: 'sourcePressure', label: '压差' },
+  { value: 'temperature', label: '温度' },
+];
 
 /** 煤层预警参数 表格配置 */
 export const columnsCoalAlarm: BasicColumn[] = [
   {
-    title: 'N1',
-    dataIndex: 'name1',
-    width: 100,
+    title: '煤矿名称',
+    dataIndex: 'mineName',
+    width: 400,
   },
   {
-    title: '部门',
-    dataIndex: 'departName',
-    customRender: ({ text }) => {
-      return getDepartName(text);
-    },
+    title: '煤层名称',
+    dataIndex: 'coalSeamName',
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
   },
   /*  {
     title: '职务',
@@ -22,50 +36,40 @@ export const columnsCoalAlarm: BasicColumn[] = [
     width: 150,
     slots: { customRender: 'post' },
   },*/
-  {
-    title: '手机',
-    width: 110,
-    dataIndex: 'phone',
-    customRender: ({ record, text }) => {
-      if (record.izHideContact && record.izHideContact === '1') {
-        return '/';
-      }
-      return text;
-    },
-  },
 ];
 
 /** 采空区超限 表格配置 */
 export const columnsGoafLimit: BasicColumn[] = [
   {
-    title: 'N2',
-    dataIndex: 'name2',
-    width: 100,
+    title: '采空区名称',
+    dataIndex: 'devicePos',
+    width: 400,
   },
   {
-    title: '部门',
-    dataIndex: 'departName',
-    customRender: ({ text }) => {
-      return getDepartName(text);
-    },
+    title: '气温',
+    dataIndex: 'fireAirTemperature',
+  },
+  {
+    title: '水温',
+    dataIndex: 'fireWaterTemperature',
   },
-  /*  {
-    title: '职务',
-    dataIndex: 'post',
-    width: 150,
-    slots: { customRender: 'post' },
-  },*/
   {
-    title: '手机',
-    width: 110,
-    dataIndex: 'phone',
-    customRender: ({ record, text }) => {
-      if (record.izHideContact && record.izHideContact === '1') {
-        return '/';
-      }
-      return text;
+    title: '状态',
+    dataIndex: 'status',
+    customRender({ text }) {
+      return h(
+        Tag,
+        {
+          color: text == 1 ? 'success' : 'error',
+        },
+        text == 1 ? '正常' : '异常'
+      );
     },
   },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+  },
 ];
 
 export const searchFormSchema: FormSchema[] = [
@@ -77,5 +81,416 @@ export const searchFormSchema: FormSchema[] = [
   },
 ];
 
-export const schemasCoalAlarm: FormSchema[] = [];
-export const schemasGoafLimit: FormSchema[] = [];
+export const schemasCoalAlarm: FormSchema[] = [
+  // "mineCode": null,
+  // "coalSeamId": null,
+  {
+    label: 'ID',
+    field: 'id',
+    show: false,
+    component: 'Input',
+  },
+  {
+    label: '规则名称:',
+    field: 'ruleName',
+    labelWidth: 118,
+    component: 'Input',
+
+    colProps: { span: 24 },
+  },
+  {
+    field: 'm1',
+    label: '密闭内外压差变化风险提示模型',
+    // labelWidth: 110,
+    component: 'Divider',
+  },
+  {
+    field: 'ycWarn1',
+    label: '预警等级(Ⅰ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '<= 绝对压差变化(%) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'ycWarn1Start', label: '', show: false, component: 'Input' },
+  { field: 'ycWarn1End', label: '', show: false, component: 'Input' },
+  {
+    field: 'ycWarn2',
+    label: '预警等级(Ⅱ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '<= 绝对压差变化(%) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'ycWarn2Start', label: '', show: false, component: 'Input' },
+  { field: 'ycWarn2End', label: '', show: false, component: 'Input' },
+  {
+    field: 'ycWarn3',
+    label: '预警等级(Ⅲ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '<= 绝对压差变化(%) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'ycWarn3Start', label: '', show: false, component: 'Input' },
+  { field: 'ycWarn3End', label: '', show: false, component: 'Input' },
+  {
+    field: 'ycWarn4',
+    label: '预警等级(Ⅳ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  绝对压差变化(%) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'ycWarn4', label: '', show: false, component: 'Input' },
+  {
+    field: 'm2',
+    label: '煤自然发火隐患分级预警模型',
+    // labelWidth: 110,
+    component: 'Divider',
+  },
+  {
+    field: 'fireWarn1CoRzl',
+    label: '预警等级(Ⅰ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO日增率(%) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1CoRzl', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn1CoZf',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO增幅(μ) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1CoZf', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn1CoNd',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO浓度(ppm) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1CoNd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn1O2',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  O2(%) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1O2', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn1T',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  T-μT(℃) <',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1T', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn1Co2',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO2(%) <=',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn1Co2', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn2CoRzl',
+    label: '预警等级(Ⅱ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< CO日增率(%) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn2CoRzlStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn2CoRzlEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn2CoZf',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< CO增幅(μ) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn2CoZfStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn2CoZfEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn2CoNd',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< CO浓度(ppm) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn2CoNdStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn2CoNdEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn2T',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '<= T-μT(℃) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn2TStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn2TEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn2Co2',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO2(%) <',
+    colProps: { span: 12, style: 'margin-right: 1px' },
+  },
+  {
+    field: 'fireWarn3CoRzl',
+    label: '预警等级(Ⅲ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< CO日增率(%) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn3CoRzlStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn3CoRzlEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn3CoZf',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< CO增幅(μ) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn3CoZfStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn3CoZfEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn3CoNd',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '<= CO浓度(ppm) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn3CoNdStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn3CoNdEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn3T',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputRangeNumber',
+    /** 借用 */
+    groupName: '< T-μT(℃) <=',
+    colProps: { span: 12 },
+  },
+  { field: 'fireWarn3TStart', label: '', show: false, component: 'Input' },
+  { field: 'fireWarn3TEnd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn3Co2',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  CO2(%) <',
+    colProps: { span: 12, style: 'margin-right: 1px' },
+  },
+  {
+    field: 'fireWarn4CoRzl',
+    label: '预警等级(Ⅳ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '< CO日增率(%)  ',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn4CoRzl', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn4CoZf',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '< CO增幅(μ)  ',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn4CoZf', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn4CoNd',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '< CO浓度(ppm)  ',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn4CoNd', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn4T',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '< T-μT(℃)  ',
+    colProps: { span: 12 },
+  },
+  // { field: 'fireWarn4T', label: '', show: false, component: 'Input' },
+  {
+    field: 'fireWarn4Co2',
+    label: ' ',
+    suffix: '',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '<= CO2(%)  ',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'm3',
+    label: '火区密闭启封判定模型',
+    // labelWidth: 110,
+    component: 'Divider',
+  },
+  // { field: 'ycWarn4', label: '', show: false, component: 'Input' },
+  {
+    field: 'openWarn1',
+    label: '预警等级(Ⅰ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  空气温度(℃) <',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'openWarn2',
+    label: '预警等级(Ⅱ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  氧气浓度(%) <',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'openWarn3',
+    label: '预警等级(Ⅲ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  不含甲烷、乙烯、CO浓度(%) <',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'openWarn4',
+    label: '预警等级(Ⅳ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputGreaterNumber',
+    /** 借用 */
+    groupName: '  出水温度(℃) <',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'openWarn5',
+    label: '预警等级(Ⅴ):',
+    labelWidth: 118,
+    component: 'Input',
+    slot: 'InputLowerNumber',
+    /** 借用 */
+    groupName: '< 指标稳定时长(天)  ',
+    colProps: { span: 12 },
+  },
+];
+export const schemasGoafLimit: FormSchema[] = [
+  {
+    label: 'ID',
+    field: 'id',
+    show: false,
+    component: 'Input',
+  },
+  {
+    label: '监测值:',
+    field: 'alarmField',
+    component: 'Select',
+    componentProps: {
+      options: goafAlarmOptions,
+    },
+  },
+  {
+    label: '下限值:',
+    field: 'lowerLimit',
+    component: 'InputNumber',
+  },
+  {
+    label: '上限值:',
+    field: 'upperLimit',
+    component: 'InputNumber',
+  },
+];

+ 125 - 0
src/views/system/algorithm/components/DynamicForm.vue

@@ -0,0 +1,125 @@
+<script lang="ts">
+  // 在 setup 中使用
+  import { h, defineComponent } from 'vue';
+  import BasicForm from './BasicForm.vue'; // 假设 BasicForm 组件的路径
+  import { FormItem, InputGroup, InputNumber, Input } from 'ant-design-vue';
+
+  export default defineComponent({
+    name: 'MyFormComponent',
+
+    setup() {
+      return () =>
+        h(
+          BasicForm,
+          {
+            register: 'registerForm',
+          },
+          {
+            // 使用 slots 而不是 scopedSlots(Vue 3)
+            InputRangeNumber: ({ model, field, schema }) => {
+              return h(FormItem, null, {
+                default: () =>
+                  h(InputGroup, null, {
+                    default: () => [
+                      h(InputNumber, {
+                        modelValue: model[`${field}Start`],
+                        'onUpdate:modelValue': (val) => (model[`${field}Start`] = val),
+                        style: { width: 'calc(50% - 100px)' },
+                        placeholder: '-',
+                      }),
+                      h(Input, {
+                        value: schema.groupName,
+                        style: {
+                          width: '200px',
+                          borderLeft: '0',
+                          pointerEvents: 'none',
+                          color: 'inherit',
+                        },
+                        disabled: true,
+                      }),
+                      h(InputNumber, {
+                        modelValue: model[`${field}End`],
+                        'onUpdate:modelValue': (val) => (model[`${field}End`] = val),
+                        style: {
+                          width: 'calc(50% - 100px)',
+                          borderLeft: '0',
+                        },
+                        placeholder: '-',
+                      }),
+                    ],
+                  }),
+              });
+            },
+
+            InputGreaterNumber: ({ model, field, schema }) => {
+              return h(FormItem, null, {
+                default: () =>
+                  h(InputGroup, null, {
+                    default: () => [
+                      h(InputNumber, {
+                        style: { width: 'calc(50% - 100px)' },
+                        placeholder: '-',
+                        disabled: true,
+                      }),
+                      h(Input, {
+                        value: schema.groupName,
+                        style: {
+                          width: '200px',
+                          borderLeft: '0',
+                          pointerEvents: 'none',
+                          color: 'inherit',
+                        },
+                        disabled: true,
+                      }),
+                      h(InputNumber, {
+                        modelValue: model[field],
+                        'onUpdate:modelValue': (val) => (model[field] = val),
+                        style: {
+                          width: 'calc(50% - 100px)',
+                          borderLeft: '0',
+                        },
+                        placeholder: '-',
+                      }),
+                    ],
+                  }),
+              });
+            },
+
+            InputLowerNumber: ({ model, field, schema }) => {
+              return h(FormItem, null, {
+                default: () =>
+                  h(InputGroup, null, {
+                    default: () => [
+                      h(InputNumber, {
+                        modelValue: model[field],
+                        'onUpdate:modelValue': (val) => (model[field] = val),
+                        style: { width: 'calc(50% - 100px)' },
+                        placeholder: '-',
+                      }),
+                      h(Input, {
+                        value: schema.groupName,
+                        style: {
+                          width: '200px',
+                          borderLeft: '0',
+                          pointerEvents: 'none',
+                          color: 'inherit',
+                        },
+                        disabled: true,
+                      }),
+                      h(InputNumber, {
+                        style: {
+                          width: 'calc(50% - 100px)',
+                          borderLeft: '0',
+                        },
+                        placeholder: '-',
+                        disabled: true,
+                      }),
+                    ],
+                  }),
+              });
+            },
+          }
+        );
+    },
+  });
+</script>

+ 105 - 0
src/views/system/algorithm/components/EnfMineTree.vue

@@ -0,0 +1,105 @@
+<template>
+  <a-card :bordered="false" :body-style="{ background: backgroundColor }">
+    <a-spin :spinning="loading">
+      <a-input-search v-if="showSearch" class="mb-10px" placeholder="按名称搜索…" @search="onSearch" allowClear />
+      <!--组织机构树-->
+      <template v-if="treeData.length > 0">
+        <a-tree
+          :style="{ background: backgroundColor }"
+          showLine
+          :clickRowToExpand="false"
+          :treeData="treeData"
+          :selectedKeys="selectedKeys"
+          v-model:expandedKeys="expandedKeys"
+          @select="onSelect"
+        >
+        </a-tree>
+
+        <!-- style="overflow-y: auto; height: calc(100vh - 310px)" -->
+        <!-- :selectedKeys="selectedKeys" -->
+        <!-- v-model:expandedKeys="expandedKeys" -->
+      </template>
+      <a-empty v-else description="暂无数据" />
+    </a-spin>
+  </a-card>
+</template>
+
+<script lang="ts" setup>
+  import { onMounted, ref } from 'vue';
+  import { getMineTree } from '../algorithm.api';
+
+  // 定义props
+  defineProps({
+    // 是否显示搜索框
+    showSearch: {
+      type: Boolean,
+      default: true,
+    },
+    // 背景色
+    backgroundColor: {
+      type: String,
+      default: 'inherit',
+    },
+  });
+  const emit = defineEmits(['select', 'rootTreeData']);
+
+  const loading = ref<boolean>(false);
+  // 树列表数据
+  const treeData = ref<any[]>([]);
+  // 当前展开的项
+  const expandedKeys = ref<any[]>([]);
+  // 当前选中的项
+  const selectedKeys = ref<any[]>([]);
+
+  // 搜索关键字
+  const searchKeyword = ref('');
+
+  /**
+   * 设置当前选中的行
+   */
+  function setSelectedKey(key: string, data?: object) {
+    selectedKeys.value = [key];
+    if (data) {
+      emit('select', data);
+    }
+  }
+
+  // 搜索事件
+  async function onSearch(value: string = '') {
+    try {
+      loading.value = true;
+      treeData.value = [];
+      const { tree, firstLeafKey, firstParentKey } = await getMineTree({ searchValue: value });
+      if (Array.isArray(tree)) {
+        treeData.value = tree;
+      }
+      if (expandedKeys.value.length === 0) {
+        expandedKeys.value = [firstParentKey];
+      }
+      if (selectedKeys.value.length === 0) {
+        selectedKeys.value = [firstLeafKey];
+      }
+    } finally {
+      loading.value = false;
+    }
+    searchKeyword.value = value;
+  }
+
+  // 树选择事件
+  function onSelect(selKeys, event) {
+    if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
+      setSelectedKey(selKeys[0], event.selectedNodes[0]);
+    } else {
+      // 这样可以防止用户取消选择
+      setSelectedKey(selectedKeys.value[0]);
+    }
+  }
+
+  onMounted(() => {
+    onSearch();
+  });
+
+  defineExpose({
+    onSearch,
+  });
+</script>

+ 110 - 0
src/views/system/algorithm/hooks/operations.ts

@@ -0,0 +1,110 @@
+export function useOperations() {
+  const formPropsMap = new Map([
+    [
+      'coal',
+      {
+        schemas: schemasCoalAlarm,
+        submitFunc: (res) => (res.id ? updateCoalSeamAlarmRule(res) : addCoalSeamAlarmRule(res)),
+        fetchRecord({ id, mineCode }) {
+          return getCoalSeamAlarmRule({ coalSeamId: id, mineCode: mineCode }).then((r) => r[r.length - 1]);
+        },
+      },
+    ],
+    [
+      'goaf',
+      {
+        schemas: schemasGoafLimit,
+        submitFunc: (res) => (res.id ? updateGoafDataLimit(res) : addGoafDataLimit(res)),
+        fetchRecord({ id }) {
+          return getGoafDataLimit({ goafId: id }).then((r) => r[r.length - 1]);
+        },
+      },
+    ],
+  ]);
+  const modalPropsMap = new Map<string, Partial<ModalProps>>([
+    ['coal', { title: '预警参数设置', visible: true, loading: true }],
+    ['goaf', { title: '超限预警设置', visible: true, loading: true }],
+  ]);
+  const submitResolver = ref<(res: any) => Promise<void>>();
+  // 点击编辑后,获取对应的表单和弹窗配置
+  async function handleEdit(record, sign: string) {
+    if (!modalPropsMap.has(sign)) return;
+    if (!formPropsMap.has(sign)) return;
+    setModalProps(modalPropsMap.get(sign) as ModalProps);
+    const { schemas, fetchRecord, submitFunc } = formPropsMap.get(sign)!;
+    const res = await fetchRecord(record);
+    console.log('debug before resetSchema');
+    await resetSchema(schemas);
+    console.log('debug after resetSchema');
+
+    await nextTick();
+
+    console.log('debug before setFieldsValue');
+    if (res.id) {
+      await setFieldsValue(res);
+    } else {
+      await resetFields();
+    }
+    console.log('debug after setFieldsValue');
+
+    await nextTick();
+
+    console.log('debug before setFormProps');
+    // 不要使用setFormProps因为它会错误的触发submit方法
+    // await setFormProps({ submitFunc });
+    submitResolver.value = submitFunc;
+    console.log('debug after setFormProps');
+
+    setModalProps({ loading: false });
+    // await setFormProps({ model: res });
+  }
+  async function handleAdd(record, sign: string) {
+    if (!modalPropsMap.has(sign)) return;
+    if (!formPropsMap.has(sign)) return;
+    setModalProps(modalPropsMap.get(sign) as ModalProps);
+    const { schemas, submitFunc } = formPropsMap.get(sign)!;
+    await resetSchema(schemas);
+
+    await nextTick();
+
+    await resetFields();
+
+    await nextTick();
+
+    // 不要使用setFormProps因为它会错误的触发submit方法
+    // await setFormProps({ submitFunc });
+    submitResolver.value = submitFunc;
+
+    setModalProps({ loading: false });
+    // await setFormProps({ model: res });
+  }
+
+  const deletionPropsMap = new Map<string, { api: (id: string) => Promise<void> }>([
+    ['coal', { api: (id) => deleteCoalSeamAlarmRule({ id }) }],
+    ['goaf', { api: (id) => deleteGoafDataLimit({ id }) }],
+  ]);
+
+  function handleDelete(record, sign: string) {
+    if (!deletionPropsMap.has(sign)) return;
+    deletionPropsMap.get(sign)?.api(record.id);
+  }
+
+  const [registerModal, { setModalProps, closeModal }] = useModal();
+  const [registerForm, { setProps: setFormProps, resetFields, setFieldsValue, validate, submit, resetSchema }] = useForm({
+    model: {},
+    schemas: schemasCoalAlarm,
+    showResetButton: false,
+    showSubmitButton: false,
+    colon: false,
+    compact: true,
+  });
+
+  function handleSubmit() {
+    return validate().then((res) => {
+      submitResolver && submitResolver(res);
+    });
+    submit().then(() => {
+      closeModal();
+    });
+  }
+}

+ 219 - 104
src/views/system/algorithm/index.vue

@@ -1,153 +1,268 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
-  <div class="algorithm-model">
+  <Flex class="algorithm-model h-full">
+    <!-- <EnfMineTree class="flex-grow-1" @select="onTreeSelect" /> -->
     <!--引用表格-->
-    <BasicTable :ellipsis="true" @register="registerTable">
-      <template #tableTitle>
-        <div class="table-title-bar">
-          <a-tabs v-model:activeKey="activeKey" @change="handleTabChange" size="small">
-            <a-tab-pane tab="预警参数" key="coal"></a-tab-pane>
-            <a-tab-pane tab="超限预警" key="goaf"></a-tab-pane>
-          </a-tabs>
-          <!-- <span class="export-btn" v-if="searchInfo.logType == 2">
-            <a-tooltip>
-              <template #title>导出</template>
-              <a-button type="text" preIcon="ant-design:download-outlined" shape="circle" @click="onExportXls" />
-            </a-tooltip>
-          </span> -->
-        </div>
+    <BasicTable class="flex-grow-1" :expandedRowKeys="expandedRowKeys" :ellipsis="true" @register="registerTable">
+      <template #formTitle>
+        <!-- <a-button type="primary" @click="handleEdit({}, 'coal')">
+          <PlusOutlined />
+          新增
+        </a-button> -->
+        矿区CODE级联选择器占位符
+      </template>
+      <template #expandedRowRender>
+        <BasicTable @register="registerInnerTable">
+          <template #action="{ record }">
+            <button @click="handleEdit({ goafId: record.id }, 'goaf')" class="action-btn">
+              <SvgIcon name="edit" />
+            </button>
+            <button @click="handleAdd({ goafId: record.id }, 'goaf')" class="action-btn ml-1">
+              <PlusOutlined />
+            </button>
+            <a-popconfirm title="确认删除?" @confirm="handleDelete(record, 'coal')">
+              <button @click="handleDelete(record, 'goaf')" class="action-btn ml-1">
+                <SvgIcon name="delete" />
+              </button>
+            </a-popconfirm>
+          </template>
+        </BasicTable>
       </template>
 
       <template #action="{ record }">
-        <TableAction
-          :actions="[
-            {
-              label: '编辑',
-              onClick: () => handleEdit(record),
-            },
-            {
-              label: '删除',
-              onClick: () => {},
-            },
-          ]"
-        />
+        <button @click="handleEdit({ coalSeamId: record.id, mineCode: record.mineCode }, 'coal')" class="action-btn">
+          <SvgIcon name="edit" />
+        </button>
+        <button @click="handleAdd({ coalSeamId: record.id, mineCode: record.mineCode }, 'coal')" class="action-btn ml-1">
+          <PlusOutlined />
+        </button>
+        <a-popconfirm title="确认删除?" @confirm="handleDelete(record, 'coal')">
+          <button class="action-btn ml-1">
+            <SvgIcon name="delete" />
+          </button>
+        </a-popconfirm>
       </template>
     </BasicTable>
-    <BasicModal @register="registerModal" @ok="handleSubmit" title="预警参数设置">
-      <BasicForm @register="registerForm" />
-    </BasicModal>
-    <!-- <BasicModal @register="registerCoalModal" @ok="handleCoalEdit" title="预警参数设置">
-      <BasicForm @register="registerCoalForm" />
-    </BasicModal>
-    <BasicModal @register="registerGoafModal" @ok="handleGoafEdit" title="超限预警设置">
-      <BasicForm @register="registerGoafForm" />
-    </BasicModal> -->
-  </div>
+  </Flex>
+  <BasicModal width="60%" :height="600" @register="registerModal" @ok="handleSubmit" title="预警参数设置">
+    <BasicForm @register="registerForm">
+      <template #InputRangeNumber="{ model, field, schema }">
+        <a-form-item>
+          <a-input-group>
+            <a-input-number v-model:value="model[`${field}Start`]" style="width: calc(50% - 100px)" placeholder="-" />
+            <a-input style="width: 200px; border-left: 0; pointer-events: none; color: inherit" :value="schema.groupName" disabled />
+            <a-input-number v-model:value="model[`${field}End`]" style="width: calc(50% - 100px); border-left: 0" placeholder="-" />
+          </a-input-group>
+        </a-form-item>
+      </template>
+      <template #InputGreaterNumber="{ model, field, schema }">
+        <a-form-item>
+          <a-input-group>
+            <a-input-number style="width: calc(50% - 100px)" placeholder="-" disabled />
+            <a-input style="width: 200px; border-left: 0; pointer-events: none; color: inherit" :value="schema.groupName" disabled />
+            <a-input-number v-model:value="model[field]" style="width: calc(50% - 100px); border-left: 0" placeholder="-" />
+          </a-input-group>
+        </a-form-item>
+      </template>
+      <template #InputLowerNumber="{ model, field, schema }">
+        <a-form-item>
+          <a-input-group>
+            <a-input-number v-model:value="model[field]" style="width: calc(50% - 100px)" placeholder="-" />
+            <a-input style="width: 200px; border-left: 0; pointer-events: none; color: inherit" :value="schema.groupName" disabled />
+            <a-input-number style="width: calc(50% - 100px); border-left: 0" placeholder="-" disabled />
+          </a-input-group>
+        </a-form-item>
+      </template>
+    </BasicForm>
+  </BasicModal>
 </template>
 
 <script lang="ts" setup>
-  import { nextTick, provide, ref, unref } from 'vue';
+  import { nextTick, provide, ref } from 'vue';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import DepartLeftTree from './components/DepartLeftTree.vue';
+  // import EnfMineTree from './components/EnfMineTree.vue';
   import { useModal, BasicModal, ModalProps } from '/@/components/Modal';
-  import { useForm, BasicForm, FormProps } from '/@/components/Form';
-  import { BasicTable, BasicTableProps, FormSchema, TableAction } from '/@/components/Table';
+  import { useForm, BasicForm } from '/@/components/Form';
+  import { BasicTable, useTable } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { columnsCoalAlarm, columnsGoafLimit, schemasCoalAlarm, schemasGoafLimit, searchFormSchema } from './algorithm.data';
-  import { list, positionList } from './algorithm.api';
-  import { getCacheByDynKey } from '@/utils/auth';
-  import { JEECG_CHAT_UID } from '@/enums/cacheEnum';
+  import {
+    addCoalSeamAlarmRule,
+    deleteCoalSeamAlarmRule,
+    deleteGoafDataLimit,
+    getCoalSeam,
+    getGoafList,
+    updateGoafDataLimit,
+    addGoafDataLimit,
+    updateCoalSeamAlarmRule,
+    getCoalSeamAlarmRule,
+    getGoafDataLimit,
+  } from './algorithm.api';
+  import { Flex } from 'ant-design-vue';
+  import { PlusOutlined } from '@ant-design/icons-vue';
+  import { last } from 'lodash-es';
+
+  import { SvgIcon } from '/@/components/Icon';
 
   const { prefixCls } = useDesign('algorithm-list');
   provide('prefixCls', prefixCls);
 
-  const activeKey = ref('coal');
-  const tablePropsMap = new Map<string, Partial<BasicTableProps>>([
-    ['coal', { api: () => Promise.resolve([{ id: 1, name1: '测试TAB1' }]), columns: columnsCoalAlarm, rowKey: 'id' }],
-    ['goaf', { api: () => Promise.resolve([{ id: 2, name2: '测试TAB1' }]), columns: columnsGoafLimit, rowKey: 'id' }],
-  ]);
-  const modalPropsMap = new Map<string, Partial<ModalProps>>([
-    ['coal', { title: '预警参数设置', visible: true }],
-    ['goaf', { title: '超限预警设置', visible: true }],
-  ]);
-  const formPropsMap = new Map<string, Partial<FormProps>>([
-    ['coal', { schemas: schemasCoalAlarm }],
-    ['goaf', { schemas: schemasGoafLimit }],
-  ]);
-  // const rowEditMap;
-
-  // 给子组件定义一个ref变量
-  const leftTree = ref();
-
-  // 当前选中的部门code
-  const orgCode = ref('');
-  const positionInfo = ref({});
+  // 当前选中的矿区code
+  const mineCode = ref('');
+  // 为了更好的控制展开逻辑
+  const expandedRowKeys = ref<string[]>([]);
 
   // 列表页面公共参数、方法
   const { tableContext } = useListPage({
     tableProps: {
-      ...tablePropsMap.get('coal'),
+      api: getCoalSeam,
+      columns: columnsCoalAlarm,
+      rowKey: 'id',
       showIndexColumn: true,
       formConfig: {
+        showResetButton: false,
         schemas: searchFormSchema,
       },
-      // canResize: false,
-      // showTableSetting: false,
-      // 请求之前对参数做处理
-      beforeFetch(params) {
-        params.orgCode = orgCode.value;
+      beforeFetch(p) {
+        p.mineCode = mineCode.value;
+      },
+      onExpand(expanded, record) {
+        // 展开最多一行
+        if (expanded) {
+          expandedRowKeys.value = [record.id];
+          nextTick(reloadInner);
+        } else {
+          expandedRowKeys.value = [];
+        }
       },
     },
   });
   //注册table数据
-  const [registerTable, { reload, setProps }] = tableContext;
+  const [registerTable] = tableContext;
+  const [registerInnerTable, { reload: reloadInner }] = useTable({
+    api: getGoafList,
+    columns: columnsGoafLimit,
+    rowKey: 'id',
+    pagination: false,
+    showActionColumn: true,
+    actionColumn: {
+      title: '操作',
+      dataIndex: 'action',
+      slots: { customRender: 'action' },
+    },
+    beforeFetch(p) {
+      p.mineCode = mineCode.value;
+      p.coalSeamId = expandedRowKeys.value[0];
+    },
+  });
+
+  const formPropsMap = new Map([
+    [
+      'coal',
+      {
+        schemas: schemasCoalAlarm,
+        submitFunc: (res) => (res.id ? updateCoalSeamAlarmRule(res) : addCoalSeamAlarmRule(res)),
+        fetchRecord: (params) => getCoalSeamAlarmRule(params).then((r) => last(r)),
+      },
+    ],
+    [
+      'goaf',
+      {
+        schemas: schemasGoafLimit,
+        submitFunc: (res) => (res.id ? updateGoafDataLimit(res) : addGoafDataLimit(res)),
+        fetchRecord: (params) => getGoafDataLimit(params).then((r) => last(r)),
+      },
+    ],
+  ]);
+  const modalPropsMap = new Map<string, Partial<ModalProps>>([
+    ['coal', { title: '预警参数设置', visible: true, loading: true }],
+    ['goaf', { title: '超限预警设置', visible: true, loading: true }],
+  ]);
+  const submitResolver = ref<(res: any) => Promise<void>>();
+  // 点击编辑后,获取对应的表单和弹窗配置
+  async function handleEdit(record, sign: string) {
+    if (!modalPropsMap.has(sign)) return;
+    if (!formPropsMap.has(sign)) return;
+    setModalProps(modalPropsMap.get(sign) as ModalProps);
+    const { schemas, fetchRecord, submitFunc } = formPropsMap.get(sign)!;
+    const res = await fetchRecord(record);
+    await resetSchema(schemas);
+
+    await nextTick();
+
+    await setFieldsValue(res);
+
+    await nextTick();
 
-  // 左侧树选择后触发
-  function onTreeSelect(data) {
-    orgCode.value = data.orgCode;
-    reload();
+    // 不要使用setFormProps因为它会错误的触发submit方法
+    // await setFormProps({ submitFunc });
+    submitResolver.value = (res) => submitFunc(Object.assign(res, record));
+
+    setModalProps({ loading: false });
   }
+  async function handleAdd(record, sign: string) {
+    if (!modalPropsMap.has(sign)) return;
+    if (!formPropsMap.has(sign)) return;
+    setModalProps(modalPropsMap.get(sign) as ModalProps);
+    const { schemas, submitFunc } = formPropsMap.get(sign)!;
+    await resetSchema(schemas);
+
+    await nextTick();
+
+    await resetFields();
+
+    await nextTick();
+
+    // 不要使用setFormProps因为它会错误的触发submit方法
+    // await setFormProps({ submitFunc });
+    submitResolver.value = (res) => submitFunc(Object.assign(res, record));
 
-  function handleTabChange(key) {
-    activeKey.value = key;
-    if (!tablePropsMap.has(key)) return;
-    setProps(tablePropsMap.get(key) as BasicTableProps);
-    reload();
+    setModalProps({ loading: false });
   }
 
-  function handleEdit(record) {
-    if (!modalPropsMap.has(activeKey.value)) return;
-    if (!formPropsMap.has(activeKey.value)) return;
-    setModalProps(modalPropsMap.get(activeKey.value) as ModalProps);
-    nextTick(() => {
-      setFormProps({
-        model: record,
-        schemas: formPropsMap.get(activeKey.value) as FormSchema[],
-      });
-    });
+  const deletionPropsMap = new Map<string, { api: (id: string) => Promise<void> }>([
+    ['coal', { api: (id) => deleteCoalSeamAlarmRule({ id }) }],
+    ['goaf', { api: (id) => deleteGoafDataLimit({ id }) }],
+  ]);
+
+  function handleDelete(record, sign: string) {
+    if (!deletionPropsMap.has(sign)) return;
+    deletionPropsMap.get(sign)?.api(record.id);
   }
 
   const [registerModal, { setModalProps }] = useModal();
-  const [registerForm, { setProps: setFormProps }] = useForm({
+  const [registerForm, { resetFields, setFieldsValue, validate, resetSchema }] = useForm({
+    model: {},
     schemas: schemasCoalAlarm,
     showResetButton: false,
     showSubmitButton: false,
+    colon: false,
+    compact: true,
   });
-  // const [registerCoalModal, coalModalContext] = useModal();
-  // const [registerCoalForm, coalFormContext] = useForm({
-  //   schemas: schemasCoalAlarm,
-  // });
-
-  // function handleCoalEdit() {}
-
-  // const [registerGoafModal, goafModalContext] = useModal();
-  // const [registerGoafForm, goafFormContext] = useForm({
-  //   schemas: schemasGoafLimit,
-  // });
 
-  function handleGoafEdit() {}
+  function handleSubmit() {
+    return validate().then((res) => {
+      submitResolver.value && submitResolver.value(res);
+    });
+  }
 </script>
 
 <style lang="less">
   @import './index.less';
+
+  .ant-input-group {
+    display: flex;
+    text-align: center;
+    .ant-input-number {
+      min-width: 100px;
+      &:first-child {
+        border-top-right-radius: 0;
+        border-bottom-right-radius: 0;
+      }
+      &:last-child {
+        border-top-left-radius: 0;
+        border-bottom-left-radius: 0;
+      }
+    }
+  }
 </style>