Explorar el Código

[Feat 0000] 算法模型管理页面初步开发并对接完毕

houzekong hace 4 meses
padre
commit
5b66e22586

+ 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>