Sfoglia il codice sorgente

Merge branch 'lkh_chaoqibihuan' into 'all-v4'

feat: 初步完成所有功能,还差抄送人接口等待后端修改

See merge request skyeye/skyeye_frontend/skyeye-admin!303
Fei Liu 1 anno fa
parent
commit
7edda82326

+ 52 - 0
src/api/message/question-notifications.ts

@@ -0,0 +1,52 @@
+import { http } from '@/utils/http/axios';
+import { issueDetilasType } from '@/views/message/systemNotifications/type.ts';
+
+// 修改推送文案
+export function modifyContent(content: string, id: number) {
+  return http.request({
+    url: `/issueProcessMessage/modifyContent?content=${content}&id=${id}`,
+    method: 'post',
+  });
+}
+
+// 下发车间
+export function modifyWorkshopList(workshopList: number[]) {
+  return http.request({
+    url: '/issueProcessMessage/modifyWorkshopList',
+    method: 'post',
+    params: { workshopList },
+  });
+}
+
+// 获取推送范围
+export function getPushRange() {
+  return http.request({
+    url: '/issueProcessMessage/queryPushRange',
+    method: 'get',
+  });
+}
+
+// 修改推送范围
+export function updatePushRange(data: { cameraIdList: number[] }) {
+  return http.request({
+    url: '/issueProcessMessage/updatePushRange',
+    method: 'PUT',
+    data,
+  });
+}
+
+// 查询问题处理通知
+export function getIssueProcessMessage() {
+  return http.request({
+    url: '/issueProcessMessage/queryIssueProcessMessage',
+    method: 'get',
+  });
+}
+// 修改问题处理通知配置
+export function updateIssueProcessMessage(data: issueDetilasType) {
+  return http.request({
+    url: '/issueProcessMessage/updateMessageConfig',
+    method: 'PUT',
+    data,
+  });
+}

+ 233 - 0
src/views/message/questionNotifications/components/ManageContent.vue

@@ -0,0 +1,233 @@
+<template>
+  <div class="manage_content">
+    <div class="content_row" style="margin-bottom: 32px">
+      <ContentPanel
+        :savedData="savedData"
+        :issue-type="QuestionStatus.toAuth"
+        :title="'审核'"
+        :save-update="saveUpdate"
+      />
+      <ContentPanel
+        :savedData="savedData"
+        :issue-type="QuestionStatus.todo"
+        :title="'整改'"
+        :save-update="saveUpdate"
+      />
+    </div>
+    <div class="content_row">
+      <ContentPanel
+        :savedData="savedData"
+        :issue-type="QuestionStatus.toReview"
+        :title="'复核'"
+        :save-update="saveUpdate"
+      />
+      <ContentPanel
+        :savedData="savedData"
+        :issue-type="QuestionStatus.finishe"
+        :title="'完结'"
+        :save-update="saveUpdate"
+      />
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref, onMounted } from 'vue';
+
+  import { ElMessage } from 'element-plus';
+  import {
+    getIssueProcessMessage,
+    updateIssueProcessMessage,
+  } from '@/api/message/question-notifications';
+  import {
+    QuestionStatus,
+    PushTypeStatus,
+    issueDetilasType,
+    issueProcessType,
+    panelDetails,
+  } from '../type';
+  import ContentPanel from './contentPanel.vue';
+
+  //根据流程的issuePhase值与pushType值去匹配id
+  const idMatch = (issuePhase: number, pushType: number) => {
+    let id = 0;
+    savedData.value.forEach((item) => {
+      if (item.issuePhase === issuePhase) {
+        item.issueProcessMessageList.forEach((subItem) => {
+          if (subItem.pushType === pushType) {
+            id = subItem.id;
+          }
+        });
+      }
+    });
+    return id;
+  };
+  const saveUpdate = (obj: panelDetails, panelChosen: number) => {
+    let dataUpdate = {
+      issuePhase: panelChosen,
+      issueProcessMessageList: <issueProcessType[]>[],
+    };
+    dataUpdate.issueProcessMessageList.push({
+      id: idMatch(panelChosen, PushTypeStatus.atonce),
+      isEnabled: obj.atProcessing === true ? 1 : 0,
+      pushType: PushTypeStatus.atonce,
+    });
+    //完结状态下面的没有
+    if (panelChosen != QuestionStatus.finishe) {
+      //如果为开启,那超时时长也不用传了
+      if (obj.atExpiry === false) {
+        dataUpdate.issueProcessMessageList.push({
+          id: idMatch(panelChosen, PushTypeStatus.expire),
+          isEnabled: 0,
+          pushType: PushTypeStatus.expire,
+        });
+      } else {
+        dataUpdate.issueProcessMessageList.push({
+          id: idMatch(panelChosen, PushTypeStatus.expire),
+          isEnabled: 1,
+          overtime: obj.expiryTime!,
+          pushType: PushTypeStatus.expire,
+        });
+      }
+      //这边同理
+      if (obj.atLongTimeExpiry === false) {
+        dataUpdate.issueProcessMessageList.push({
+          id: idMatch(panelChosen, PushTypeStatus.longtime),
+          isEnabled: 0,
+          pushType: PushTypeStatus.longtime,
+        });
+      } else {
+        dataUpdate.issueProcessMessageList.push({
+          id: idMatch(panelChosen, PushTypeStatus.longtime),
+          isEnabled: 1,
+          overtime: obj.longTimeValue!,
+          pushType: PushTypeStatus.longtime,
+          //只传obj.copyTo中的id
+          ccRecipients: obj.copyTo!.map((user) => ({
+            id: user.id,
+            realname: user.realname,
+            staffNo: user.staffNo,
+          })),
+        });
+      }
+    }
+    updateIssueProcessMessage(dataUpdate)
+      .then(() => {
+        ElMessage({
+          message: '保存成功',
+          type: 'success',
+          plain: true,
+        });
+        getProcessDetails();
+      })
+      .catch((error) => {
+        console.error(error);
+      });
+  };
+
+  const savedData = ref(<issueDetilasType[]>[]);
+  const getProcessDetails = () => {
+    // savedData.value = [];
+    getIssueProcessMessage().then((res) => {
+      savedData.value = res;
+    });
+  };
+
+  onMounted(() => {
+    getProcessDetails();
+  });
+</script>
+<style lang="scss" scoped>
+  .manage_content {
+    width: 100%;
+    // height: 300px;
+    // overflow-x: scroll;
+    // background-color: yellowgreen;
+  }
+  .content_row {
+    width: 1140px;
+    display: flex;
+    flex-direction: row;
+    // background-color: gray;
+  }
+
+  .content_panel {
+    width: 530px;
+    height: 326px;
+    // background-color: pink;
+    border-radius: 8px;
+    border: 1px solid #e4e7ed;
+    .panel_top {
+      width: 100%;
+      height: 50px;
+      display: flex;
+      flex-direction: row;
+      background-color: #fafafa;
+      border-radius: 8px;
+      padding: 13px 16px 0px 16px;
+      .top_content {
+        // width: 100%;
+        height: 24px;
+        display: flex;
+        flex-direction: row;
+        .circle {
+          width: 24px;
+          height: 24px;
+          border-radius: 12px;
+          background: #1890ff;
+          font-weight: 600;
+          font-size: 14px;
+          color: #ffffff;
+          line-height: 24px;
+          text-align: center;
+        }
+        .title_name {
+          height: 24px;
+          margin-left: 8px;
+          font-weight: 600;
+          font-size: 16px;
+          color: rgba(0, 0, 0, 0.88);
+          line-height: 24px;
+          text-align: left;
+        }
+        .title_explain {
+          height: 14px;
+          margin-left: 8px;
+          margin-top: 6px;
+          font-weight: 400;
+          font-size: 10px;
+          color: #999999;
+          line-height: 14px;
+          text-align: left;
+        }
+      }
+    }
+    .subtitle {
+      height: 17px;
+      margin-left: 30px;
+      margin-top: 17px;
+      font-weight: 600;
+      font-size: 12px;
+      color: #303133;
+      line-height: 17px;
+      text-align: left;
+      display: flex;
+      flex-direction: row;
+    }
+    .subtitle_explain_disable {
+      height: 21px;
+      margin-left: 30px;
+      // margin-top: 4px;
+      // margin-top: 3px;
+      font-weight: 400;
+      font-size: 10px;
+      color: #a8abb2;
+      line-height: 21px;
+      text-align: left;
+      display: flex;
+      flex-direction: row;
+    }
+    .editable {
+      color: #303133 !important;
+    }
+  }
+</style>

