Parcourir la source

[Feat 0000] Monitor Site 监测站点

Discription

开发监测模块
开发监测-测点模块
开发测点相关页面
注册测点相关api

Associated Tasks

暂无

Assignee

houzekong
houzekong il y a 1 an
Parent
commit
92fc77067f

+ 80 - 0
src/api/sys/model/monitorModel.ts

@@ -0,0 +1,80 @@
+/** 基本的返回结构 */
+export interface AlgoResponse<T> {
+  code: number;
+  msg: string;
+  requestId: string;
+  data: T;
+}
+
+/** 测点树的节点 */
+export interface MonitorSiteTreeNode {
+  id: string;
+  parentId: string | null;
+  label: string;
+  disasterType: any;
+  disasterName: any;
+  sensorType: any;
+  sensorTypeName: any;
+  sequence: number;
+  leafNode: number;
+  display: number;
+  parentNode?: {
+    id: string;
+    label: string;
+  };
+  x?: number;
+  y?: number;
+  z?: number;
+  sensorStatus?: number;
+  children: MonitorSiteTreeNode[];
+}
+
+/** 测点树请求参数 */
+export interface MonitorSiteTreeParams {
+  mineCode?: string;
+}
+
+/** 测点详情 */
+export interface MonitorSite {
+  mineCode: string;
+  disasterType: number;
+  sensorCode: string;
+  sensorPosition: string;
+  sensorName: string;
+  sensorType: any;
+  metricRows: {
+    orderId: number;
+    metricName: string;
+    metricValue: string;
+    unit: string;
+    status: any;
+    statusDesc: any;
+    sensorType: any;
+  }[];
+}
+
+/** 测点详情请求参数 */
+export interface MonitorSiteParams {
+  disasterType: number;
+  mineCode: string;
+  sensorCode: string;
+  sensorType: string;
+  parentNodeId: string;
+}
+
+/** 测点操作相应 */
+export interface MonitorSiteOperation {
+  clickType: string;
+  /** 类型未知 */
+  id: any;
+}
+
+export interface MonitorSiteOperationParams {
+  clickType: string;
+  id?: string;
+  data?: any;
+  type?: string;
+  sensorType?: string;
+  sensorStatus?: string;
+  from: 'tank';
+}

+ 68 - 0
src/api/sys/monitor.ts

@@ -0,0 +1,68 @@
+// 监测相关的api
+import { defHttp } from '@/utils/http/axios';
+
+import { ErrorMessageMode } from '#/axios';
+import {
+  MonitorSite,
+  MonitorSiteOperation,
+  MonitorSiteOperationParams,
+  MonitorSiteParams,
+  MonitorSiteTreeNode,
+  MonitorSiteTreeParams,
+  AlgoResponse,
+} from './model/monitorModel';
+
+enum Api {
+  getMonitorSiteTree = '/algoapi/gis2d/getDisasterTpTree',
+  getMonitorSite = 'algoapi/water/monitor/sensortips/details',
+}
+
+/**
+ * 获取测点树数据
+ */
+export function getMonitorSiteTree(
+  params: MonitorSiteTreeParams,
+  mode: ErrorMessageMode = 'message',
+) {
+  return defHttp.post<AlgoResponse<MonitorSiteTreeNode>>(
+    {
+      url: Api.getMonitorSiteTree,
+      params,
+    },
+    {
+      errorMessageMode: mode,
+    },
+  );
+}
+
+/**
+ * 获取测点详情数据
+ */
+export function getMonitorSite(params: MonitorSiteParams, mode: ErrorMessageMode = 'message') {
+  return defHttp.post<AlgoResponse<MonitorSite>>(
+    {
+      url: Api.getMonitorSite,
+      params,
+    },
+    {
+      errorMessageMode: mode,
+    },
+  );
+}
+
+/** 测点操作内嵌的iframe地址 */
+export const monitorSiteOperationUrl = 'http://39.105.183.243:18224/valkyrja/';
+
+/** 发送测点操作指令 */
+export function postMonitorOperation(params: MonitorSiteOperationParams) {
+  window.postMessage(JSON.stringify(params), '*');
+}
+
+export function handleMonitorOperation(
+  raw: MessageEvent<string>,
+  callback: (data: MonitorSiteOperation) => void,
+) {
+  if (raw.isTrusted && raw.origin === monitorSiteOperationUrl && raw.data) {
+    callback(JSON.parse(raw.data));
+  }
+}

