Hwf 8 mesiacov pred
rodič
commit
38bed77a71

+ 4 - 4
src/api/globalMap/onlinePlotting.ts

@@ -71,7 +71,7 @@ export const endCollaboration = (data) => {
 // 关闭协同
 export const closeCollaboration = (data) => {
   return request({
-    url: '/api/pattern/ws/bz_add',
+    url: '/api/pattern/ws/delete_all_user',
     method: 'put',
     data: data
   });
@@ -87,10 +87,10 @@ export const getBzList = (params) => {
 };
 
 // 添加分组
-export const addGroup = (params) => {
+export const addGroup = (data) => {
   return request({
     url: '/api/pattern/ws/add_group',
-    method: 'get',
-    params: params
+    method: 'post',
+    data: data
   });
 };

+ 253 - 0
src/utils/convergedCommunication.ts

@@ -0,0 +1,253 @@
+const socketUrl = import.meta.env.VITE_COMMUNICATION_WEBSOCKET;
+const base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+const base64DecodeChars = new Array(
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
+
+export class ConvergedCommunication {
+  private retryCount;
+  public socket;
+  public svr;
+  public port;
+  public userid;
+  public openchannelid;
+  // tmtype设置模板样式。
+  public tmtype;
+  // wndid设置在几号窗口接收openchannelid的图像
+  public wndid;
+  // top置顶窗口是否置顶:top="1":窗口置顶,top="0":窗口不置顶。
+  public top;
+  //  x指定窗口的左边的位置。
+  public x;
+  // y指定窗口的顶部的位置。
+  public y;
+  // width指定窗口的宽度。
+  public width;
+  // height指定窗口的高度。
+  public height;
+  // visible窗口是否显示 1显示
+  public visible;
+  // showcaption设置是否显示标题栏 1显示
+  public showcaption;
+
+  // 初始化
+  constructor() {
+    this.svr = '19.152.196.106';
+    this.port = '4222';
+    this.userid = 'Of18NRTvjjpugOx37pezInXhuL0OEksX';
+    this.openchannelid = 'cs0001@xf_00';
+    this.tmtype = '4';
+    this.wndid = '0';
+    this.top = '1';
+    this.x = '20';
+    this.y = '20';
+    this.width = '960';
+    this.height = '540';
+    this.visible = '1';
+    this.showcaption = '1';
+    this.openSocket();
+  }
+
+  openSocket() {
+    if (this.socket != null) {
+      this.socket.close();
+      this.socket = null;
+    }
+
+    this.socket = new WebSocket(socketUrl);
+
+    this.socket.onopen = () => {
+      console.log('onopen');
+    };
+
+    this.socket.onclose = () => {
+      console.log('onclose');
+    };
+
+    this.socket.onmessage = (message) => {
+      console.log('onmessage: ', message);
+    };
+
+    this.socket.onerror = () => {
+      console.log('onerror');
+      this.restartSocket();
+    };
+  }
+  startSocket() {
+    this.openSocket();
+  }
+
+  restartSocket() {
+    this.retryCount++;
+    console.log('第' + this.retryCount + '次重连.');
+    setTimeout(() => {
+      this.openSocket();
+    }, 1000);
+  }
+
+  stopSocket() {
+    if (this.socket != null) {
+      this.socket.close();
+      this.socket = null;
+    }
+  }
+
+  base64encode(str) {
+    let out, i, len;
+    let c1, c2, c3;
+    len = str.length;
+    i = 0;
+    out = '';
+    while (i < len) {
+      c1 = str.charCodeAt(i++) & 0xff;
+      if (i == len) {
+        out += base64EncodeChars.charAt(c1 >> 2);
+        out += base64EncodeChars.charAt((c1 & 0x3) << 4);
+        out += '==';
+        break;
+      }
+      c2 = str.charCodeAt(i++);
+      if (i == len) {
+        out += base64EncodeChars.charAt(c1 >> 2);
+        out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+        out += base64EncodeChars.charAt((c2 & 0xF) << 2);
+        out += '=';
+        break;
+      }
+      c3 = str.charCodeAt(i++);
+      out += base64EncodeChars.charAt(c1 >> 2);
+      out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+      out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
+      out += base64EncodeChars.charAt(c3 & 0x3F);
+    }
+    return out;
+  }
+
+  base64decode(str) {
+    let c1, c2, c3, c4;
+    let i, len, out;
+    len = str.length;
+    i = 0;
+    out = '';
+    while (i < len) {
+      /* c1 */
+      do {
+        c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+      } while (i < len && c1 == -1);
+      if (c1 == -1)
+        break;
+      /* c2 */
+      do {
+        c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+      } while (i < len && c2 == -1);
+      if (c2 == -1)
+        break;
+      out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
+      /* c3 */
+      do {
+        c3 = str.charCodeAt(i++) & 0xff;
+        if (c3 == 61)
+          return out;
+        c3 = base64DecodeChars[c3];
+      } while (i < len && c3 == -1);
+      if (c3 == -1)
+        break;
+      out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
+      /* c4 */
+      do {
+        c4 = str.charCodeAt(i++) & 0xff;
+        if (c4 == 61)
+          return out;
+        c4 = base64DecodeChars[c4];
+      } while (i < len && c4 == -1);
+      if (c4 == -1)
+        break;
+      out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
+    }
+    return out;
+  }
+
+  utf16to8(str) {
+    let out, i, len, c;
+    out = '';
+    len = str.length;
+    for (i = 0; i < len; i++) {
+      c = str.charCodeAt(i);
+      if ((c >= 0x0001) && (c <= 0x007F)) {
+        out += str.charAt(i);
+      } else if (c > 0x07FF) {
+        out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
+        out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
+        out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+      } else {
+        out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
+        out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+      }
+    }
+    return out;
+  }
+
+  utf8to16(str) {
+    const len = str.length;
+    let out, i, c;
+    let char2, char3;
+    out = '';
+    i = 0;
+    while (i < len) {
+      c = str.charCodeAt(i++);
+      switch (c >> 4) {
+        case 0:
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+        case 6:
+        case 7:
+          // 0xxxxxxx
+          out += str.charAt(i - 1);
+          break;
+        case 12:
+        case 13:
+          // 110x xxxx   10xx xxxx
+          char2 = str.charCodeAt(i++);
+          out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
+          break;
+        case 14:
+          // 1110 xxxx  10xx xxxx  10xx xxxx
+          char2 = str.charCodeAt(i++);
+          char3 = str.charCodeAt(i++);
+          out += String.fromCharCode(((c & 0x0F) << 12) |
+            ((char2 & 0x3F) << 6) |
+            ((char3 & 0x3F) << 0));
+          break;
+      }
+    }
+    return out;
+  }
+
+  start() {
+    const str =
+      `-r svr="${this.svr}" port="${this.port}" userid="${this.port}" openchannelid="${this.openchannelid}"` +
+      `tmtype="${this.tmtype}" wndid="${this.wndid}" top="${this.top}" x="${this.x}" y="${this.y}"` +
+      `w="${this.width}" h="${this.height}" visible="${this.visible}" showcaption="${this.showcaption}"`;
+    const val = 'AVCON-OCX://' + this.base64encode(this.utf16to8(str));
+    if (str.match('-r') && str.match('startws="1"')) {
+      setTimeout(() => {
+        this.startSocket();
+      }, 5000);
+    }
+    // 创建一个a标签元素
+    const a = document.createElement('a');
+    // 设置a标签的href属性
+    a.href = val;
+    // 触发点击事件
+    a.click();
+  }
+}

+ 445 - 0
src/views/emergencyCommandMap/RightSection/InitConsultation.vue

@@ -0,0 +1,445 @@
+<template>
+  <Dialog custom-show type="lg" title="指挥体系" hide-footer @close="handleClose">
+    <div class="content">
+      <div class="left-content">
+        <el-input v-model="queryParams.keyword" class="custom-input" placeholder="组织架构搜索">
+          <template #prefix>
+            <el-icon class="el-input__icon"><search /></el-icon>
+          </template>
+        </el-input>
+        <div class="tree-container">
+          <div class="tree-box">
+            <el-tree :data="treeData" accordion @node-click="handleNodeClick" />
+          </div>
+        </div>
+      </div>
+      <div class="middle-content">
+        <div class="search-box">
+          <el-select
+            v-model="queryParams.value1"
+            class="custom-select select-box"
+            placeholder="全部"
+            popper-class="custom-select-popper"
+            :teleported="false"
+          >
+            <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="组织架构搜索">
+            <template #prefix>
+              <el-icon class="el-input__icon"><search /></el-icon>
+            </template>
+          </el-input>
+        </div>
+        <div class="user-box">
+          <div class="user-table">
+            <div class="tr">
+              <div class="td2">
+                <div :class="getCheckedClass()" @click="handleChecked"></div>
+              </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">
+                <div class="td2">
+                  <div :class="item.checked ? 'common-checked-active' : 'common-checked'" @click="handleChecked2(item)"></div>
+                </div>
+                <div class="td">{{ item.name }}</div>
+                <div class="td3">
+                  {{ item.duty }}
+                  <div class="phone-icon"></div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="select-box2">
+        <div class="select-header">
+          <div class="left-item">
+            <div>已选择:</div>
+            <div class="text">{{ selectList.length }}</div>
+            <div>人</div>
+          </div>
+          <div class="clear-btn" @click="clearSelect">清空</div>
+        </div>
+        <div class="select-content">
+          <div v-for="(item, index) in selectList" :key="index" class="box-item">
+            <div class="line">
+              <div class="text1">{{ item.name }}</div>
+              <div class="text2">{{ item.duty }}</div>
+            </div>
+            <div class="line" style="margin-top: 20px">
+              <div class="text2">{{ item.dept }}</div>
+            </div>
+            <div class="close-btn" @click="deleteItem(item)"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="footer">
+      <div></div>
+      <div class="btn-box">
+        <div class="common-btn-primary2" style="margin-right: 20px" @click="handlePhoneCall">电话呼叫</div>
+        <div class="common-btn-primary2" @click="handleStartMeeting">发起会议</div>
+      </div>
+    </div>
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import { Search } from '@element-plus/icons-vue';
+import { ConvergedCommunication } from '@/utils/convergedCommunication';
+
+const props = defineProps({
+  modelValue: Boolean
+});
+const emits = defineEmits(['update:modelValue', 'close']);
+let activeIndex = ref(0);
+const options = ref([{ name: '全部', value: '全部' }]);
+const queryParams = ref({
+  value1: '',
+  keyword: ''
+});
+const menu = ref([{ name: '视频会商' }, { name: '无人机' }, { name: '单兵设备' }]);
+const treeData = ref([
+  {
+    label: '茂名市',
+    children: [
+      {
+        label: '茂名市委',
+        children: [
+          {
+            label: '茂名市纪委监委',
+            isLeaf: true
+          },
+          {
+            label: '茂名市委办公室',
+            isLeaf: true
+          },
+          {
+            label: '茂名市委组织部',
+            isLeaf: true
+          }
+        ]
+      },
+      {
+        label: '茂名市人大',
+        children: [
+          {
+            label: '茂名市人大常委会秘书长、副秘书长',
+            isLeaf: true
+          },
+          {
+            label: '茂名市人大常委会办公室',
+            isLeaf: true
+          },
+          {
+            label: '茂名市人大常委会研究室',
+            isLeaf: true
+          }
+        ]
+      }
+    ]
+  }
+]);
+const userList = ref([]);
+const selectList = computed(() => {
+  const data = [];
+  userList.value.forEach((item) => {
+    if (item.checked) {
+      data.push(item);
+    }
+  });
+  return data;
+});
+const getCheckedClass = () => {
+  let res = 'common-checked';
+  const len = userList.value.length;
+  const len2 = selectList.value.length;
+  if (len2 > 0 && len2 === len) {
+    res = 'common-checked-active';
+  } else if (len2 > 0) {
+    res = 'common-checked-half';
+  }
+  return res;
+};
+const handleNodeClick = (item) => {
+  if (item.isLeaf) {
+    const data = [
+      {
+        id: 1,
+        name: '李莉莉',
+        duty: '副主任',
+        dept: '茂名市/茂名市政府/茂名应急管理局'
+      },
+      {
+        id: 2,
+        name: '何里',
+        duty: '副主任',
+        dept: '茂名市/茂名市政府/茂名应急管理局'
+      },
+      {
+        id: 3,
+        name: '张三',
+        duty: '副主任',
+        dept: '茂名市/茂名市政府/茂名应急管理局'
+      }
+    ];
+    data.forEach((item) => {
+      item.checked = false;
+    });
+    userList.value = data;
+  }
+};
+// 全选、全取消
+const handleChecked = () => {
+  const checkedClass = getCheckedClass();
+  let flag = true;
+  if (checkedClass === 'common-checked-active') {
+    flag = false;
+  }
+  userList.value.forEach((item) => {
+    item.checked = flag;
+  });
+};
+// 单个选中、取消选中
+const handleChecked2 = (item) => {
+  item.checked = !item.checked;
+};
+// 清空
+const clearSelect = () => {
+  userList.value.forEach((item) => {
+    if (item.checked) {
+      item.checked = false;
+    }
+  });
+};
+// 清空指定项
+const deleteItem = (item) => {
+  for (let i = 0; i < userList.value.length; i++) {
+    if (item.id === userList.value[i].id) {
+      userList.value[i].checked = false;
+      break;
+    }
+  }
+};
+
+// 弹窗关闭后
+const handleClose = () => {
+  emits('update:modelValue', false);
+};
+
+// 融合通信参数
+const handlePhoneCall = () => {
+  emits('update:modelValue', false);
+}
+// 发起会议
+const handleStartMeeting = () => {
+  const convergedCommunication = new ConvergedCommunication();
+  convergedCommunication.start();
+  emits('update:modelValue', false);
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  display: flex;
+  margin-top: 12px;
+  .left-content {
+    width: 350px;
+    height: 560px;
+    overflow-y: auto;
+    padding-right: 10px;
+    border-right: 1px solid #2187ff;
+  }
+  .middle-content {
+    width: 350px;
+    height: 560px;
+    padding: 0 10px;
+    border-right: 1px solid #2187ff;
+    .search-box {
+      height: 32px;
+      display: flex;
+      .select-box {
+        flex-shrink: 0;
+        width: 176px !important;
+        height: 56px;
+        line-height: 56px;
+        margin-right: 10px;
+        color: #83a3be;
+        font-size: 14px;
+      }
+    }
+    .user-box {
+      width: 100%;
+      .user-table {
+        padding: 15px 0;
+        display: flex;
+        flex-direction: column;
+        font-size: 14px;
+        color: #fbffff;
+        .tr {
+          min-height: 32px;
+          background-color: #102e76;
+        }
+        .tr,
+        .tr2 {
+          display: flex;
+          padding: 5px 0;
+          .td {
+            flex: 1;
+            display: flex;
+            align-items: center;
+          }
+          .td2 {
+            width: 65px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+          .td3 {
+            flex: 2;
+            display: flex;
+            align-items: center;
+          }
+        }
+        .table-content {
+          height: 300px;
+          overflow-y: auto;
+        }
+        .tr2 {
+          margin-top: 8px;
+          background-color: #122868;
+        }
+        .phone-icon {
+          width: 31px;
+          height: 31.5px;
+          background: url('@/assets/images/emergencyCommandMap/communication/phone.png');
+          background-size: 100% 100%;
+          cursor: pointer;
+        }
+      }
+    }
+  }
+
+  .custom-select-popper {
+    .el-scrollbar {
+      .el-select-dropdown__item {
+        color: #b1cae0;
+        font-size: 14px;
+        height: 56px;
+        line-height: 56px;
+      }
+    }
+  }
+  .input {
+    background: transparent;
+    color: #83a3be;
+    font-size: 14px;
+    outline: none;
+    appearance: none;
+    height: 100%;
+    border: none;
+    &::placeholder {
+      color: #83a3be;
+    }
+  }
+  .tree-container {
+    display: flex;
+    .tree-box {
+      width: 100%;
+      height: 528px;
+      overflow-y: auto;
+      padding: 15px 8px;
+      :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;
+          font-size: 7px;
+        }
+        .el-tree-node:focus > .el-tree-node__content,
+        .el-tree-node__content:hover {
+          background-color: transparent !important;
+        }
+      }
+    }
+  }
+  .select-box2 {
+    margin-left: 30px;
+    width: 400px;
+    height: 100%;
+    .select-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      color: #fbffff;
+      font-size: 14px;
+      border-bottom: 1px solid #247dff;
+      padding: 20px;
+      .left-item {
+        display: flex;
+        align-items: center;
+        .text {
+          margin: 0 10px;
+          color: #00e8ff;
+          font-family: 'BEBAS-1';
+        }
+      }
+      .clear-btn {
+        color: #00e8ff;
+        cursor: pointer;
+      }
+    }
+    .select-content {
+      height: 450px;
+      overflow-y: auto;
+      .box-item {
+        border-bottom: 1px solid #247dff;
+        padding: 7px;
+        position: relative;
+        .line {
+          color: #fff;
+          font-size: 14px;
+          display: flex;
+          .text1 {
+            margin-right: 35px;
+          }
+          .text2 {
+            color: #a7ccdf;
+          }
+        }
+        .close-btn {
+          position: absolute;
+          right: 10px;
+          top: 50px;
+          cursor: pointer;
+          width: 9px;
+          height: 9px;
+          background: url('@/assets/images/emergencyCommandMap/communication/close.png') no-repeat;
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
+}
+.footer {
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .btn-box {
+    display: flex;
+  }
+}
+</style>

+ 22 - 2
src/views/emergencyCommandMap/RightSection/RenWuDengJi.vue

@@ -45,7 +45,20 @@
             <el-input v-model="newTask.contact_phone" class="custom-input2" placeholder="发布人的联系电话" />
           </el-form-item>
           <el-form-item label="完成时限" label-width="60px">
-            <el-date-picker v-model="newTask.complete_time" type="date" class="common-date-picker" popper-class="common-date-popper" placeholder="选择完成时间" style="width: 100%" />
+            <el-date-picker
+              v-model="newTask.complete_time"
+              type="date"
+              class="common-date-picker"
+              popper-class="common-date-popper"
+              placeholder="选择完成时间"
+              style="width: 100%"
+            />
+          </el-form-item>
+          <el-form-item label="上传图片" label-width="60px">
+            <FileUpload v-model="newTask.imgList" :file-type="['jpg', 'jpeg', 'png']" :limit="9" :file-size="5" class="upload-box" />
+          </el-form-item>
+          <el-form-item label="上传文件" label-width="60px">
+            <FileUpload v-model="newTask.fileList" :file-type="['mp4', 'avi', 'wmv']" :limit="6" :file-size="1024" class="upload-box" />
           </el-form-item>
         </el-form>
       </div>
@@ -64,7 +77,9 @@ const newTask = reactive({
   unit_name: '',
   registrar: '',
   contact_phone: '',
-  complete_time: ''
+  complete_time: '',
+  imgList: [],
+  fileList: []
 });
 const units = ref([]);
 
@@ -137,5 +152,10 @@ onMounted(() => {
       margin-right: 5px !important;
     }
   }
+  .upload-box {
+    :deep(.el-upload__tip) {
+      color: #ffffff !important;
+    }
+  }
 }
 </style>

+ 29 - 9
src/views/emergencyCommandMap/RightSection/RenWuGenZong.vue

@@ -12,22 +12,34 @@
             <div :class="item.processing_status === '已完成' ? 'success-bg' : item.processing_status === '已完成' ? 'primary-bg' : 'processing-bg'">
               {{ item.processing_status }}
             </div>
-            <div class="btn" @click="openUpdateDialog(item)">更新</div>
+            <div class="btn" @click="handleLeaderInstruction(item)">领导批示</div>
+            <div class="btn" @click="openUpdateDialog(item)">任务反馈</div>
+            <div class="box-title">
+              <template v-if="item.isExpanded">
+                <div class="btn" @click="toggleExpand(item)">收起</div>
+              </template>
+              <template v-else>
+                <div class="btn" @click="toggleExpand(item)">展开</div>
+              </template>
+            </div>
           </div>
         </div>
         <div class="box-content">{{ item.task_description }}</div>
+        <div v-if="item.isExpanded" class="box3">
+          <div class="box-header">
+            <div class="box-title">任务反馈</div>
+            <div class="time">{{ item.update_time }}</div>
+            <div class="people">{{ item.registrar }}</div>
+          </div>
+          <div class="box-title">{{ item.feedback_content }}</div>
+          <div class="time">附件</div>
+        </div>
       </div>
     </div>
   </div>
   <div class="more" @click="showMoreEventManageList">查看更多</div>
 
-  <RenWuGengXin
-    v-model="newSectionState.showListDialog"
-    :task="selectedTask"
-    :event-id="eventId"
-    title="任务进度更新"
-    @update-success="handleUpdateSuccess"
-  />
+  <RenWuGengXin v-model="newSectionState.showListDialog" :task="selectedTask" :event-id="eventId" @update-success="handleUpdateSuccess" />
 
   <!--弹窗-->
   <RenWuGenZongInfo v-if="eventManageState.showListDialog" v-model="eventManageState.showListDialog" :event-id="eventId" />
@@ -92,6 +104,14 @@ const handleUpdateSuccess = (updatedData) => {
   console.log('任务进度更新成功:', updatedData);
   fetchData(); // 重新加载任务列表
 };
+const handleLeaderInstruction = (item) => {
+  // 处理领导批示逻辑
+  console.log('领导批示:', item);
+};
+
+const toggleExpand = (item) => {
+  item.isExpanded = !item.isExpanded;
+};
 
 const toggleScroll = () => {
   showScroll.value = !showScroll.value;
@@ -207,7 +227,7 @@ watch(
       color: #ffffff;
       border-radius: 11px;
       text-align: center;
-      margin-left: 30px;
+      margin-left: 5px;
     }
     .btn {
       padding: 0 7px;

+ 4 - 4
src/views/emergencyCommandMap/RightSection/RenWuGenZongInfo.vue

@@ -306,10 +306,10 @@ const closeDialog = () => {
 .success-bg,
 .processing-bg,
 .btn {
-  width: 154px;
-  height: 56px;
-  line-height: 56px;
-  font-size: 32px;
+  width: 80px;
+  height: 30px;
+  line-height: 30px;
+  font-size: 16px;
   color: #ffffff;
   border-radius: 10px;
   text-align: center;

+ 78 - 7
src/views/emergencyCommandMap/RightSection/RenWuGengXin.vue

@@ -1,23 +1,56 @@
 <template>
-  <Dialog v-model="visible" type="sm" title="任务进度更新" height="auto" hide-footer>
+  <Dialog v-model="visible" type="sm" title="任务反馈" class="box1" height="650px" hide-footer>
     <div class="dialog-content">
       <div class="dialog-body">
         <el-form ref="taskForm" :model="newTask">
           <el-row :gutter="20">
             <el-col :span="24">
               <el-form-item label="任务描述:">
-                <!--                <el-input v-model="newTask.task_description" class="custom-input2" readonly />-->
                 <div class="display-field">{{ newTask.task_description }}</div>
               </el-form-item>
             </el-col>
+            <el-col :span="24">
+              <el-form-item label="任务类型:">
+                <div class="display-field">{{ get_task_type_text(newTask.task_type) }}</div>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="执行人:">
+                <div class="display-field">{{ newTask.registrar }}</div>
+              </el-form-item>
+            </el-col>
             <el-col :span="24">
               <el-form-item label="执行单位:">
-                <!--                <el-input v-model="newTask.unit_name" class="custom-input2" readonly />-->
                 <div class="display-field">{{ newTask.unit_name }}</div>
               </el-form-item>
             </el-col>
             <el-col :span="24">
-              <el-form-item label="完成进度">
+              <el-form-item label="预估完成时间:">
+                <div class="display-field">{{ newTask.complete_time }}</div>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="图片:">
+                <div class="display-field">{{ newTask.complete_time }}</div>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="文件:">
+                <div class="display-field">{{ newTask.complete_time }}</div>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="反馈人:">
+                <div class="display-field">{{ newTask.registrar }}</div>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="反馈内容:">
+                <el-input v-model="newTask.feedback_content" class="custom-input2" clearable placeholder="请输入反馈内容"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="完成进度:">
                 <el-select
                   v-model="newTask.processing_status"
                   class="custom-select"
@@ -29,10 +62,32 @@
                 </el-select>
               </el-form-item>
             </el-col>
+            <el-col :span="24">
+              <el-form-item label="上传图片:">
+                <!--                <el-upload-->
+                <!--                  v-model="newTask.attachments"-->
+                <!--                  action="/uploadAttachments"-->
+                <!--                  list-type="picture-card"-->
+                <!--                  :teleported="false"-->
+                <!--                  auto-upload="true"-->
+                <!--                  limit="9"-->
+                <!--                  :teleported="false"-->
+                <!--                  popper-class="custom-uploadper"-->
+                <!--                  class="custom-upload"-->
+                <!--                  :teleported="false"-->
+                <!--                  clearable-->
+                <!--                  filterable-->
+                <!--                >-->
+                <!--                  <el-upload-list-item v-for="file in newTask.attachments" :key="file.name">-->
+                <!--                    <el-icon name="el-icon-upload" class="upload-icon"></el-icon>-->
+                <!--                    <el-button type="danger" size="mini" @click="removeAttachment(file)">移除</el-button>-->
+                <!--                  </el-upload-list-item>-->
+                <!--                </el-upload>-->
+              </el-form-item>
+            </el-col>
           </el-row>
         </el-form>
       </div>
-
       <div class="footer">
         <div class="flex">
           <div class="common-btn-primary" :disabled="!isFormValid" @click="confirmRegister">确定</div>
@@ -57,6 +112,18 @@ const emit = defineEmits(['update:modelValue', 'update-success']);
 const newTask = reactive({ ...props.task });
 const units = ref(['待处理', '已完成']);
 
+const opt_task_type = [
+  { text: "全部", value: "" },
+  { text: "事件处置", value: "0" },
+  { text: "防范措施", value: "1" },
+  { text: "险情处理", value: "2" },
+  { text: "督办任务", value: "3" }
+];
+
+const get_task_type_text = (val) => {
+  return opt_task_type.find(item => item.value == val).text
+}
+
 const visible = computed({
   get: () => props.modelValue,
   set: (val) => {
@@ -113,10 +180,14 @@ watch(
 
 .dialog-body {
   padding: 20px;
+  :deep(.el-form-item__label) {
+    font-size: 16px !important;
+    color: #ffffff !important;
+  }
 }
 /* 新增样式,用于显示字段 */
 .display-field {
-  padding: 6px 12px;
+  padding: 0 12px;
   //border: 1px solid #dcdfe6; /* 确保边框颜色与输入框一致 */
   border-radius: 4px;
   //background-color: #f5f7fa;
@@ -124,7 +195,7 @@ watch(
   background-color: transparent;
   min-height: 32px;
   line-height: 32px;
-  font-size: 40px; /* 确保字体大小与输入框一致 */
+  font-size: 16px; /* 确保字体大小与输入框一致 */
   color: #ffffff; /* 确保字体颜色与输入框一致 */
   //字体加粗
   //font-weight: bold;

+ 21 - 18
src/views/emergencyCommandMap/RightSection/RightTop.vue

@@ -40,8 +40,7 @@
 
       <!-- 资源调度部分保持不变 -->
       <div v-else-if="activeTab === '资源调度'" class="resource-scheduling">
-        <!-- 资源调度内容,可以根据需要进行填充 -->
-        <p>资源调度内容正在开发中...</p>
+        <div class="tip">暂无数据</div>
       </div>
     </div>
   </div>
@@ -172,9 +171,10 @@ watch(
 
       .success-icon,
       .error-icon {
-        margin-right: -50px;
-        width: 123px;
-        height: 79px;
+        margin-right: -20px;
+        width: 40px;
+        min-width: 40px;
+        height: 26px;
         background-repeat: no-repeat;
         background-size: contain;
       }
@@ -189,12 +189,13 @@ watch(
 
       .box2 {
         flex: 1;
+        min-height: 85px;
         background-image: url('@/assets/images/taskTracking/box1.png');
         background-repeat: no-repeat;
-        background-size: 1642px 377px;
+        background-size: 100% 100%;
         background-position: bottom left;
-        padding: 20px 20px 20px 60px;
-        margin-top: 20px;
+        padding: 10px 10px 10px 20px;
+        margin-top: 15px;
         position: relative;
         &:first-child {
           margin-top: 0;
@@ -211,27 +212,29 @@ watch(
           }
 
           .box-title {
-            font-size: 44px;
+            font-size: 16px;
             font-family: YouSheBiaoTiHei;
             color: #ffffff;
             background-image: url('@/assets/images/taskTracking/titleBox.png');
             background-repeat: no-repeat;
-            background-size: 311px 56px;
+            background-size: 102px 18px;
             background-position: bottom left;
-            padding-left: 50px;
+            padding-left: 20px;
           }
 
           .time {
-            font-size: 32px;
+            font-size: 12px;
             color: #00e8ff;
-            margin-left: 70px;
+            margin-left: 10px;
           }
 
           .status {
-            width: 354px;
-            height: 56px;
-            line-height: 56px;
-            font-size: 32px;
+            min-width: 100px;
+            padding: 0 10px;
+            white-space: nowrap;
+            height: 20px;
+            line-height: 20px;
+            font-size: 12px;
             color: #ffffff;
             border-radius: 10px;
             text-align: center;
@@ -270,7 +273,7 @@ watch(
   .card-content {
     display: flex;
     flex-wrap: wrap;
-    padding: 0 0 0 20px;
+    padding: 0 0 10px 20px;
     width: 100%;
   }
 }

+ 15 - 3
src/views/emergencyCommandMap/RightSection/TaskDelivery.vue

@@ -10,8 +10,12 @@
         </template>
         <template #default>
           <div v-for="(task, index) in tasks" :key="index" class="task-item">
-            <p class="unit-name">{{ task.dept_name }}</p>
+            <div class="task-header">
+              <p class="unit-name">{{ task.dept_name }}</p>
+              <div class="common-btn-primary2" @click="handleShowApprove">领导批示</div>
+            </div>
             <p class="task-description">{{ task.content }}</p>
+            <div></div>
           </div>
         </template>
       </el-skeleton>
@@ -80,7 +84,7 @@ const sendTasks = () => {
     ElMessage.error('事件ID未定义,无法发送任务!');
     return;
   }
-  
+
   sendTask({ eventId: props.eventId })
     .then(() => {
       ElMessage.success('任务已成功发送!');
@@ -88,7 +92,10 @@ const sendTasks = () => {
     })
     .catch(() => {
       //ElMessage.error('发送任务失败,请稍后再试!');
-    })
+    });
+};
+const handleShowApprove = (item) => {
+  item.show = true;
 };
 </script>
 
@@ -105,6 +112,11 @@ const sendTasks = () => {
   background-color: rgba(255, 255, 255, 0.1);
   border: 1px solid rgba(255, 255, 255, 0.3);
   border-radius: 4px;
+  .task-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
 }
 
 .unit-name {

+ 15 - 10
src/views/emergencyCommandMap/RightSection/index.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="right-section">
     <div class="task-header">
-      <div class="task-item">
+      <div class="task-item" @click="handleShowInitConsultation">
         <div class="icon">
           <div class="icon1"></div>
         </div>
-        <div class="task-text gradient-text">发起会商</div>
+        <div class="task-text gradient-text">指挥体系</div>
       </div>
       <div class="task-item" @click="startPlan">
         <div class="icon">
@@ -35,6 +35,7 @@
     <RightTop :event-id="eventId" />
     <PublicOpinionSupervision />
     <JointDuty />
+    <InitConsultation v-if="showInitConsultation" v-model="showInitConsultation" />
     <StartPlan v-if="startPlanState.show" v-model:show="startPlanState.show" :title="startPlanState.title" :event-id="startPlanState.eventId" />
     <SelectPlan v-model="selectPlanState.show" :title="selectPlanState.title" :temp-event-id="eventId" @update-plan="updatePlan" />
     <RenWuDengJi v-if="renWuDengJiState.show" v-model:show="renWuDengJiState.show" :event-id="eventId" />
@@ -49,6 +50,7 @@ import JointDuty from '@/views/emergencyCommandMap/RightSection/JointDuty.vue';
 import RightTop from '@/views/emergencyCommandMap/RightSection/RightTop.vue';
 import StartPlan from './StartPlan.vue';
 import SelectPlan from './SelectPlan.vue';
+import InitConsultation from './InitConsultation.vue';
 import PublicOpinionSupervision from '@/views/emergencyCommandMap/RightSection/PublicOpinionSupervision.vue';
 import RenWuDengJi from '@/views/emergencyCommandMap/RightSection/RenWuDengJi.vue';
 
@@ -89,10 +91,10 @@ const getEventIdFromUrl = () => {
 */
 
 // 更新 startPlan 方法
-const startPlan = async() => {
-  const response = await getEventDetail({ event_id: eventId.value })
-  const event_data = (response.data);
-  if(event_data.emergency_notify_count > 0) {
+const startPlan = async () => {
+  const response = await getEventDetail({ event_id: eventId.value });
+  const event_data = response.data;
+  if (event_data.emergency_notify_count > 0) {
     ElMessage.warning('预案已启动');
     return;
   }
@@ -106,7 +108,7 @@ const startPlan = async() => {
     nextTick().then(() => {
       console.log('DOM updated, showing StartPlan:', startPlanState.show);
     });
-  } else if(event_data.del_flag === '9') {
+  } else if (event_data.del_flag === '9') {
     selectPlanState.title = '预案任务下发';
     selectPlanState.tempEventId = eventId.value;
     selectPlanState.show = true;
@@ -124,10 +126,13 @@ const loadPlans = () => {
     { id: 3, name: '应急预案C' }
   ];
 };
-
+let showInitConsultation = ref(false);
+const handleShowInitConsultation = () => {
+  showInitConsultation.value = true;
+};
 // 初始化数据
 const initData = () => {
-  eventId.value = route.query.event_id as string || '';
+  eventId.value = (route.query.event_id as string) || '';
   // getEventIdFromUrl();
   console.log('URL中的eventId:', route.query.event_id);
 };
@@ -169,7 +174,7 @@ const updatePlan = (data: { eventId: string }) => {
   flex-direction: column;
   justify-content: space-between;
   font-size: 14px;
-  padding-top: 57px;
+  padding-top: 0;
   padding-bottom: 150px;
   .task-header {
     display: flex;

+ 4 - 0
src/views/globalMap/RightMenu/OnlinePlotting/LayerDetail.vue

@@ -88,6 +88,10 @@ const handleShowGroup = () => {
   showGroup.value = true;
 };
 const handleAddGroup = () => {
+  const obj = {
+    group_name: groupForm.value.name,
+    pattern_id: props.patternId
+  }
   addGroup(groupForm.value).then((res) => {
     emits('update:modelValue', false);
   });

+ 4 - 3
src/views/globalMap/RightMenu/OnlinePlotting/index.vue

@@ -157,7 +157,7 @@ import {
   getPatternInfo,
   getPatternList,
   createCollaboration,
-  closeCollaboration
+  closeCollaboration, startCollaboration
 } from '@/api/globalMap/onlinePlotting';
 import TextEdit from '@/views/globalMap/RightMenu/OnlinePlotting/TextEdit.vue';
 import EditDialog from '@/views/globalMap/RightMenu/OnlinePlotting/EditDialog.vue';
@@ -698,7 +698,7 @@ const handleCloseShare = () => {
   shareState.id = '';
 };
 const handleCloseCollaboration = () => {
-  closeCollaboration({pattern_id: patternId.value}).then(() => {
+  closeCollaboration({ pattern_id: patternId.value }).then(() => {
     collaboration.value = false;
   });
 };
@@ -736,7 +736,8 @@ const handleShareConfirm = (data) => {
 };
 const handleSendForm = () => {
   userWebsocket.init();
-  createCollaboration(form.value);
+  // createCollaboration(form.value);
+  startCollaboration()
   patternId.value = form.value.pattern_id;
   collaboration.value = true;
 };