+ 60 - 26
src/views/message/systemNotifications/components/WorkShopTree.vue

@@ -5,14 +5,14 @@
         <el-input
           v-model="queryStr"
           :style="{ width: '300px', height: '30px' }"
-          placeholder="请输入搜索内容"
+          placeholder="请输入相机名称/设备ID"
           :prefix-icon="Search"
+          @change="getCameraData({ queryString: queryStr })"
           clearable
       /></el-form-item>
       <el-tree
         ref="treeRef"
-        v-loading="loading"
-        :data="nodeData"
+        :data="cameraTreeTemp"
         show-checkbox
         node-key="code"
         :props="defaultProps"
@@ -62,12 +62,50 @@
   import { onMounted, ref, watch, computed } from 'vue';
   import { Search } from '@element-plus/icons-vue';
   import { ElTree } from 'element-plus';
+  import { ElMessage } from 'element-plus';
   import { countLeafNodes } from '@/views/message/persongroup/utils/index';
-  import { getList } from '@/api/message/system-notifications';
+  import { getList, getPushRange, updatePushRange } from '@/api/message/question-notifications';
   import useComTree from '@/views/system-config/scene-manage/store/use-com-tree';
   import { storeToRefs } from 'pinia';
   import { cloneDeep } from 'lodash-es';
   import { TreeKey } from 'element-plus/es/components/tree/src/tree.type';
+  import { treeSelected } from '../type';
+  import {
+    CameraTree,
+    CameraTreeNodeType,
+    CameraQueryForm,
+    getCameraTree,
+  } from '@/api/camera/camera-preview';
+
+  const props = defineProps<{
+    selectedUser: treeSelected[];
+    reFreshCamera: (arr: treeSelected[]) => unknown;
+  }>();
+
+  //相机树信息
+  const cameraTreeTemp = ref<CameraTree[]>([]); // 保存修改name之后的树
+
+  // 把树节点中所有 nodeType = camera 的 name 替换成 name + code
+  function getCameraNameCode(data) {
+    const cameraNameCode = data;
+    for (let i = 0; i < data.length; i++) {
+      const node = cameraNameCode[i];
+      if (node.nodeType === 'camera') {
+        node.name = node.name + ` [${node.code}] `;
+      }
+      if (node.children && node.children.length > 0) {
+        getCameraNameCode(node.children);
+      }
+    }
+    return cameraNameCode;
+  }
+
+  const getCameraData = async (tempQuery) => {
+    await getCameraTree(tempQuery).then((res) => {
+      cameraTreeTemp.value = getCameraNameCode(res);
+    });
+  };
+
   const comTree = useComTree();
   const { comTreeData } = storeToRefs(comTree);
   const loading = ref(true);