+ 11 - 9
src/locales/lang/zh-CN/routes/vent.json

@@ -2,24 +2,26 @@
   "fire": {
     "fire": "火灾",
     "home": "火灾预警监测",
-    "fireCompositeWarn":"工作面监测预警分析",
-    "Atomizing":"工作面监测预警分析(1)",
+    "fireCompositeWarn": "工作面监测预警分析",
+    "Atomizing": "工作面监测预警分析(1)",
     "goaf": "密闭采空区监测预警分析",
     "goaflist": "密闭采空区监测预警分析(1)",
     "fireMonitor": "变电硐室防灭火监控预警系统",
-    "workFace":"工作面监测预警分析",
+    "workFace": "工作面监测预警分析",
     "beltConveyor": "带试运输机放灭火监测系统",
-    "fireDistributionPoint":"安全监控系统预警分析",
+    "fireDistributionPoint": "安全监控系统预警分析",
     "nitrogen": "智能注氮系统监测分析",
     "grout": "智能灌浆系统监测分析"
-   
   },
   "dust": {
     "dust": "粉尘",
     "home": "粉尘预警监测",
-    "dustWarnAnalysis":"粉尘监测预警分析",
-    "dustWarnAnalysislist":"粉尘监测预警分析(1)",
-    "dustAtomizing":"智能喷雾降尘装置"
-  
+    "dustWarnAnalysis": "粉尘监测预警分析",
+    "dustWarnAnalysislist": "粉尘监测预警分析(1)",
+    "dustAtomizing": "智能喷雾降尘装置"
+  },
+  "monitor": {
+    "monitor": "监测",
+    "monitorSite": "测点"
   }
 }

+ 28 - 0
src/router/routes/vent/monitor.ts

@@ -0,0 +1,28 @@
+// 测点页面路由
+import type { AppRouteModule } from '@/router/types';
+import { LAYOUT } from '@/router/constant';
+import { t } from '@/hooks/web/useI18n';
+
+const monitor: AppRouteModule = {
+  path: '/monitor',
+  name: 'Monitor',
+  component: LAYOUT,
+  redirect: '/monitor/index',
+  meta: {
+    orderNo: 400,
+    icon: 'ion:circle-outline',
+    title: t('routes.vent.monitor.monitor'),
+  },
+  children: [
+    {
+      path: 'monitor-site',
+      name: 'monitorSite',
+      meta: {
+        title: t('routes.vent.monitor.monitorSite'),
+      },
+      component: () => import('@/views/vent/monitor/site/index.vue'),
+    },
+  ],
+};
+
+export default monitor;

+ 0 - 0
src/views/vent/monitor/index.vue


+ 101 - 0
src/views/vent/monitor/site/components/siteFilter.vue

