Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	src/types/components.d.ts
yangyuxuan 3 mesi fa
parent
commit
3c78486b0c

+ 9 - 0
src/api/emergencyCommandMap/Drone.ts

@@ -0,0 +1,9 @@
+import request from '@/utils/request';
+// 无人机-部门树
+export function getDroneTree(params) {
+  return request({
+    url: '/api/videoResource/avcon/get_drone_tree',
+    method: 'get',
+    params: params
+  });
+}

+ 20 - 0
src/api/emergencyCommandMap/communication.ts

@@ -18,6 +18,7 @@ export function getStartMiniWithNoParam(data) {
   });
 }
 
+/*
 // 视频会商-部门树
 export function getAvconDeptTree(params) {
   return request({
@@ -35,3 +36,22 @@ export function getAvconDeptList(id, params) {
     params: params
   });
 }
+*/
+
+// 视频会商-设备树状分组
+export function getAvconDeviceTree(params) {
+  return request({
+    url: '/api/videoResource/avcon/get_device_tree',
+    method: 'get',
+    params: params
+  });
+}
+
+// 视频会商-设备列表
+export function getAvconDeviceList(id, params) {
+  return request({
+    url: '/api/videoResource/avcon/get_device_list/'+id,
+    method: 'get',
+    params: params
+  });
+}

BIN
src/assets/images/censusDataAnalysis/bar.png


+ 9 - 1
src/types/auto-imports.d.ts