@@ -120,11 +158,6 @@
   const treeRef = ref<InstanceType<typeof ElTree>>();
   const total = ref<number>(0);
   const selected = ref<number>(0);
-  interface treeSelected {
-    code: number | string;
-    name: string;
-    id: number;
-  }
   const selectedPeople = ref<treeSelected[]>([]);
   const handleTagClose = (code) => {
     const index = selectedPeople.value.findIndex((item) => item.code === code);
@@ -139,7 +172,20 @@
     emit('cancel');
   };
   const handleSubmit = () => {
-    emit('submit', selectedPeople.value);
+    const arr = selectedPeople.value.map((selectedPeople) => selectedPeople.id);
+    updatePushRange({ cameraIdList: arr })
+      .then(() => {
+        ElMessage({
+          message: '修改成功',
+          type: 'success',
+          plain: true,
+        });
+        props.reFreshCamera(selectedPeople.value);
+      })
+      .catch((error) => {
+        console.error(error);
+        handleCancle();
+      });
   };
   const handleCheckChange = (node, checked) => {
     if (!node.children) {
@@ -166,24 +212,12 @@
       treeRef.value!.setChecked(node.code, !checked.checked, true);
     }
   };
-  const props = defineProps<{
-    selectedUser: treeSelected[];
-  }>();
+
   onMounted(() => {
-    if (props.selectedUser) {
-      selectedPeople.value = cloneDeep(props.selectedUser);
-    }
-    selected.value = selectedPeople.value.length;
-    getList().then((res) => {
-      comTreeData.value = res;
-      total.value = countLeafNodes(nodeData.value);
-      const selectedIds: TreeKey[] = selectedPeople.value.map((item) => item.code);
-      treeRef.value!.setCheckedKeys(selectedIds);
-      loading.value = false;
+    getPushRange().then((res) => {
+      selectedPeople.value = res;
     });
-  });
-  watch(queryStr, (query) => {
-    treeRef.value!.filter(query);
+    getCameraData({});
   });
 </script>
 

+ 322 - 0
src/views/message/questionNotifications/components/contentPanel.vue

@@ -0,0 +1,322 @@
+<template>
+  <div class="content_panel" style="margin-right: 40px">
+    <div class="panel_top">
+      <div class="top_content">
+        <div class="circle">{{ props.issueType }}</div>
+        <div class="title_name">问题{{ props.title }}阶段</div>
+        <div class="title_explain">问题{{ props.title }}阶段</div>
+      </div>
+      <el-button
+        v-show="editDetails.openEdit === false"
+        link
+        type="primary"
+        size="small"
+        @click="openQuestionEdit"
+        style="height: 15px; margin-top: 4px; margin-left: auto"
+        ><el-tooltip class="box-item" effect="dark" content="编辑" placement="top">
+          <el-icon :size="16">
+            <EditPen />
+          </el-icon>
+        </el-tooltip>
+      </el-button>
+      <el-button
+        v-show="editDetails.openEdit === true"
+        type="primary"
+        style="height: 24px; margin-left: auto; width: 48px"
+        @click="saveQuestionEdit"
+        >保存</el-button
+      >
+    </div>
+    <div class="subtitle"
+      >问题{{ props.title }}时
+      <el-switch
+        :disabled="!editDetails.openEdit"
+        v-model="editDetails.atProcessing"
+        size="small"
+        style="height: 16px; margin-left: 12px"
+      />
+    </div>
+    <div class="subtitle_explain_disable">自动推送给问题发生地点的所有{{ props.title }}员</div>
+    <div v-show="props.issueType != QuestionStatus.finishe" class="subtitle"
+      >超期未{{ props.title }}
+      <el-switch
+        :disabled="!editDetails.openEdit"
+        v-model="editDetails.atExpiry"
+        size="small"
+        style="height: 16px; margin-left: 12px"
+      />
+    </div>
+    <div
+      v-show="props.issueType != QuestionStatus.finishe"
+      :class="[
+        'subtitle_explain_disable',
+        { editable: editDetails.openEdit === true && editDetails.atExpiry === true },
+      ]"
+      style="margin-top: 10px"
+      >超过<el-input-number
+        v-model="editDetails.expiryTime"
+        :disabled="!editDetails.openEdit || !editDetails.atExpiry"
+        :min="0"
+        size="small"
+        style="width: 70px"
+        controls-position="right"
+      />小时未{{ props.title }}继续推送给{{ props.title }}员</div
+    >
+    <div v-show="props.issueType != QuestionStatus.finishe" class="subtitle"
+      >长期未{{ props.title }}
+      <el-switch
+        :disabled="!editDetails.openEdit"
+        v-model="editDetails.atLongTimeExpiry"
+        size="small"
+        style="height: 16px; margin-left: 12px"
+      />
+    </div>
+    <div
+      v-show="props.issueType != QuestionStatus.finishe"
+      :class="[
+        'subtitle_explain_disable',
+        { editable: editDetails.openEdit === true && editDetails.atLongTimeExpiry === true },
+      ]"
+      style="margin-top: 10px"
+      >超过<el-input-number
+        v-model="editDetails.longTimeValue"
+        :disabled="!editDetails.openEdit || !editDetails.atLongTimeExpiry"
+        :min="0"
+        size="small"
+        style="width: 70px"
+        controls-position="right" />小时未{{ props.title }}再次推送给{{
+        props.title
+      }}员并抄送给<el-select
+        :disabled="!editDetails.openEdit || !editDetails.atLongTimeExpiry"
+        v-model="editDetails.copyTo"
+        value-key="id"
+        @click="openNameTree"
+        @change="openNameTree"
+        placeholder="Select"
+        size="small"
+        style="width: 105px"
+        multiple
+        collapse-tags
+        collapse-tags-tooltip
+      >
+        <el-option
+          v-for="item in editDetails.copyTo"
+          :key="item.id"
+          :label="item.realname"
+          :value="item"
+        /> </el-select
+    ></div>
+    <div class="subtitle">推送文案 </div>
+    <el-tooltip
+      class="box-item"
+      effect="dark"
+      :disabled="!showToolTip"
+      :content="editDetails.textToPush"
+      placement="bottom"
+    >
+      <el-input
+        disabled
+        style="width: 364px; margin-left: 30px; margin-top: 4px"
+        :placeholder="textToshow"
+      />
+    </el-tooltip>
+  </div>
+  <el-dialog
+    v-model="dialogVisible"
+    title="添加人员"
+    align-center
+    :close-on-click-modal="false"
+    style="height: 583px"
+    :width="731"
+    :destroy-on-close="true"
+    class="workShopDialog"
+  >
+    <!-- <SelectTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" /> -->
+    <PersonFilterSelection
+      @cancel="handleCancle"
+      @submit="handleSubmit"
+      :init-selected="selectedUser"
+    />
+  </el-dialog>
+</template>
+<script lang="ts" setup>
+  import { panelDetails, QuestionStatus, issueDetilasType, PushTypeStatus } from '../type';
+  import { EditPen } from '@element-plus/icons-vue';
+  import { ref, watch, computed } from 'vue';
+  import { SelectedFilterPersonInfo } from '@/api/message/person-group';
+  import PersonFilterSelection from '@/views/message/components/PersonFilterSelection.vue';
+
+  const props = defineProps<{
+    savedData: Array<issueDetilasType>;
+    issueType: number;
+    title: string;
+    saveUpdate: (data: panelDetails, issueType: number) => unknown;
+  }>();
+
+  const editDetails = ref<panelDetails>({
+    atProcessing: false,
+    atExpiry: false,
+    atLongTimeExpiry: false,
+    expiryTime: 0,
+    longTimeValue: 0,
+    copyTo: [],
+    textToPush: '您有一条【待审核/超期未审核/长期未审核】的问题单,请及时关注并整改',
+    openEdit: false,
+  });
+
+  const textToshow = computed(() => {
+    if (editDetails.value.textToPush.length > 24) {
+      return editDetails.value.textToPush.slice(0, 24) + '...';
+    } else {
+      return editDetails.value.textToPush;
+    }
+  });
+  const showToolTip = computed(() => {
+    if (editDetails.value.textToPush.length > 20) {
+      return true;
+    } else {
+      return false;
+    }
+  });
+  const openQuestionEdit = () => {
+    editDetails.value.openEdit = true;
+  };
+  const saveQuestionEdit = () => {
+    props.saveUpdate(editDetails.value, props.issueType);
+    editDetails.value.openEdit = false;
+  };
+
+  const dialogVisible = ref<boolean>(false);
+  const selectedUser = ref<SelectedFilterPersonInfo[]>([]);
+
+  const openNameTree = () => {
+    selectedUser.value = editDetails.value.copyTo!;
+    dialogVisible.value = true;
+  };
+
+  const handleSubmit = (selectedData: SelectedFilterPersonInfo[]) => {
+    // selectedUser.value = selectedData;
+    editDetails.value.copyTo = selectedData;
+    dialogVisible.value = false;
+  };
+  const handleCancle = () => {
+    dialogVisible.value = false;
+  };
+  watch(
+    () => props.savedData, //监听状态值,,当拉取的数据发生变化时,对editDetails进行赋初值
+    (newValue) => {
+      //问题审核阶段,后端拉取保存数据
+      // console.log('props.savedData=', newValue);
+      let subData = newValue.find((item) => item.issuePhase === props.issueType);
+      let atonceData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.atonce,
+      );
+      editDetails.value.atProcessing = atonceData?.isEnabled === 1 ? true : false;
+      //完成阶段不需要执行下面的内容
+      if (props.issueType != QuestionStatus.finishe) {
+        let expireData = subData?.issueProcessMessageList.find(
+          (item) => item.pushType === PushTypeStatus.expire,
+        );
+        console.log('expireData=', expireData);
+        editDetails.value.atExpiry = expireData?.isEnabled === 1 ? true : false;
+        if (expireData && expireData!.overtime) {
+          editDetails.value.expiryTime = expireData!.overtime;
+        }
+        // editDetails.value.expiryTime = expireData!.overtime;
+        let longTimeData = subData?.issueProcessMessageList.find(
+          (item) => item.pushType === PushTypeStatus.longtime,
+        );
+        editDetails.value.atLongTimeExpiry = longTimeData?.isEnabled === 1 ? true : false;
+        if (longTimeData && longTimeData!.overtime) {
+          editDetails.value.longTimeValue = longTimeData!.overtime;
+        }
+        if (longTimeData && longTimeData!.ccRecipients) {
+          editDetails.value.copyTo = longTimeData!.ccRecipients;
+        }
+      }
+    },
+    { deep: true, immediate: true }, //deep,深度监听。immediate,在程序开始运行时即马上监听
+  );
+</script>
+<style lang="scss" scoped>
+  .content_panel {
+    width: 530px;
+    height: 326px;
+    // background-color: pink;
+    border-radius: 8px;
+    border: 1px solid #e4e7ed;
+    .panel_top {
+      width: 100%;
+      height: 50px;
+      display: flex;
+      flex-direction: row;
+      background-color: #fafafa;
+      border-radius: 8px;
+      padding: 13px 16px 0px 16px;
+      .top_content {
+        // width: 100%;
+        height: 24px;
+        display: flex;
+        flex-direction: row;
+        .circle {
+          width: 24px;
+          height: 24px;
+          border-radius: 12px;
+          background: #1890ff;
+          font-weight: 600;
+          font-size: 14px;
+          color: #ffffff;
+          line-height: 24px;
+          text-align: center;
+        }
+        .title_name {
+          height: 24px;
+          margin-left: 8px;
+          font-weight: 600;
+          font-size: 16px;
+          color: rgba(0, 0, 0, 0.88);
+          line-height: 24px;
+          text-align: left;
+        }
+        .title_explain {
+          height: 14px;
+          margin-left: 8px;
+          margin-top: 6px;
+          font-weight: 400;
+          font-size: 10px;
+          color: #999999;
+          line-height: 14px;
+          text-align: left;
+        }
+      }
+    }
+    .subtitle {
+      height: 17px;
+      margin-left: 30px;
+      margin-top: 17px;
+      font-weight: 600;
+      font-size: 12px;
+      color: #303133;
+      line-height: 17px;
+      text-align: left;
+      display: flex;
+      flex-direction: row;
+    }
+    .subtitle_explain_disable {
+      height: 21px;
+      margin-left: 30px;
+      // margin-top: 4px;
+      // margin-top: 3px;
+      font-weight: 400;
+      font-size: 10px;
+      color: #a8abb2;
+      line-height: 21px;
+      text-align: left;
+      display: flex;
+      flex-direction: row;
+    }
+    .editable {
+      color: #303133 !important;
+    }
+  }
+</style>

