Ver Fonte

对接知识库、预案接口

Hwf há 9 meses atrás
pai
commit
a4a2ca3d81

+ 1 - 1
src/api/globalMap/index.ts

@@ -20,7 +20,7 @@ export const getMapProduct2 = (query: MapQuery) => {
 };
 
 // 动态接口请求
-export const getRescueMateria = (url) => {
+export const getRescueMateria = (url: string) => {
   return request({
     url: '/api/gateway/v1/' + url,
     method: 'get'

+ 20 - 1
src/api/routineCommandMap/index.ts

@@ -1,6 +1,8 @@
 import request from '@/utils/request';
+import { EmergencyVideoParams } from '@/api/routineCommandMap/type';
 
-export function getEmergencyVideoCata(data) {
+// 获取视频监控列表
+export function getEmergencyVideoCata(data: EmergencyVideoParams) {
   return request({
     url: '/api/gateway/v2/emergency_video_cata',
     method: 'post',
@@ -20,3 +22,20 @@ export function getVideoUrlById(id: string) {
   });
 }
 
+// 获取预案管理列表
+export function getEmergencyPlanList(params) {
+  return request({
+    url: '/api/emergency_plan/plan/list',
+    method: 'get',
+    params: params
+  });
+}
+
+// 获取应急知识库
+export function getReportsList(params) {
+  return request({
+    url: '/api/knowledge/select',
+    method: 'get',
+    params: params
+  });
+}

+ 4 - 0
src/api/routineCommandMap/type.ts

@@ -0,0 +1,4 @@
+export interface EmergencyVideoParams {
+  current: number;
+  size: number;
+}

+ 6 - 2
src/components/Map/index.vue

@@ -71,7 +71,7 @@ const { initMouseTool, drawGraphics, setColor, setDrawType, setGraphicsType, clo
 // 测距工具
 const { initRuler, isRanging, toggleRangingTool } = useRuler();
 // 初始化地图
-const { getAMap, getMap, switchMap, addMarker, clearMarker, getMarkers } = useAMap({
+const { getAMap, getMap, switchMap, addMarker, addSearchMarker, clearMarker, getMarkers } = useAMap({
   key: '30d3d8448efd68cb0b284549fd41adcf', // 申请好的Web端开发者Key,首次调用 load 时必填
   version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
   pitch: mapState.isThreeDimensional ? 45 : 0,
@@ -174,7 +174,11 @@ const switchThreeDimensional = () => {
   map.setPitch(pitch);
 };
 
-defineExpose({ addMarker, getMarkers, clearMarker, handleUndo });
+const setCenter = (item) => {
+  map.setCenter([item.longitude, item.latitude]);
+};
+
+defineExpose({ addMarker, addSearchMarker, setCenter, getMarkers, clearMarker, handleUndo });
 
 // 卸载事件
 onUnmounted(() => {

+ 21 - 2
src/hooks/AMap/useAMap.ts

@@ -49,8 +49,13 @@ export function useAMap(options) {
       nowLayer = satellite;
     }
   };
+  // 添加搜索的标记的
+  const addSearchMarker = (item) => {
+    map.setCenter([item.longitude, item.latitude]);
+    addMarker(item, true);
+  };
   // 实例化点标记
-  const addMarker = (item) => {
+  const addMarker = (item, isSearchItem?: boolean) => {
     if (!labelsLayer) {
       console.log(1);
       createLabelsLayer();
@@ -90,10 +95,23 @@ export function useAMap(options) {
     // 将LabelMarker添加到LabelsLayer图层
     labelsLayer.add(labelMarker);
     if (markers[item.parentId]) {
-      markers[item.parentId].push(labelMarker);
+      // if (isSearchItem) {
+        // const exists = markers[item.parentId].some(function (obj) {
+        //   return obj._opts?.text?.content === item.name;
+        // });
+        // debugger;
+        // if (!exists) {
+        //   markers[item.parentId].push(labelMarker);
+        // }
+      // } else {
+        markers[item.parentId].push(labelMarker);
+      // }
     } else {
       markers[item.parentId] = [labelMarker];
     }
+    labelMarker.on('click', () => {
+      // 使用 item.id 从 markerMap 中检索数据
+    });
   };
   // 创建LabelsLayer图层
   const createLabelsLayer = () => {
@@ -132,6 +150,7 @@ export function useAMap(options) {
     getMap,
     switchMap,
     addMarker,
+    addSearchMarker,
     clearMarker,
     getMarkers
   };

+ 6 - 7
src/utils/ruoyi.ts

@@ -49,16 +49,15 @@ export function parseTime(time: any, pattern?: string) {
  * @param dateRange
  * @param propName
  */
-export const addDateRange = (params: any, dateRange: any[], propName?: string) => {
+export const addDateRange = (params: any, dateRange: any[], beginTimeName?: string, endTimeName?: string) => {
   const search = params;
-  search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
   dateRange = Array.isArray(dateRange) ? dateRange : [];
-  if (typeof propName === 'undefined') {
-    search.params['beginTime'] = dateRange[0];
-    search.params['endTime'] = dateRange[1];
+  if (!beginTimeName || !endTimeName) {
+    search['beginTime'] = dateRange[0];
+    search['endTime'] = dateRange[1];
   } else {
-    search.params['begin' + propName] = dateRange[0];
-    search.params['end' + propName] = dateRange[1];
+    search[beginTimeName] = dateRange[0];
+    search[beginTimeName] = dateRange[1];
   }
   return search;
 };

+ 59 - 6
src/views/globalMap/LeftMenu.vue

@@ -2,8 +2,12 @@
   <div class="menu-container">
     <div class="search-box">
       <el-icon color="#fff"><Search /></el-icon>
-      <input v-model="searchText" class="input" @change="changeSearchText" />
+      <input v-model="searchState.searchText" class="input" @change="changeSearchText" />
+      <div v-show="searchState.showList" class="search-list">
+        <div v-for="(item, index) in searchState.resultList" :key="index" class="list-item" @click="selectSearchMarker(item)">{{ item.name }}</div>
+      </div>
     </div>
+
     <div class="menu-box">
       <div class="menu-btn" @click="changExpand">{{ menuState.isExpand ? '收起' : '展开' }}</div>
       <Transition>
@@ -52,19 +56,47 @@ interface Props {
   menuData: object;
 }
 const props = withDefaults(defineProps<Props>(), {});
-const emits = defineEmits(['swtichMap', 'addMarkers', 'clickMenu']);
+const emits = defineEmits(['switchMap', 'addMarkers', 'clickMenu', 'selectSearchMarker']);
+
+const searchState = reactive({
+  searchText: '',
+  showList: false,
+  resultList: []
+});
 
-const searchText = ref('');
 const changeSearchText = () => {
-  console.log('搜索值:', searchText.value);
+  if (!searchState.searchText) {
+    searchState.showList = false;
+    return;
+  } else {
+    console.log('搜索值:', searchState.searchText);
+    let data = [];
+    for (let i = 1; i < 11; i++) {
+      data.push({
+        parentId: '12',
+        id: '11',
+        name: 'xxxxxxxxx' + searchState.searchText + i,
+        latitude: 21.662999,
+        longitude: 110.925456,
+        path: 'emergency_expert'
+      });
+    }
+    searchState.showList = true;
+    searchState.resultList = data;
+  }
+};
+
+// 点击搜索结果,添加标注
+const selectSearchMarker = (item) => {
+  searchState.showList = false;
+  emits('selectSearchMarker', item);
 };
 
-let menuState = reactive({
+const menuState = reactive({
   // 是否展开菜单
   isExpand: true,
   activeIndex: 0
 });
-
 // 展示收起整个菜单
 const changExpand = () => {
   menuState.isExpand = !menuState.isExpand;
@@ -107,6 +139,27 @@ const handleClick = (item) => {
     background: #042350;
     color: #fff;
     width: 100%;
+    margin-left: 5px;
+  }
+  .search-list {
+    position: absolute;
+    top: 45px;
+    left: 0;
+    background-color: #eceff7;
+    border: 1px solid #042350;
+    width: 100%;
+    color: #000;
+    z-index: 9;
+    .list-item {
+      display: block;
+      font-size: 16px;
+      line-height: 40px;
+      cursor: pointer;
+      padding: 0 10px;
+      &:hover {
+        background-color: #0a6ebd;
+      }
+    }
   }
 }
 .menu-box {

+ 52 - 4
src/views/globalMap/index.vue

@@ -32,9 +32,15 @@
       @unSelectGraphics="unSelectGraphics"
     />
     <!--左侧菜单-->
-    <LeftMenu :menu-data="menuData" style="position: absolute; top: 20px; left: 20px" @add-markers="addMarkers" @click-menu="clickMenu" />
+    <LeftMenu
+      :menu-data="menuData"
+      style="position: absolute; top: 20px; left: 20px"
+      @add-markers="addMarkers"
+      @click-menu="clickMenu"
+      @selectSearchMarker="selectSearchMarker"
+    />
     <!--更换地图类型-->
-    <SwitchMapTool :active-map="activeMap" class="tool-box" @swtich-map="swtichMap" />
+    <SwitchMapTool :active-map="activeMap" class="tool-box" @swtich-map="switchMap" />
     <!--时间轴-->
     <TimeAxis />
     <DrawTools
@@ -47,6 +53,9 @@
       @undo="undo"
     />
     <AnalyzeDataDialog v-if="analysisSpatialDataShow" :selectedScope="selectedScope" />
+    <div v-if="showDialog" class="box">
+      <div v-for="(item, index) in markerList" :key="index" @click="toAddress(item)">{{item.name}}</div>
+    </div>
   </div>
 </template>
 
@@ -54,6 +63,7 @@
 import Map from '@/components/Map/index.vue';
 // import YztMap from '@/components/Map/YztMap/index.vue';
 import YMap from '@/components/Map/YMap.vue';
+import Dialog from '@/components/Dialog/index2.vue';
 import MapLogical from '@/components/Map/MapLogical.vue';
 import { iconList, logicalData } from './data/mapData';
 import SwitchMapTool from '@/views/globalMap/SwitchMapTool.vue';
@@ -72,10 +82,12 @@ let map2Ref = ref(null);
 // logical vectorgraph satellite satellite2
 let activeMap = ref('vectorgraph');
 
-const swtichMap = (key) => {
+const switchMap = (key) => {
   activeMap.value = key;
 };
 let menuData = ref([]);
+let showDialog = ref(false);
+let markerList = ref([]);
 const initData = () => {
   listMenu().then((res: any) => {
     const data = res.data[0]?.children;
@@ -101,10 +113,13 @@ const addMarkers = (item) => {
   const dom = activeMap.value === 'satellite2' ? map2Ref.value : mapRef.value;
   if (dom) {
     if (!item.checked) {
+      showDialog.value = false;
       dom.clearMarker(item.path);
     } else {
       getRescueMateria(item.path).then((res) => {
         const data = res.data && res.data.list ? res.data?.list : [];
+        markerList.value = data;
+        showDialog.value = true;
         data.forEach((item2) => {
           // 获取图标
           if (iconList[item.path]) {
@@ -121,6 +136,11 @@ const addMarkers = (item) => {
     }
   }
 };
+// 跳转指定地点
+const toAddress = (item) => {
+  const dom = activeMap.value === 'satellite2' ? map2Ref.value : mapRef.value;
+  dom.setCenter(item);
+};
 
 let showDrawTools = ref(false);
 // 点击菜单
@@ -129,6 +149,16 @@ const clickMenu = (item) => {
     showDrawTools.value = !showDrawTools.value;
   }
 };
+// 点击搜索结果,添加标注
+const selectSearchMarker = (item) => {
+  const dom = activeMap.value === 'satellite2' ? map2Ref.value : mapRef.value;
+  let item2 = deepClone(item);
+  if (iconList[item.path]) {
+    item2.image = iconList[item.path].image;
+    item2.size = iconList[item.path].size;
+  }
+  dom.addSearchMarker(item2);
+};
 
 const mouseToolState = reactive({
   drawing: false,
@@ -213,7 +243,7 @@ const analysisSpatial = (data, len?: string) => {
     location = `POLYGON((${pathStr}))`;
     getEmergencyExpertNum({ location }).then((res) => {
       selectedScope[data.id] = deepClone(analysisSpatialData);
-      selectedScope[data.id].area = countCircleArea(data.center, data.radius);
+      selectedScope[data.id].area = countRectangleArea(pathArr);
       selectedScope[data.id].expert = res.data.list[0].expertNum?.toString();
       analysisSpatialDataShow.value = true;
     });
@@ -249,4 +279,22 @@ onMounted(() => {
   bottom: 100px;
   left: 20px;
 }
+.box {
+  position: absolute;
+  top: 20px;
+  right: 20px;
+  width: 300px;
+  background-color: #041d55;
+  display: flex;
+  flex-direction: column;
+  padding: 20px;
+  color: #fff;
+  font-size: 16px;
+  height: 500px;
+  max-height: 90%;
+  div {
+    line-height: 30px;
+    cursor: pointer;
+  }
+}
 </style>

+ 0 - 0
src/views/routineCommandMap/planManage/Details.vue → src/views/routineCommandMap/PlanManage/Details.vue


+ 0 - 0
src/views/routineCommandMap/planManage/index.vue → src/views/routineCommandMap/PlanManage/index.vue


+ 111 - 0
src/views/routineCommandMap/RightSection/KnowledgeDialog.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="app-container">
+    <transition name="fade">
+      <div class="mb-[10px]">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+          <el-form-item style="width: 200px" label="事件类型" prop="eventType">
+            <el-select v-model="queryParams.eventType" placeholder="全部" clearable>
+              <el-option label="全部" value=""></el-option>
+              <el-option v-for="dict in mm_event_type" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="发布日期" prop="publishDate">
+            <el-date-picker
+              v-model="dateRange"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              value-format="YYYY-MM-DD"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-input v-model="queryParams.summary" placeholder="请输入报告的关键词/主题词" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </transition>
+
+    <!--      表格组件-->
+    <el-table v-loading="loading" :data="knowledgeList">
+      <el-table-column label="报告编号" align="center" prop="reportId" />
+      <el-table-column label="报告名称" align="center" prop="reportName" />
+      <el-table-column label="主题词" align="center" prop="summary" />
+      <el-table-column label="事件类型" align="center" prop="eventType" />
+      <el-table-column label="摘要" align="center" prop="summary" />
+      <el-table-column label="来源单位" align="center" prop="publishingUnit" />
+      <el-table-column label="发布日期" align="center" prop="publishDate" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-tooltip content="查看" placement="top">
+            <el-button link type="primary" icon="View" @click="handleView(scope.row)"></el-button>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+  </div>
+</template>
+
+<script lang="ts" setup name="KnowledgeDialog">
+import { getReportsList } from '@/api/routineCommandMap';
+import { parseTime } from '@/utils/ruoyi';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { mm_event_type } = toRefs<any>(proxy?.useDict('mm_event_type'));
+const queryFormRef = ref();
+let loading = ref(false);
+let total = ref(0);
+let dateRange = ref(['', '']);
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  summary: '',
+  eventType: ''
+});
+let knowledgeList = ref([]);
+let showDetailDialog = ref(false);
+let detailData = ref({});
+
+// 查询列表数据
+const getList = () => {
+  getReportsList(proxy?.addDateRange(queryParams, dateRange.value)).then((res) => {
+    res.data.forEach((item) => {
+      item.publishDate = parseTime(item.publishDate);
+      item.img = 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500';
+    });
+    knowledgeList.value = res.data;
+  });
+};
+
+// 搜索
+const handleQuery = () => {
+  queryParams.pageNum = 1;
+  getList();
+};
+
+// 重置
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+// 显示应急知识库详情
+const handleView = (row) => {
+  showDetailDialog.value = true;
+};
+
+onMounted(() => {
+  getList();
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 35 - 57
src/views/routineCommandMap/RightSection.vue → src/views/routineCommandMap/RightSection/index.vue

@@ -26,7 +26,7 @@
         <div class="img-list">
           <div v-for="(item, index) in planManageState.listData" :key="index" class="img-item" @click="showPlanManageDetail">
             <img class="img" :src="item.img" alt="" />
-            <div class="img-text">{{ item.title }}</div>
+            <div class="img-text">{{ item.planName }}</div>
           </div>
         </div>
       </div>
@@ -40,7 +40,7 @@
         <div class="img-list">
           <div v-for="(item, index) in knowledgeBaseState.listData" :key="index" class="img-item" @click="showKnowledgeBaseDetail">
             <img class="img" :src="item.img" alt="" />
-            <div class="img-text">{{ item.title }}</div>
+            <div class="img-text">{{ item.reportName }}</div>
           </div>
         </div>
       </div>
@@ -57,13 +57,16 @@
   <Dialog v-model="planManageState.showListDialog" title="预案管理列表" width="70%"></Dialog>
   <Dialog v-model="planManageState.showDetailDialog" title="预案管理详情" width="70%"></Dialog>
   <!--应急知识库弹窗-->
-  <Dialog v-model="knowledgeBaseState.showListDialog" title="应急知识库列表" width="70%"></Dialog>
+  <Dialog v-model="knowledgeBaseState.showListDialog" title="应急知识库列表" width="70%">
+    <KnowledgeDialog />
+  </Dialog>
   <Dialog v-model="knowledgeBaseState.showDetailDialog" title="应急知识库详情" width="70%"></Dialog>
 </template>
 
-<script lang="ts" setup>
+<script lang="ts" setup name="RightSection">
 // 视频监控
-import { getEmergencyVideoCata } from '@/api/routineCommandMap';
+import { getEmergencyPlanList, getEmergencyVideoCata, getReportsList } from '@/api/routineCommandMap';
+import KnowledgeDialog from '@/views/routineCommandMap/RightSection/KnowledgeDialog.vue';
 
 const videoMonitorState = reactive({
   listData: [],
@@ -83,6 +86,10 @@ const showVideoDialog = (item) => {
 
 // 预案管理
 const planManageState = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 5
+  },
   listData: [],
   showListDialog: false,
   showDetailDialog: false
@@ -98,22 +105,21 @@ const showPlanManageDetail = () => {
 
 // 应急知识库
 const knowledgeBaseState = reactive({
+  queryParams: {
+    pageNum: 1,
+    pageSize: 5
+  },
   listData: [],
-  showListDialog: false,
-  showDetailDialog: false
+  showListDialog: false
 });
 // 显示应急知识库列表
 const showKnowledgeBaseList = () => {
   knowledgeBaseState.showListDialog = true;
 };
-// 显示应急知识库详情
-const showKnowledgeBaseDetail = () => {
-  knowledgeBaseState.showDetailDialog = true;
-};
 
 // 初始化数据
 const initData = () => {
-  // 预案管理数据
+  // 视频监控数据
   getEmergencyVideoCata({
     current: 1,
     size: 6
@@ -121,51 +127,20 @@ const initData = () => {
     videoMonitorState.listData = res.rows;
     videoMonitorState.listData[0]['video_code'] = '44098102801327000256';
   });
-  planManageState.listData = [
-    {
-      title: '广东省城市轨道交通运营突发事件应急预案',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: '广东省城市轨道交通运营突发事件应急预案',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: '广东省城市轨道交通运营突发事件应急预案',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: '广东省城市轨道交通运营突发事件应急预案',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: '广东省城市轨道交通运营突发事件应急预案',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    }
-  ];
+  // 预案管理数据
+  getEmergencyPlanList(planManageState.queryParams).then((res) => {
+    res.data.forEach((item) => {
+      item.img = 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
+    });
+    planManageState.listData = res.data;
+  });
   // 应急知识库数据
-  knowledgeBaseState.listData = [
-    {
-      title: 'xxx市轨道交通运营突发事件',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: 'xxx市轨道交通运营突发事件',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: 'xxx市轨道交通运营突发事件',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: 'xxx市轨道交通运营突发事件',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    },
-    {
-      title: 'xxx市轨道交通运营突发事件',
-      img: 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
-    }
-  ];
+  getReportsList(knowledgeBaseState.queryParams).then((res) => {
+    res.data.forEach((item) => {
+      item.img = 'https://img0.baidu.com/it/u=1918456140,3851309096&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500'
+    });
+    knowledgeBaseState.listData = res.data;
+  });
 };
 // 加载完成事件
 onMounted(() => {
@@ -274,14 +249,17 @@ onMounted(() => {
 
 .img-list {
   display: flex;
-  justify-content: space-between;
   .img-item {
     width: 280px;
     height: 500px;
+    margin-left: 215px;
     display: flex;
     flex-direction: column;
     align-items: center;
     cursor: pointer;
+    &:first-child {
+      margin-left: 0;
+    }
     .img {
       width: 260px;
       height: 360px;

+ 1 - 1
src/views/routineCommandMap/index.vue

@@ -11,7 +11,7 @@
 
 <script lang="ts" setup name="videoList">
 import LeftSection from './LeftSection.vue';
-import RightSection from './RightSection.vue';
+import RightSection from './RightSection/index.vue';
 import MiddleSection from './MiddleSection.vue';
 import autofit from 'autofit.js';