@@ -78,6 +78,7 @@ declare global {
   const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
   const onUnmounted: typeof import('vue')['onUnmounted']
   const onUpdated: typeof import('vue')['onUpdated']
+  const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const provide: typeof import('vue')['provide']
   const provideLocal: typeof import('@vueuse/core')['provideLocal']
@@ -189,6 +190,7 @@ declare global {
   const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
   const useGamepad: typeof import('@vueuse/core')['useGamepad']
   const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
+  const useId: typeof import('vue')['useId']
   const useIdle: typeof import('@vueuse/core')['useIdle']
   const useImage: typeof import('@vueuse/core')['useImage']
   const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
@@ -205,6 +207,7 @@ declare global {
   const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
   const useMemoize: typeof import('@vueuse/core')['useMemoize']
   const useMemory: typeof import('@vueuse/core')['useMemory']
+  const useModel: typeof import('vue')['useModel']
   const useMounted: typeof import('@vueuse/core')['useMounted']
   const useMouse: typeof import('@vueuse/core')['useMouse']
   const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
@@ -252,6 +255,7 @@ declare global {
   const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
   const useSupported: typeof import('@vueuse/core')['useSupported']
   const useSwipe: typeof import('@vueuse/core')['useSwipe']
+  const useTemplateRef: typeof import('vue')['useTemplateRef']
   const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
   const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
   const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
@@ -303,7 +307,7 @@ declare global {
 // for type re-export
 declare global {
   // @ts-ignore
-  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+  export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
   import('vue')
 }
 // for vue template auto import
@@ -384,6 +388,7 @@ declare module 'vue' {
     readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
     readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
+    readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
     readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
@@ -495,6 +500,7 @@ declare module 'vue' {
     readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
     readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
     readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
+    readonly useId: UnwrapRef<typeof import('vue')['useId']>
     readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
     readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
     readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
@@ -511,6 +517,7 @@ declare module 'vue' {
     readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
     readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
     readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
+    readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
     readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
     readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
     readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
@@ -558,6 +565,7 @@ declare module 'vue' {
     readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
     readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
     readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
+    readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
     readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
     readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
     readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>

+ 7 - 8
src/types/components.d.ts

@@ -24,12 +24,10 @@ declare module 'vue' {
     DistributionMap: typeof import('./../components/Map/YztMap/DistributionMap.vue')['default']
     DrawMap: typeof import('./../components/Map/YztMap/DrawMap.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
-    ElAnchor: typeof import('element-plus/es')['ElAnchor']
-    ElAnchorLink: typeof import('element-plus/es')['ElAnchorLink']
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
-    ElCard: typeof import('element-plus/es')['ElCard']
+    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
@@ -44,6 +42,7 @@ declare module 'vue' {
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
@@ -53,17 +52,17 @@ declare module 'vue' {
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
+    ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
+    ElSlider: typeof import('element-plus/es')['ElSlider']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
-    ElTable: typeof import('element-plus/es')['ElTable']
-    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
+    ElTimeline: typeof import('element-plus/es')['ElTimeline']
+    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
     ElTree: typeof import('element-plus/es')['ElTree']
-    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     ExcelEditor: typeof import('./../components/ExcelEditor/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']

+ 196 - 1
src/views/censusDataAnalysis/HistoricalAnalysis.vue

@@ -39,14 +39,209 @@
           </div>
         </div>
         <div class="title-box2">历史年度自然灾害受灾人口统计</div>
-        <img src="@/assets/images/censusDataAnalysis/bar.png" alt="" style="cursor: pointer" />
+        <Chart :option="option1" style="width: 100%; height: 300px" />
       </div>
     </div>
   </div>
 </template>
 
 <script lang="ts" setup>
+import * as echarts from 'echarts';
 let dateRange = ref([]);
+const CubeLeft = echarts.graphic.extendShape({
+  shape: {
+    x: 0,
+    y: 0
+  },
+  buildPath: function (ctx, shape) {
+    const xAxisPoint = shape.xAxisPoint;
+    const c0 = [shape.x + 12, shape.y + 3];
+    const c1 = [shape.x - 12, shape.y];
+    const c2 = [shape.x - 12, xAxisPoint[1] - 6];
+    const c3 = [shape.x + 12, xAxisPoint[1]];
+    ctx.moveTo(c0[0], c0[1]).lineTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).closePath();
+  }
+});
+const CubeRight = echarts.graphic.extendShape({
+  shape: {
+    x: 0,
+    y: 0
+  },
+  buildPath: function (ctx, shape) {
+    const xAxisPoint = shape.xAxisPoint;
+    const c1 = [shape.x + 12, shape.y + 3];
+    const c2 = [shape.x + 12, xAxisPoint[1]];
+    const c3 = [shape.x + 27, xAxisPoint[1] - 9];
+    const c4 = [shape.x + 27, shape.y];
+    ctx.moveTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).lineTo(c4[0], c4[1]).closePath();
+  }
+});
+const CubeTop = echarts.graphic.extendShape({
+  shape: {
+    x: 0,
+    y: 0
+  },
+  buildPath: function (ctx, shape) {
+    const c1 = [shape.x - 12, shape.y];
+    const c2 = [shape.x + 12, shape.y + 3];
+    const c3 = [shape.x + 27, shape.y];
+    const c4 = [shape.x + 3, shape.y - 3];
+    ctx.moveTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).lineTo(c4[0], c4[1]).closePath();
+  }
+});
+echarts.graphic.registerShape('CubeLeft', CubeLeft);
+echarts.graphic.registerShape('CubeRight', CubeRight);
+echarts.graphic.registerShape('CubeTop', CubeTop);
+const option1 = ref({
+  backgroundColor: '#040a37',
+  grid: {
+    left: 20,
+    right: 10,
+    bottom: 40,
+    top: 10,
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['茂南区', '电白区', '高州市', '信宜市', '化州市'],
+    axisLine: {
+      show: true,
+      lineStyle: {
+        color: '#3c4550'
+      }
+    },
+    offset: 10,
+    axisTick: {
+      show: false
+    },
+    axisLabel: {
+      fontSize: 16,
+      color: '#c9c9c9'
+    }
+  },
+  yAxis: {
+    type: 'value',
+    axisLine: {
+      show: false
+    },
+    splitLine: {
+      show: true,
+      lineStyle: {
+        color: '#3c4550'
+      }
+    },
+    axisTick: {
+      show: false
+    },
+    axisLabel: {
+      fontSize: 16,
+      color: '#c9c9c9'
+    },
+    boundaryGap: ['20%', '20%']
+  },
+  series: [
+    {
+      type: 'custom',
+      renderItem: (params, api) => {
+        const location = api.coord([api.value(0), api.value(1)]);
+        return {
+          type: 'group',
+          children: [
+            {
+              type: 'CubeLeft',
+              shape: {
+                api,
+                xValue: api.value(0),
+                yValue: api.value(1),
+                x: location[0],
+                y: location[1],
+                xAxisPoint: api.coord([api.value(0), 0])
+              },
+              style: {
+                fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  {
+                    offset: 0,
+                    color: '#49BEE5'
+                  },
+                  {
+                    offset: 1,
+                    color: '#3B80E2'
+                  }
+                ])
+              }
+            },
+            {
+              type: 'CubeRight',
+              shape: {
+                api,
+                xValue: api.value(0),
+                yValue: api.value(1),
+                x: location[0],
+                y: location[1],
+                xAxisPoint: api.coord([api.value(0), 0])
+              },
+              style: {
+                fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  {
+                    offset: 0,
+                    color: '#49BEE5'
+                  },
+                  {
+                    offset: 1,
+                    color: '#3B80E2'
+                  }
+                ])
+              }
+            },
+            {
+              type: 'CubeTop',
+              shape: {
+                api,
+                xValue: api.value(0),
+                yValue: api.value(1),
+                x: location[0],
+                y: location[1],
+                xAxisPoint: api.coord([api.value(0), 0])
+              },
+              style: {
+                fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  {
+                    offset: 0,
+                    color: '#3B80E2'
+                  },
+                  {
+                    offset: 1,
+                    color: '#49BEE5'
+                  }
+                ])
+              }
+            }
+          ]
+        };
+      },
+      data: []
+    },
+    {
+      type: 'bar',
+      label: {
+        show: true,
+        position: 'top',
+        fontSize: 17,
+        fontWeight: 'bold',
+        color: '#fff',
+        offset: [4, -10]
+      },
+      itemStyle: {
+        color: 'transparent'
+      },
+      data: []
+    }
+  ]
+});
+onMounted(() => {
+  option1.value.series[0].data = [2012, 1230, 3790, 2349, 1654];
+  option1.value.series[1].data = [2012, 1230, 3790, 2349, 1654];
+});
 </script>
 
 <style lang="scss" scoped>

+ 58 - 23
src/views/dataFilling/dossierDetail.vue

@@ -7,7 +7,7 @@
             <h2 v-if="detailData.title" key="business" style="font-weight: bolder">{{ detailData.title }}</h2>
             <p class="report-period">【填报周期】:{{ detailData.start }} 至 {{ detailData.end }}</p>
           </el-col>
-          <el-col :span="1.5">
+          <el-col v-if="props.collectionStatus === '0'" :span="1.5">
             <el-button type="primary" @click="handleWrite()"> 收取 </el-button>
           </el-col>
           <el-col :span="1.5">
@@ -33,14 +33,15 @@
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
-import { ElTable, ElButton, ElCol, ElRow, ElTableColumn } from 'element-plus';
+import { ElTable, ElButton, ElCol, ElRow, ElTableColumn, ElMessageBox } from 'element-plus';
 import * as XLSX from 'xlsx';
 import { fillDetail, collectList } from '@/api/dataFilling/fileManagement';
 
 const emits = defineEmits(['close']);
 const props = defineProps({
-  eventId: String | Number
-  // table_name: string
+  eventId: String,
+  tableName: String,
+  collectionStatus: String
 });
 //初始化表头和表格数据
 const editableHeaders = ref([]);
@@ -75,26 +76,60 @@ const fetchFillDetail = async () => {
   }
 };
 
-//收取
+// //收取
+// const handleWrite = () => {
+//   if (props.eventId && props.eventId !== '') {
+//     const data = {
+//       report_id: props.eventId,
+//       creator_id: 3,
+//       new_status: 2
+//     };
+//     collectList(data)
+//       .then((response) => {
+//         if (response.code === 200) {
+//           console.log(response.msg);
+//         } else {
+//           console.error('更新失败', response.msg);
+//         }
+//       })
+//       .catch((error) => {
+//         console.error('请求失败', error);
+//       });
+//   }
+// };
+// 收取
 const handleWrite = () => {
-  if (props.eventId && props.eventId !== '') {
-    const data = {
-      report_id: props.eventId,
-      creator_id: 3,
-      new_status: 2
-    };
-    collectList(data)
-      .then((response) => {
-        if (response.code === 200) {
-          console.log(response.msg);
-        } else {
-          console.error('更新失败', response.msg);
-        }
-      })
-      .catch((error) => {
-        console.error('请求失败', error);
-      });
-  }
+  ElMessageBox.confirm('收取后,表格不可再填报,确认是否收取?', '提示', {
+    confirmButtonText: '确认',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(() => {
+      if (props.eventId && props.eventId !== '') {
+        const data = {
+          report_id: props.eventId,
+          creator_id: 3,
+          new_status: 2
+        };
+        collectList(data)
+          .then((response) => {
+            if (response.code === 200) {
+              console.log(response.msg);
+              // 更新 collectionStatus 为 '2'(已收取)
+              emits('update:collectionStatus', '2');
+            } else {
+              console.error('更新失败', response.msg);
+            }
+          })
+          .catch((error) => {
+            console.error('请求失败', error);
+          });
+      }
+    })
+    .catch(() => {
+      // 用户点击了取消按钮
+      console.log('取消收取');
+    });
 };
 const handleReturn = () => {
   emits('close');

+ 8 - 2
src/views/dataFilling/fileManagement.vue

@@ -46,6 +46,7 @@
       v-if="dossierDetailState.show"
       :event-id="dossierDetailState.eventId"
       :table-name="dossierDetailState.table_name"
+      :collection-status="dossierDetailState.collection_status"
       @close="handleCancel"
     />
   </div>
@@ -135,16 +136,21 @@ const handleView = (row) => {
   if (row) {
     dossierDetailState.eventId = row.report_id;
     dossierDetailState.table_name = row.table_name;
+    dossierDetailState.collection_status = row.collection_status;
     dossierDetailState.show = true;
   }
 };
 let dossierDetailState = reactive({
   show: false,
   eventId: '',
-  table_name: ''
+  table_name: '',
+  collection_status: ''
 });
+const handleCancel = () => {
+  dossierDetailState.show = false;
+  fetchFillList();
+};
 onMounted(() => {
-  // debugger
   fetchFillList();
   loading.value = false;
 });

+ 172 - 74
src/views/emergencyCommandMap/LeftSection/Communication.vue

@@ -2,7 +2,7 @@
   <div class="communication-container">
     <div class="common-title gradient-text">前方通信</div>
     <div class="tabs">
-      <div v-for="(item, index) in menu" :key="index" :class="activeIndex === index ? 'tab tab_active' : 'tab'">
+      <div v-for="(item, index) in menu" :key="index" :class="activeIndex === index ? 'tab tab_active' : 'tab'" @click="activeIndex = index">
         {{ item.name }}
       </div>
     </div>
@@ -24,7 +24,7 @@
           >
             <el-option v-for="level in options" :key="level.value" :label="level.name" :value="level.value"></el-option>
           </el-select>
-          <el-input v-model="queryParams.keyword" class="custom-input" placeholder="组织架构搜索">
+          <el-input v-model="queryParams.value1" class="custom-input" placeholder="设备搜索">
             <template #prefix>
               <el-icon class="el-input__icon"><search /></el-icon>
             </template>
@@ -42,8 +42,8 @@
                 <div class="td2">
                   <div :class="getCheckedClass()" @click="handleChecked"></div>
                 </div>
-                <div class="td">姓名</div>
-                <div class="td3">职务</div>
+                <div class="td">设备</div>
+                <!--<div class="td3">姓名职务</div>-->
               </div>
               <div class="table-content">
                 <div v-for="(item, index) in userList" :key="index" class="tr2">
@@ -51,10 +51,11 @@
                     <div :class="item.checked ? 'common-checked-active' : 'common-checked'" @click="handleChecked2(item)"></div>
                   </div>
                   <div class="td">{{ item.name }}</div>
-                  <div class="td3">
+                  <!--<div class="td3">
                     {{ item.duty }}
                     <div class="phone-icon"></div>
                   </div>
+                  -->
                 </div>
               </div>
             </div>
@@ -88,22 +89,62 @@
           <div class="icon1"></div>
           <div class="text">会议号入会</div>
         </div>
-        <div class="btn" @click="handleStartCall">
+        <div class="btn" @click="handleOpenCall">
           <div class="icon2"></div>
           <div class="text">电话呼叫</div>
         </div>
-        <div class="btn" @click="handleStartMeeting">
+        <div class="btn" @click="handleOpenMeeting">
           <div class="icon3"></div>
           <div class="text">发起会议</div>
         </div>
       </div>
     </div>
-    <Dialog v-if="showJoinMeeting" customShow type="xs" height="180px" title="加入会议" @confirm="handleJoinMeeting" @close="closeDialog">
+    <Drone v-show="activeIndex === 1" />
+    <IndividualEquipment v-show="activeIndex === 2" />
+
+    <Dialog v-if="showOpenMeeting" customShow type="xs" height="220px" title="发起会议" @confirm="handleStartMeeting" @close="closeOpenDialog">
+      <div class="dialog-content">
+        <el-form ref="form2Ref" :model="openMeetingForm" :rules="rules2">
+          
+          <el-form-item label="账号" label-width="80px" prop="username">
+            <el-input v-model="openMeetingForm.username" class="custom-input2" clearable placeholder="请输入设备账号" />
+          </el-form-item>
+
+          <el-form-item label="密码" label-width="80px" prop="userpass">
+            <el-input v-model="openMeetingForm.userpass" type="password" class="custom-input2" clearable placeholder="请输入设备密码" />
+          </el-form-item>
+        </el-form>
+      </div>
+    </Dialog>
+
+    <Dialog v-if="showJoinMeeting" customShow type="xs" height="280px" title="加入会议" @confirm="handleJoinMeeting" @close="closeDialog">
       <div class="dialog-content">
         <el-form ref="formRef" :model="meetingForm" :rules="rules">
           <el-form-item label="会议号" label-width="80px" prop="roomcode">
             <el-input v-model="meetingForm.roomcode" class="custom-input2" clearable placeholder="请输入会议号" />
           </el-form-item>
+          <el-form-item label="账号" label-width="80px" prop="username">
+            <el-input v-model="meetingForm.username" class="custom-input2" clearable placeholder="请输入设备账号" />
+          </el-form-item>
+
+          <el-form-item label="密码" label-width="80px" prop="userpass">
+            <el-input v-model="meetingForm.userpass" type="password" class="custom-input2" clearable placeholder="请输入设备密码" />
+          </el-form-item>
+        </el-form>
+      </div>
+    </Dialog>
+
+    <Dialog v-if="showOpenMeeting" customShow type="xs" height="220px" title="电话呼叫" @confirm="handleStartCall" @close="closeOpenDialog">
+      <div class="dialog-content">
+        <el-form ref="form2Ref" :model="openMeetingForm" :rules="rules2">
+          
+          <el-form-item label="账号" label-width="80px" prop="username">
+            <el-input v-model="openMeetingForm.username" class="custom-input2" clearable placeholder="请输入设备账号" />
+          </el-form-item>
+
+          <el-form-item label="密码" label-width="80px" prop="userpass">
+            <el-input v-model="openMeetingForm.userpass" type="password" class="custom-input2" clearable placeholder="请输入设备密码" />
+          </el-form-item>
         </el-form>
       </div>
     </Dialog>
@@ -112,10 +153,11 @@
 
 <script lang="ts" setup>
 import { Search } from '@element-plus/icons-vue';
-import { getAvconDeptList, getAvconDeptTree, getStartMiniWithNoParam, getStartMiniParam } from '@/api/emergencyCommandMap/communication';
+import { getStartMiniWithNoParam, getStartMiniParam, getAvconDeviceTree, getAvconDeviceList } from '@/api/emergencyCommandMap/communication';
+import Drone from './Drone.vue';
+import IndividualEquipment from './IndividualEquipment.vue';
 
 const proxy = getCurrentInstance()?.proxy;
-
 let activeIndex = ref(0);
 const options = ref([{ name: '全部', value: '全部' }]);
 const menu = ref([{ name: '视频会商' }, { name: '无人机' }, { name: '单兵设备' }]);
@@ -127,10 +169,11 @@ const queryParams = reactive({
 });
 
 const getTree = () => {
-  getAvconDeptTree(queryParams).then((res) => {
+  getAvconDeviceTree(queryParams).then((res) => {
     treeData.value = res.data;
   });
 };
+/*
 const selectList = computed(() => {
   const data = [];
   userList.value.forEach((item) => {
@@ -140,6 +183,8 @@ const selectList = computed(() => {
   });
   return data;
 });
+*/
+const selectList = ref([]);
 const getCheckedClass = () => {
   let res = 'common-checked';
   const len = userList.value.length;
@@ -152,7 +197,7 @@ const getCheckedClass = () => {
   return res;
 };
 const handleNodeClick = (item) => {
-  getAvconDeptList(item.id, {}).then((res) => {
+  getAvconDeviceList(item.id, {}).then((res) => {
     res.data.forEach((item) => {
       item.checked = false;
     });
@@ -211,10 +256,14 @@ const deleteItem = (item) => {
 let formRef = ref();
 let showJoinMeeting = ref(false);
 let meetingForm = reactive({
-  roomcode: ''
+  roomcode: '',
+  username: '',
+  userpass: ''
 });
 const rules = reactive({
-  roomcode: [{ required: true, message: '会议号不能为空', trigger: 'blur' }]
+  roomcode: [{ required: true, message: '会议号不能为空', trigger: 'blur' }],
+  username: [{ required: true, message: '会议账号不能为空', trigger: 'blur' }],
+  userpass: [{ required: true, message: '会议密码不能为空', trigger: 'blur' }]
 });
 // 点击会议号入会
 const showJoinMeetingDialog = () => {
@@ -224,6 +273,27 @@ const closeDialog = () => {
   showJoinMeeting.value = false;
   meetingForm.roomcode = '';
 };
+///////////////////
+let form2Ref = ref();
+let showOpenMeeting = ref(false);
+let openMeetingForm = reactive({
+  username: '',
+  userpass: ''
+});
+const rules2 = reactive({
+  username: [{ required: true, message: '会议账号不能为空', trigger: 'blur' }],
+  userpass: [{ required: true, message: '会议密码不能为空', trigger: 'blur' }]
+});
+const handleOpenMeeting = () => {
+  showOpenMeeting.value = true;
+};
+const closeOpenDialog = () => {
+  showOpenMeeting.value = false;
+};
+const handleOpenCall = () => {
+  showOpenMeeting.value = true;
+};
+
 // 会议号入会
 const handleJoinMeeting = () => {
   formRef.value?.validate((valid) => {
@@ -231,8 +301,8 @@ const handleJoinMeeting = () => {
       const screenWidth = window.screen.width * window.devicePixelRatio;
       const screenHeight = window.screen.height * window.devicePixelRatio;
       const data = {
-        'userid': '', // 空表示后台获取当前用户对应融合通信dev_id
-        'password': '123',
+        userid: meetingForm.username, // 空表示后台获取当前用户对应融合通信dev_id
+        password: meetingForm.userpass || '123',
         roomcode: meetingForm.roomcode, // 会议号
         windowpos: { 'x': 0, 'y': 0, 'width': screenWidth, 'height': screenHeight, 'top': true }
       };
@@ -243,6 +313,9 @@ const handleJoinMeeting = () => {
         a.href = res.data;
         // 触发点击事件
         a.click();
+        // 保存账号和密码
+        localStorage.setItem('meeting_username', meetingForm.username)
+        localStorage.setItem('meeting_userpass', meetingForm.userpass)
       });
       closeDialog();
     }
@@ -251,72 +324,97 @@ const handleJoinMeeting = () => {
 
 // 电话呼叫
 const handleStartCall = () => {
-  let dev_list = [];
-  userList.value.forEach((item) => {
-    if (item.checked && item.mobile != '') {
-      dev_list.push({ id: item.mobile, avtype: 'a' }); // a 音频 v 视频 默认 av
-    }
-  });
-  if (dev_list.length == 0) {
-    proxy?.$modal.msgError('请勾选人员');
-    return false;
-  }
-  const screenWidth = window.screen.width * window.devicePixelRatio;
-  const screenHeight = window.screen.height * window.devicePixelRatio;
-  const data = {
-    'userid': '', // 空表示后台获取当前用户对应融合通信dev_id
-    'password': '123',
-    roomid: '', // 空表示新会议室
-    windowpos: { 'x': 0, 'y': 0, 'width': screenWidth, 'height': screenHeight, 'top': true },
-    members: {
-      num: dev_list.length + 2, // 配置多少个座位,一般就是邀请人多少个就多少个
-      'dev-list': dev_list
+  form2Ref.value?.validate((valid) => {
+    if (valid) {
+      let dev_list = [];
+      userList.value.forEach((item) => {
+        if (item.checked && item.mobile != '') {
+          dev_list.push({ id: item.mobile, avtype: 'a' }); // a 音频 v 视频 默认 av
+        }
+      });
+      if (dev_list.length == 0) {
+        proxy?.$modal.msgError('请勾选人员');
+        return false;
+      }
+      const screenWidth = window.screen.width * window.devicePixelRatio;
+      const screenHeight = window.screen.height * window.devicePixelRatio;
+      const data = {
+        userid: openMeetingForm.username, // 空表示后台获取当前用户对应融合通信dev_id
+        password: openMeetingForm.userpass,
+        roomid: '', // 空表示新会议室
+        windowpos: { 'x': 0, 'y': 0, 'width': screenWidth, 'height': screenHeight, 'top': true },
+        members: {
+          num: dev_list.length + 2, // 配置多少个座位,一般就是邀请人多少个就多少个
+          'dev-list': dev_list
+        }
+      };
+      getStartMiniParam(data).then((res) => {
+        // 创建一个a标签元素
+        const a = document.createElement('a');
+        // 设置a标签的href属性
+        a.href = res.data;
+        // 触发点击事件
+        a.click();
+
+        // 保存账号和密码
+        localStorage.setItem('meeting_username', openMeetingForm.username)
+        localStorage.setItem('meeting_userpass', openMeetingForm.userpass)
+      });
+      closeOpenDialog();
     }
-  };
-  getStartMiniParam(data).then((res) => {
-    // 创建一个a标签元素
-    const a = document.createElement('a');
-    // 设置a标签的href属性
-    a.href = res.data;
-    // 触发点击事件
-    a.click();
-  });
+  })
 };
 // 发起会议
 const handleStartMeeting = () => {
-  let dev_list = [];
-  userList.value.forEach((item) => {
-    if (item.checked && item.mobile != '') {
-      dev_list.push({ id: item.mobile, avtype: 'av' }); // a 音频 v 视频 默认 av
-    }
-  });
-  if (dev_list.length == 0) {
-    proxy?.$modal.msgError('请勾选人员');
-    return false;
-  }
-  const screenWidth = window.screen.width * window.devicePixelRatio;
-  const screenHeight = window.screen.height * window.devicePixelRatio;
-  const data = {
-    userid: '', // 空表示后台获取当前用户对应融合通信dev_id
-    password: '123', // 空表示后台获取
-    roomid: '',
-    windowpos: { 'x': 0, 'y': 0, 'width': screenWidth, 'height': screenHeight, 'top': true },
-    members: {
-      num: dev_list.length + 2, // 配置多少个座位,一般就是邀请人多少个就多少个
-      'dev-list': dev_list
+  form2Ref.value?.validate((valid) => {
+    if (valid) {
+      let dev_list = [];
+      userList.value.forEach((item) => {
+        if (item.checked && item.mobile != '') {
+          dev_list.push({ id: item.mobile, avtype: 'av' }); // a 音频 v 视频 默认 av
+        }
+      });
+      if (dev_list.length == 0) {
+        proxy?.$modal.msgError('请勾选人员');
+        return false;
+      }
+      const screenWidth = window.screen.width * window.devicePixelRatio;
+      const screenHeight = window.screen.height * window.devicePixelRatio;
+      const data = {
+        userid: openMeetingForm.username, // 空表示后台获取当前用户对应融合通信dev_id
+        password: openMeetingForm.userpass,
+        roomid: '',
+        windowpos: { 'x': 0, 'y': 0, 'width': screenWidth, 'height': screenHeight, 'top': true },
+        members: {
+          num: dev_list.length + 2, // 配置多少个座位,一般就是邀请人多少个就多少个
+          'dev-list': dev_list
+        }
+      };
+      getStartMiniParam(data).then((res) => {
+        // 创建一个a标签元素
+        const a = document.createElement('a');
+        // 设置a标签的href属性
+        a.href = res.data;
+        // 触发点击事件
+        a.click();
+
+        // 保存账号和密码
+        localStorage.setItem('meeting_username', openMeetingForm.username)
+        localStorage.setItem('meeting_userpass', openMeetingForm.userpass)
+      });
+      closeOpenDialog();
     }
-  };
-  getStartMiniParam(data).then((res) => {
-    // 创建一个a标签元素
-    const a = document.createElement('a');
-    // 设置a标签的href属性
-    a.href = res.data;
-    // 触发点击事件
-    a.click();
-  });
+  })
 };
 
 onMounted(() => {
+  const meeting_username = localStorage.getItem('meeting_username') || "";
+  const meeting_userpass = localStorage.getItem('meeting_userpass') || "";
+  meetingForm.username = meeting_username;
+  meetingForm.userpass = meeting_userpass;
+  openMeetingForm.username = meeting_username;
+  openMeetingForm.userpass = meeting_userpass;
+
   getTree();
 });
 </script>

+ 98 - 0
src/views/emergencyCommandMap/LeftSection/Drone.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="content">
+    <div class="left-content">
+      <div class="search-box">
+        <el-input v-model="queryParams.equipment" class="custom-input" placeholder="搜索设备" @keyup.enter="getTree">
+          <template #prefix>
+            <el-icon class="el-input__icon"><search /></el-icon>
+          </template>
+        </el-input>
+      </div>
+      <div class="tree-container">
+        <div class="tree-box">
+          <div style="overflow-y: auto; height: 100%">
+            <el-tree :data="treeData" accordion @node-click="handleNodeClick" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <Dialog v-if="showIframe" v-model="showIframe" type="xl" height="90%" :title="iframeTitle" hide-footer>
+      <iframe :src="iframeUrl" style="width: 100%; height: 99%; border: none" />
+    </Dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { Search } from '@element-plus/icons-vue';
+import { getDroneTree } from '@/api/emergencyCommandMap/Drone';
+interface QueryParams {
+  equipment: string;
+}
+const queryParams = reactive<QueryParams>({
+  equipment: '无人机'
+});
+const getTree = () => {
+  getDroneTree(queryParams).then((res) => {
+    treeData.value = res.data;
+  });
+};
+let showIframe = ref(false);
+let iframeUrl = ref('');
+let iframeTitle = ref('');
+const handleNodeClick = (data, node) => {
+  if (!!data.url && node.isLeaf) {
+    iframeUrl.value = data.url;
+    iframeTitle.value = data.label;
+    showIframe.value = true;
+  }
+};
+const treeData = ref([]);
+onMounted(() => {
+  getTree();
+});
+</script>
+
+<style scoped lang="scss">
+.content {
+  display: flex;
+  height: 260px;
+  .left-content {
+    height: 100%;
+    flex: 1;
+    margin-right: 20px;
+
+    .search-box {
+      display: flex;
+
+      .custom-input {
+        width: 100%;
+      }
+    }
+    :deep(.el-tree) {
+      height: 100%;
+      background-color: transparent;
+      color: #fbffff;
+      font-size: 14px;
+      .el-tree-node__content {
+        height: auto;
+        padding-top: 10px;
+        padding-bottom: 10px;
+        white-space: normal;
+        word-break: break-all;
+      }
+      .el-tree-node__expand-icon {
+        color: #297cfc;
+      }
+      .el-tree-node:focus > .el-tree-node__content,
+      .el-tree-node__content:hover {
+        background-color: transparent !important;
+      }
+    }
+  }
+  .right-content {
+    height: 100%;
+    flex-grow: 1;
+    flex: 1;
+  }
+}
+</style>

+ 101 - 0
src/views/emergencyCommandMap/LeftSection/IndividualEquipment.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="content">
+    <div class="left-content">
+      <div class="search-box">
+        <el-input v-model="queryParams.equipment" class="custom-input" placeholder="搜索设备" @keyup.enter="getTree">
+          <template #prefix>
+            <el-icon class="el-input__icon"><search /></el-icon>
+          </template>
+        </el-input>
+      </div>
+      <div class="tree-container">
+        <div class="tree-box">
+          <div style="overflow-y: auto; height: 100%">
+            <el-tree :data="treeData" accordion @node-click="handleNodeClick" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <Dialog v-if="showIframe" v-model="showIframe"  type="xl" height="90%" :title="iframeTitle" hide-footer>
+      <iframe :src="iframeUrl" style="width: 100%; height: 99%; border: none" />
+    </Dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { Search } from '@element-plus/icons-vue';
+//import { getAvconDeptTree } from '@/api/emergencyCommandMap/communication';
+import { getDroneTree } from '@/api/emergencyCommandMap/Drone';
+interface QueryParams {
+  equipment: string;
+}
+const queryParams = reactive<QueryParams>({
+  equipment: '单兵设备'
+});
+
+const treeData = ref([]);
+
+const getTree = () => {
+  getDroneTree(queryParams).then((res) => {
+    treeData.value = res.data;
+  });
+};
+let showIframe = ref(false);
+let iframeUrl = ref('');
+let iframeTitle = ref('');
+const handleNodeClick = (data, node) => {
+  if (!!data.url && node.isLeaf) {
+    iframeUrl.value = data.url;
+    iframeTitle.value = data.label;
+    showIframe.value = true;
+  }
+};
+onMounted(() => {
+  getTree();
+});
+</script>
+
+<style scoped lang="scss">
+.content {
+  display: flex;
+  height: 260px;
+  .left-content {
+    height: 100%;
+    flex: 1;
+    margin-right: 20px;
+
+    .search-box {
+      display: flex;
+
+      .custom-input {
+        width: 100%;
+      }
+    }
+    :deep(.el-tree) {
+      height: 100%;
+      background-color: transparent;
+      color: #fbffff;
+      font-size: 14px;
+      .el-tree-node__content {
+        height: auto;
+        padding-top: 10px;
+        padding-bottom: 10px;
+        white-space: normal;
+        word-break: break-all;
+      }
+      .el-tree-node__expand-icon {
+        color: #297cfc;
+      }
+      .el-tree-node:focus > .el-tree-node__content,
+      .el-tree-node__content:hover {
+        background-color: transparent !important;
+      }
+    }
+  }
+  .right-content {
+    height: 100%;
+    flex-grow: 1;
+    flex: 1;
+  }
+}
+</style>

+ 1 - 1
src/views/riskPrevention/planManage/index.vue

@@ -96,7 +96,7 @@
               </el-select>
             </el-form-item>
             <el-form-item label="预案附件:" prop="fileList">
-              <file-upload v-model="form.fileList" />
+              <file-upload v-model="form.fileList"  fileSize="15"/>
             </el-form-item>
           </el-form>
         </div>