+ 242 - 0
src/views/message/questionNotifications/components/problemHandleTable.vue

@@ -0,0 +1,242 @@
+<template>
+  <div>
+    <div style="padding-bottom: 24px; border-top: 1px solid rgba(0, 0, 0, 0.06)">
+      <div class="pushWorkShopBar">
+        <span class="pushiWorkShopSpan">选择推送范围:</span>
+        <el-select
+          v-model="cameraChosen"
+          multiple
+          collapse-tags
+          collapse-tags-tooltip
+          placeholder="请选择相机"
+          style="width: 258px"
+          @click="handleWorkShopEdit"
+          @change="handleWorkShopEdit"
+        >
+          <el-option
+            v-for="item in options"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+        <!-- <img src="../img/edit.png" @click="handleWorkShopEdit()" /> -->
+      </div>
+      <div class="descriptionSpan"
+        >当所选相机下发生违规问题并需要闭环处理时,系统将依据下方各阶段配置(开关启用后)自动发送待办消息!</div
+      >
+    </div>
+
+    <el-scrollbar>
+      <ManageContent />
+    </el-scrollbar>
+
+    <el-dialog
+      v-model="workDialog"
+      title="选择相机范围"
+      align-center
+      :close-on-click-modal="false"
+      style="height: 583px"
+      :width="731"
+      :destroy-on-close="true"
+      class="workShopDialog"
+    >
+      <!-- <WorkShopTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" /> -->
+      <WorkShopTree
+        @cancel="handleCancle"
+        :selectedUser="selectedUser"
+        :re-fresh-camera="setCameraChosen"
+      />
+    </el-dialog>
+    <el-dialog
+      v-model="showDialog"
+      title="编辑推送文案"
+      align-center="true"
+      width="400"
+      @close="closeDialog()"
+      :close-on-click-modal="false"
+      class="contentDialog"
+    >
+      <el-input
+        v-model="content"
+        style="width: 370px"
+        :autosize="{ minRows: 4, maxRows: 5 }"
+        maxlength="100"
+        show-word-limit
+        type="textarea"
+        placeholder="请输入推送文案"
+      />
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog()">取消</el-button>
+          <el-button type="primary" @click="submitDialog()">确认</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref, onMounted, computed } from 'vue';
+  import {
+    queryIssueProcessMessage,
+    modifyContent,
+    modifyWorkshopList,
+    queryWorkshopNamebyIds,
+    getPushRange,
+  } from '@/api/message/question-notifications';
+
+  import WorkShopTree from './WorkShopTree.vue';
+  import ManageContent from './ManageContent.vue';
+  import { ElMessage } from 'element-plus';
+  import { treeSelected } from '../type';
+
+  const cameraChosen = ref<any>([]);
+
+  const options = computed(() => {
+    return cameraChosen.value.map((item: any) => {
+      return {
+        value: item,
+        label: item,
+      };
+    });
+  });
+
+  const setCameraChosen = (arr: treeSelected[]) => {
+    cameraChosen.value = arr.map((arr) => arr.name);
+    workDialog.value = false;
+  };
+  const workDialog = ref<boolean>(false);
+  const showDialog = ref<boolean>(false);
+  const content = ref<string>('');
+
+  const editId = ref<number>(0);
+  const handleWorkShopEdit = () => {
+    workDialog.value = true;
+  };
+
+  const submitDialog = () => {
+    modifyContent(content.value, editId.value)
+      .then(() => {
+        ElMessage({
+          message: '修改成功',
+          type: 'success',
+          plain: true,
+        });
+        closeDialog();
+      })
+      .catch((error) => {
+        console.error(error);
+      });
+  };
+
+  const closeDialog = () => {
+    showDialog.value = false;
+  };
+  interface UserList {
+    code: number | string;
+    name: string;
+    id: number;
+  }
+  const selectedUser = ref<UserList[]>([]);
+
+  const handleCancle = () => {
+    workDialog.value = false;
+  };
+
+  const handleSubmit = (selectedData: UserList[]) => {
+    const params = selectedData.map((item) => item.id);
+    modifyWorkshopList(params).then(() => {
+      ElMessage({
+        message: '添加成功',
+        type: 'success',
+        plain: true,
+      });
+      workDialog.value = false;
+      // queryIssueData();
+    });
+  };
+
+  const initCameraChosen = () => {
+    getPushRange().then((res) => {
+      cameraChosen.value = res.map((res) => res.name);
+    });
+  };
+  onMounted(() => {
+    initCameraChosen();
+  });
+</script>
+<style lang="scss" scoped>
+  .pushWorkShopBar {
+    margin: 11px 0 8px 0;
+    display: flex;
+    img {
+      cursor: pointer;
+    }
+    // :nth-of-type(1) {
+    //   margin-right: 12px;
+    //   margin-bottom: 16px;
+    // }
+    .pushiWorkShopSpan {
+      // width: 56px;
+      height: 32px;
+      font-weight: 600;
+      font-size: 14px;
+      color: rgba(0, 0, 0, 0.85);
+      line-height: 32px;
+    }
+  }
+  .descriptionSpan {
+    // width: 300px;
+    margin-left: 100px;
+    height: 10px;
+    font-weight: 400;
+    font-size: 10px;
+    color: #a8abb2;
+    line-height: 10px;
+  }
+  .workshopList {
+    display: flex;
+    .left {
+      display: flex;
+      flex-wrap: wrap;
+      width: 95%;
+      max-height: 24px;
+      overflow-y: hidden;
+      gap: 20px;
+    }
+    .right {
+      flex: 1;
+      cursor: pointer;
+      span {
+        font-weight: 400;
+        font-size: 12px;
+        color: #303133;
+        line-height: 17px;
+      }
+      img {
+        margin-left: 4px;
+      }
+    }
+  }
+  .emptyDiv {
+    margin-top: 78px;
+    margin: auto;
+    width: 396px;
+    .emptyImg {
+      height: 257px;
+    }
+    .emptySpan {
+      font-family: PingFangSC, PingFang SC;
+      font-weight: 400;
+      font-size: 18px;
+      color: rgba(0, 0, 0, 0.45);
+      text-align: left;
+      font-style: normal;
+    }
+  }
+  .operation {
+    display: flex;
+    justify-content: center;
+    width: 100%;
+  }
+</style>