@@ -0,0 +1,101 @@
+<template>
+  <!-- 测点列表过滤器 -->
+  <Form :model="formData" layout="vertical" :wrapperCol="{ span: 24 }">
+    <FormItem>
+      <Input
+        v-model:value="formData.keyword"
+        placeholder="输入关键词,回车以搜索"
+        @press-enter="submit"
+      />
+    </FormItem>
+    <FormItem>
+      <CheckableTag
+        v-for="tag in tags"
+        :key="tag.value"
+        class="ml-2px mr-2px"
+        :style="{ color: tag.color }"
+        :checked="formData.tag === tag.value"
+        @change="changeHandler(tag.value)"
+      >
+        {{ tag.label }}
+      </CheckableTag>
+    </FormItem>
+  </Form>
+</template>
+<script setup lang="ts">
+  import { MonitorSiteTreeParams } from '@/api/sys/model/monitorModel';
+  import { Form, FormItem, Input, CheckableTag } from 'ant-design-vue';
+  import { defineExpose, ref, defineEmits } from 'vue';
+
+  // props & emits
+  const emit = defineEmits<{
+    submit: [value: MonitorSiteTreeParams];
+  }>();
+
+  // 配置项
+  const defaltTag = '0';
+  const tags = [
+    {
+      label: '全部',
+      color: 'white',
+      value: defaltTag,
+    },
+    {
+      label: '正常',
+      color: 'green',
+      value: 'normal',
+    },
+    {
+      label: '报警',
+      color: 'red',
+      value: 'warn',
+    },
+    {
+      label: '未知',
+      color: 'blue',
+      value: 'unknown',
+    },
+    {
+      label: '故障',
+      color: 'orange',
+      value: 'fault',
+    },
+    {
+      label: '未接入',
+      color: 'purple',
+      value: 'offline',
+    },
+    {
+      label: '失败',
+      color: 'gray',
+      value: 'fail',
+    },
+    {
+      label: '无效',
+      color: 'white',
+      value: 'none',
+    },
+  ];
+
+  // 表单数据
+  const formData = ref({
+    tag: defaltTag,
+    keyword: '',
+  });
+
+  function submit() {
+    emit('submit', { mineCode: formData.value.tag });
+  }
+
+  function changeHandler(value: string) {
+    formData.value.tag = value;
+    submit();
+  }
+
+  defineExpose({
+    formData,
+    tags,
+    submit,
+    changeHandler,
+  });
+</script>

+ 133 - 0
src/views/vent/monitor/site/components/siteForm.vue