+ 52 - 0
src/views/message/questionNotifications/type.ts

@@ -0,0 +1,52 @@
+import { SelectedFilterPersonInfo } from '@/api/message/person-group';
+
+export enum QuestionStatus {
+  toAuth = 1, // 待审核
+  todo = 2, // 待处理
+  toReview = 3, // 待复核
+  finishe = 4, //已完结
+}
+
+export enum PushTypeStatus {
+  atonce = 1, // 及时
+  expire = 2, // 超时
+  longtime = 3, // 长期
+}
+
+export interface issueDetilasType {
+  //问题处理阶段
+  issuePhase: number;
+
+  //问题处理通知列表
+  issueProcessMessageList: issueProcessType[];
+}
+export interface issueProcessType {
+  //前端没啥用,但要和后端数据去匹配
+  id: number;
+  //0禁用1启用
+  isEnabled: number;
+  //超时时长
+  overtime?: number;
+  //推送类型: 1-即时推送 2-超期未处理推送 3-长期未处理推送
+  pushType: number;
+
+  //抄送人
+  ccRecipients?: SelectedFilterPersonInfo[];
+}
+
+export interface panelDetails {
+  openEdit: boolean;
+  atProcessing: boolean;
+  atExpiry?: boolean;
+  expiryTime?: number;
+  atLongTimeExpiry?: boolean;
+  longTimeValue?: number;
+  copyTo?: SelectedFilterPersonInfo[];
+  textToPush: string;
+}
+
+export interface treeSelected {
+  code: number | string;
+  name: string;
+  id: number;
+}

+ 0 - 291
src/views/message/systemNotifications/components/problemHandleTable.vue

@@ -1,291 +0,0 @@
-<template>
-  <div>
-    <div style="padding-bottom: 20px; border-bottom: 1px solid rgba(0, 0, 0, 0.06)">
-      <div class="pushWorkShopBar">
-        <span class="pushiWorkShopSpan">推送车间</span>
-        <img src="../img/edit.png" @click="handleWorkShopEdit()" />
-        <span class="descriptionSpan">请点击编辑按钮,选择需要发送问题处理通知的车间</span>
-      </div>
-      <div class="workshopList">
-        <div class="left" ref="listLeft">
-          <el-tag type="primary" v-for="item in selectedUser" :key="item.code">
-            {{ item.name }}
-          </el-tag>
-        </div>
-        <div v-if="!isExpandShow" class="right" @click="toExpand()">
-          <div
-            style="display: flex; height: 24px; justify-content: center; align-items: center"
-            v-if="isExpand"
-          >
-            <span>展开</span><img src="@/assets/icons/arrow_bottom.png" />
-          </div>
-          <div
-            style="display: flex; height: 24px; justify-content: center; align-items: center"
-            v-else
-          >
-            <span>收起</span><img src="@/assets/icons/arrow_top.png" />
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <el-table
-      :data="problemTableData"
-      stripe
-      height="calc(100vh - 400px)"
-      :cell-style="{ textAlign: 'center' }"
-      :header-cell-style="{ 'text-align': 'center' }"
-      style="width: 100%; margin-top: 16px; --el-table-border-color: none"
-    >
-      <el-table-column width="156" prop="issuePhase" label="问题阶段" />
-
-      <el-table-column width="176" prop="issueState" label="审核状态">
-        <template #default="scope">
-          <el-tag type="warning" v-if="scope.row.issueState === 1">
-            {{ issueStateMapping[scope.row.issueState] }}
-          </el-tag>
-          <el-tag type="danger" v-else-if="scope.row.issueState === 6">
-            {{ issueStateMapping[scope.row.issueState] }}
-          </el-tag>
-          <el-tag type="success" v-else-if="scope.row.issueState === 7">
-            {{ issueStateMapping[scope.row.issueState] }}
-          </el-tag>
-          <el-tag type="primary" v-else>{{ issueStateMapping[scope.row.issueState] }}</el-tag>
-        </template>
-      </el-table-column>
-
-      <el-table-column width="156" prop="recipient" label="推送对象" />
-
-      <el-table-column width="520" prop="content" label="推送文案" />
-
-      <el-table-column label="操作" fixed="right">
-        <template #default="scope">
-          <div class="operation">
-            <el-tooltip class="box-item" effect="light" content="编辑" placement="bottom">
-              <img
-                src="../img/edit.png"
-                @click="handleProbleEdit(scope.row.id, scope.row.content)"
-              />
-            </el-tooltip>
-          </div>
-        </template>
-      </el-table-column>
-
-      <template #empty>
-        <div class="emptyDiv">
-          <img src="../img/empty.png" class="emptyImg" />
-          <span class="emptySpan">暂无数据</span>
-        </div>
-      </template>
-    </el-table>
-    <el-dialog
-      v-model="workDialog"
-      title="添加推送车间"
-      align-center
-      :close-on-click-modal="false"
-      style="height: 583px"
-      :width="731"
-      :destroy-on-close="true"
-      class="workShopDialog"
-    >
-      <WorkShopTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" />
-    </el-dialog>
-    <el-dialog
-      v-model="showDialog"
-      title="编辑推送文案"
-      align-center="true"
-      width="400"
-      @close="closeDialog()"
-      :close-on-click-modal="false"
-      class="contentDialog"
-    >
-      <el-input
-        v-model="content"
-        style="width: 370px"
-        :autosize="{ minRows: 4, maxRows: 5 }"
-        maxlength="100"
-        show-word-limit
-        type="textarea"
-        placeholder="请输入推送文案"
-      />
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="closeDialog()">取消</el-button>
-          <el-button type="primary" @click="submitDialog()">确认</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-<script lang="ts" setup>
-  import { ref, onMounted, nextTick } from 'vue';
-  import { problemPhase, issueStateMapping } from '../type';
-  import {
-    queryIssueProcessMessage,
-    modifyContent,
-    modifyWorkshopList,
-    queryWorkshopNamebyIds,
-  } from '@/api/message/system-notifications';
-  import WorkShopTree from './WorkShopTree.vue';
-  import { ElMessage } from 'element-plus';
-  const problemTableData = ref<problemPhase[]>([]);
-  const workDialog = ref<boolean>(false);
-  const showDialog = ref<boolean>(false);
-  const content = ref<string>('');
-  const listLeft = ref();
-  const isExpandShow = ref<boolean>(false);
-  const isExpand = ref<boolean>(true);
-  const toExpand = () => {
-    isExpand.value = !isExpand.value;
-    if (isExpand.value) {
-      listLeft.value.style.maxHeight = '24px';
-    } else {
-      listLeft.value.style.maxHeight = 'none';
-    }
-  };
-  const editId = ref<number>(0);
-  const handleWorkShopEdit = () => {
-    workDialog.value = true;
-  };
-  const handleProbleEdit = (id: number, _content: string) => {
-    showDialog.value = true;
-    editId.value = id;
-    content.value = _content;
-  };
-
-  const submitDialog = () => {
-    modifyContent(content.value, editId.value)
-      .then(() => {
-        ElMessage({
-          message: '修改成功',
-          type: 'success',
-          plain: true,
-        });
-        closeDialog();
-        queryIssueData();
-      })
-      .catch((error) => {
-        console.error(error);
-      });
-  };
-
-  const closeDialog = () => {
-    showDialog.value = false;
-  };
-  interface UserList {
-    code: number | string;
-    name: string;
-    id: number;
-  }
-  const selectedUser = ref<UserList[]>([]);
-  const queryIssueData = () => {
-    queryIssueProcessMessage().then((res) => {
-      let params;
-      if (res[0].workshopList) {
-        params = JSON.parse(res[0].workshopList);
-      } else {
-        params = [];
-      }
-      queryWorkshopNamebyIds(params).then((res) => {
-        selectedUser.value = res;
-        nextTick(() => {
-          const height = listLeft.value.scrollHeight;
-          isExpandShow.value = height > 24 ? false : true;
-        });
-      });
-      problemTableData.value = res;
-    });
-  };
-  const handleCancle = () => {
-    workDialog.value = false;
-  };
-  const handleSubmit = (selectedData: UserList[]) => {
-    const params = selectedData.map((item) => item.id);
-    modifyWorkshopList(params).then(() => {
-      ElMessage({
-        message: '添加成功',
-        type: 'success',
-        plain: true,
-      });
-      workDialog.value = false;
-      queryIssueData();
-    });
-  };
-  onMounted(() => {
-    queryIssueData();
-  });
-</script>
-<style lang="scss" scoped>
-  .pushWorkShopBar {
-    margin: 24px 0 0 0;
-    display: flex;
-    img {
-      cursor: pointer;
-    }
-    :nth-of-type(1) {
-      margin-right: 12px;
-      margin-bottom: 16px;
-    }
-    .pushiWorkShopSpan {
-      width: 56px;
-      height: 20px;
-      font-weight: 600;
-      font-size: 14px;
-      color: #303133;
-      line-height: 20px;
-    }
-    .descriptionSpan {
-      width: 300px;
-      height: 14px;
-      font-weight: 400;
-      font-size: 12px;
-      color: #999999;
-      line-height: 20px;
-    }
-  }
-  .workshopList {
-    display: flex;
-    .left {
-      display: flex;
-      flex-wrap: wrap;
-      width: 95%;
-      max-height: 24px;
-      overflow-y: hidden;
-      gap: 20px;
-    }
-    .right {
-      flex: 1;
-      cursor: pointer;
-      span {
-        font-weight: 400;
-        font-size: 12px;
-        color: #303133;
-        line-height: 17px;
-      }
-      img {
-        margin-left: 4px;
-      }
-    }
-  }
-  .emptyDiv {
-    margin-top: 78px;
-    margin: auto;
-    width: 396px;
-    .emptyImg {
-      height: 257px;
-    }
-    .emptySpan {
-      font-family: PingFangSC, PingFang SC;
-      font-weight: 400;
-      font-size: 18px;
-      color: rgba(0, 0, 0, 0.45);
-      text-align: left;
-      font-style: normal;
-    }
-  }
-  .operation {
-    display: flex;
-    justify-content: center;
-    width: 100%;
-  }
-</style>