@@ -0,0 +1,133 @@
+<template>
+  <!-- 测点表单 -->
+  <BasicForm
+    :model="site"
+    :schemas="schemas"
+    :label-col="{ span: 8 }"
+    :wrapper-col="{ span: 16 }"
+    label-align="right"
+    :showActionButtonGroup="false"
+  >
+    <template #create-btn="{ model }">
+      <Button type="primary" :rounded="true" @click="createSite(model)">新建测点</Button>
+    </template>
+    <template #copy-btn="{ model }">
+      <Button type="primary" :rounded="true" @click="copySite(model)">复制测点</Button>
+    </template>
+    <template #edit-btn="{ model }">
+      <Button type="primary" :rounded="true" @click="editSite(model)">编辑测点</Button>
+    </template>
+  </BasicForm>
+</template>
+<script setup lang="ts">
+  import { MonitorSite, MonitorSiteOperationParams } from '@/api/sys/model/monitorModel';
+  import { defineProps } from 'vue';
+  import { BasicForm, FormSchema } from '@/components/Form/index';
+  import { Button } from 'ant-design-vue';
+
+  // props & emits
+  defineProps<{
+    site?: MonitorSite;
+  }>();
+  const emit = defineEmits<{
+    submit: [value: MonitorSiteOperationParams];
+  }>();
+
+  // 生成输入类表单项的帮助函数
+  function generateInputSchema({
+    field,
+    label,
+    span = 5,
+    placeholder,
+    required = false,
+  }: {
+    field: string;
+    label: string;
+    span?: number;
+    placeholder?: string;
+    required?: boolean;
+  }): FormSchema {
+    return {
+      component: 'Input',
+      field,
+      label,
+      rules: [{ required }],
+      colProps: {
+        span,
+      },
+      componentProps: {
+        placehoder: placeholder || `请输入${label}`,
+      },
+    };
+  }
+
+  // 生成按钮类表单项的帮助函数,具体的按钮写在BasicForm对应色的slot下
+  function generateButtonSchema({ slot }: { slot: string }): FormSchema {
+    return {
+      field: slot,
+      slot,
+      component: 'Render',
+      colProps: {
+        span: 1,
+      },
+      componentProps: {
+        size: 'small',
+      },
+    };
+  }
+
+  // 表单配置项
+  const schemas: FormSchema[] = [
+    generateButtonSchema({ slot: 'create-btn' }),
+    generateInputSchema({ field: 'id', label: '测点编号', required: true }),
+    generateInputSchema({ field: 'sensorType', label: '测点类型', required: true }),
+    generateInputSchema({ field: 'sensorStatus', label: '测点使用状态', required: true }),
+    generateInputSchema({ field: 'pos', label: '所属位置', required: true }),
+    generateInputSchema({ field: 'x', label: 'X', required: true, span: 3 }),
+    generateButtonSchema({ slot: 'copy-btn' }),
+    generateInputSchema({ field: 'srouce', label: '系统来源', required: true }),
+    generateInputSchema({ field: 'data_type', label: '数据类型', required: true }),
+    generateInputSchema({ field: 'nuit', label: '单位', required: true }),
+    generateInputSchema({ field: 'distance', label: '巷道距离' }),
+    generateInputSchema({ field: 'y', label: 'Y', required: true, span: 3 }),
+    generateButtonSchema({ slot: 'edit-btn' }),
+    generateInputSchema({ field: 'max_messure', label: '高量程' }),
+    generateInputSchema({ field: 'min_messure', label: '低量程' }),
+    generateInputSchema({ field: 'warn_limit', label: '报警上限', required: true }),
+    generateInputSchema({ field: 'warn_deadline', label: '报警下限', required: true }),
+    generateInputSchema({ field: 'z', label: 'Z', span: 3 }),
+  ];
+
+  // 点击按钮后处理提交逻辑的方法
+  function createSite(model: MonitorSiteOperationParams) {
+    emit('submit', {
+      clickType: 'newPoin', // 点击类型  新建测点
+      sensorType: model.sensorType, // 水 0502、瓦斯0001、顶板1401等 多级菜单
+      sensorStatus: model.sensorStatus, // 状态
+      id: model.id, // 测点ID
+      from: 'tank',
+    });
+  }
+  function copySite(model: MonitorSiteOperationParams) {
+    emit('submit', {
+      clickType: 'copyPoint', // 点击类型  新建测点
+      sensorType: model.sensorType, // 水 0502、瓦斯0001、顶板1401等 多级菜单
+      sensorStatus: model.sensorStatus, // 状态
+      id: model.id, // 测点ID
+      from: 'tank',
+    });
+  }
+  function editSite(model: MonitorSiteOperationParams) {
+    emit('submit', {
+      clickType: 'modifyPoint', // 点击类型  新建测点
+      sensorType: model.sensorType, // 水 0502、瓦斯0001、顶板1401等 多级菜单
+      sensorStatus: model.sensorStatus, // 状态
+      id: model.id, // 测点ID
+      from: 'tank',
+    });
+  }
+
+  defineExpose({
+    schemas,
+  });
+</script>

+ 70 - 0
src/views/vent/monitor/site/components/siteTree.vue

@@ -0,0 +1,70 @@
+<template>
+  <Tree
+    :tree-data="processedTreeData"
+    :expanded-keys="expandedKeys"
+    :default-expand-all="true"
+    :auto-expand-parent="true"
+    :virtual="false"
+    @select="selectHandler"
+  />
+</template>
+<script setup lang="ts">
+  import { ref, watch } from 'vue';
+  import { Tree } from 'ant-design-vue';
+  import { MonitorSiteTreeNode } from '@/api/sys/model/monitorModel';
+  import { TreeNode } from '../types/siteTree';
+
+  const props = defineProps<{ treeData?: MonitorSiteTreeNode }>();
+  const emit = defineEmits(['select']);
+
+  const processedTreeData = ref<TreeNode[]>([
+    {
+      title: '暂无数据',
+      key: '0',
+      children: [],
+    },
+  ]);
+  const expandedKeys = ref<string[]>([]);
+
+  // 递归处理树节点并格式化为需要的格式
+  function walk(node: MonitorSiteTreeNode, parentNodeKeys: string[]): TreeNode {
+    const res: TreeNode = {
+      title: node.label,
+      key: node.id,
+      children: [],
+      raw: node,
+    };
+
+    if (node.children) {
+      // 额外的,记录它们的key以将它们全部展开
+      parentNodeKeys.push(node.id);
+      node.children.forEach((child) => {
+        res.children.push(walk(child, parentNodeKeys));
+      });
+    }
+
+    return res;
+  }
+
+  function selectHandler(_, { selectedNodes }) {
+    emit('select', selectedNodes.raw);
+  }
+
+  // 更新逻辑在这里
+  watch(
+    () => props.treeData,
+    (v) => {
+      if (!v) return;
+      expandedKeys.value = [];
+      processedTreeData.value = [walk(v, expandedKeys.value)];
+    },
+    {
+      immediate: true,
+    },
+  );
+
+  defineExpose({
+    processedTreeData,
+    expandedKeys,
+  });
+</script>