+ 47 - 47
src/views/message/systemNotifications/systemNotifications.vue

@@ -25,59 +25,59 @@
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
-import systemNotificationTable from './components/systemNotificationTable.vue';
-import problemHandleTable from './components/problemHandleTable.vue';
+  import { ref } from 'vue';
+  import systemNotificationTable from './components/systemNotificationTable.vue';
+  import problemHandleTable from '../questionNotifications/components/problemHandleTable.vue';
 
-enum notificationType {
-  system,
-  problemHandle,
-}
-const currentNotification = ref(notificationType.system);
+  enum notificationType {
+    system,
+    problemHandle,
+  }
+  const currentNotification = ref(notificationType.system);
 
-const changeNotification = (notificationType: notificationType) => {
-  currentNotification.value = notificationType;
-};
+  const changeNotification = (notificationType: notificationType) => {
+    currentNotification.value = notificationType;
+  };
 </script>
 
 <style lang="scss" scoped>
-.notificationPage {
-  height: calc(100vh - 64px - 18px);
-  background-color: rgba(255, 255, 255, 1);
-  padding: 24px 44px 35px 21px;
-  position: relative;
-}
+  .notificationPage {
+    height: calc(100vh - 64px - 18px);
+    background-color: rgba(255, 255, 255, 1);
+    padding: 24px 44px 35px 21px;
+    position: relative;
+  }
 
-.notificationSelectionBar {
-  display: flex;
-  width: 376px;
-  height: 38px;
-  border: 1px solid #d9d9d9;
-  background: rgba(0, 0, 0, 0.02);
-  margin-bottom: 24px;
-  cursor: pointer;
-  border-radius: 4px;
-  .notification {
-    width: 50%;
+  .notificationSelectionBar {
+    display: flex;
+    width: 376px;
+    height: 38px;
+    border: 1px solid #d9d9d9;
     background: rgba(0, 0, 0, 0.02);
-    line-height: 36px;
-    text-align: center;
-  }
-  .snotificationSelected {
-    width: 50%;
-    outline: 1px solid #1890ff;
-    background: rgba(24, 144, 255, 0.15);
-    line-height: 36px;
-    text-align: center;
-    border-radius: 4px 0 0 4px;
-  }
-  .pnotificationSelected {
-    width: 50%;
-    outline: 1px solid #1890ff;
-    background: rgba(24, 144, 255, 0.15);
-    line-height: 36px;
-    text-align: center;
-    border-radius: 0 4px 4px 0;
+    margin-bottom: 24px;
+    cursor: pointer;
+    border-radius: 4px;
+    .notification {
+      width: 50%;
+      background: rgba(0, 0, 0, 0.02);
+      line-height: 36px;
+      text-align: center;
+    }
+    .snotificationSelected {
+      width: 50%;
+      outline: 1px solid #1890ff;
+      background: rgba(24, 144, 255, 0.15);
+      line-height: 36px;
+      text-align: center;
+      border-radius: 4px 0 0 4px;
+    }
+    .pnotificationSelected {
+      width: 50%;
+      outline: 1px solid #1890ff;
+      background: rgba(24, 144, 255, 0.15);
+      line-height: 36px;
+      text-align: center;
+      border-radius: 0 4px 4px 0;
+    }
   }
-}
 </style>

+ 1 - 1
src/views/message/systemNotifications/type.ts

@@ -124,5 +124,5 @@ export const issueStateMapping = {
 
 export enum PushStatusEnum {
   PEDDING = 0, // 等待推送
-  FULFILL = 1  // 已完成
+  FULFILL = 1, // 已完成
 }