+ 80 - 0
src/views/vent/monitor/site/index.vue

@@ -0,0 +1,80 @@
+<template>
+  <div class="monitor-site flex">
+    <!-- 左侧内容 -->
+    <div class="h-full flex-basis-385px p-10px">
+      <SiteFilter @submit="refreshTree" />
+      <SiteTree class="w-full h-50em scroll-auto" :tree-data="treeData" @select="focusOnSite" />
+    </div>
+    <!-- 右侧内容 主要内容 -->
+    <div class="h-full flex-grow-1 flex flex-col relative">
+      <IFrame class="w-full h-full" :frame-src="monitorSiteOperationUrl" @message="handleMessage" />
+      <SiteForm class="absolute w-full bg-#0960bd33 p-10px" :site="selectedSite" />
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+  import SiteTree from './components/siteTree.vue';
+  import SiteFilter from './components/siteFilter.vue';
+  import SiteForm from './components/siteForm.vue';
+  import { onMounted, ref } from 'vue';
+  import {
+    MonitorSiteTreeParams,
+    MonitorSiteTreeNode,
+    MonitorSite,
+  } from '@/api/sys/model/monitorModel';
+  import {
+    getMonitorSiteTree,
+    postMonitorOperation,
+    monitorSiteOperationUrl,
+    handleMonitorOperation,
+  } from '@/api/sys/monitor';
+  import IFrame from '@/views/sys/iframe/index.vue';
+
+  // 测点树相关,包含了刷新、请求的内容
+  const treeData = ref<MonitorSiteTreeNode>();
+  function refreshTree(params: MonitorSiteTreeParams) {
+    getMonitorSiteTree(params).then((r) => {
+      if (r.code === 200) treeData.value = r.data;
+    });
+  }
+
+  // 选中的测点
+  const selectedSite = ref<MonitorSite>();
+
+  // 测点操作(iframe)相关,包括下发指令及处理消息
+
+  // 聚焦到测点上
+  function focusOnSite(site: MonitorSite) {
+    selectedSite.value = site;
+    postMonitorOperation({
+      clickType: 'treePoint',
+      id: site.mineCode,
+      data: {},
+      type: 'treeSensor',
+      from: 'tank',
+    });
+  }
+  function handleMessage(msg: any) {
+    handleMonitorOperation(msg, (d) => {
+      console.log(d);
+    });
+  }
+
+  onMounted(() => {
+    refreshTree({});
+  });
+
+  defineExpose({
+    treeData,
+    monitorSiteOperationUrl,
+    selectedSite,
+    refreshTree,
+    focusOnSite,
+  });
+</script>
+<style scoped>
+  .monitor-site {
+    width: 100%;
+    margin-top: 88px;
+  }
+</style>

+ 6 - 0
src/views/vent/monitor/site/types/siteTree.ts

@@ -0,0 +1,6 @@
+export interface TreeNode {
+  title: string;
+  key: string;
+  children: TreeNode[];
+  [k: string]: unknown;
+}