Prechádzať zdrojové kódy

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

kuanghua liu 1 rok pred
rodič
commit
e16a244c03

+ 34 - 0
src/api/message/system-notifications.ts

@@ -1,4 +1,5 @@
 import { http } from '@/utils/http/axios';
+import { issueDetilasType } from '@/views/message/systemNotifications/type.ts';
 
 export interface queryParams {
   queryParam?: string;
@@ -45,6 +46,39 @@ export function getList() {
   });
 }
 
+// 获取推送范围
+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,
+  });
+}
+
 // 下发车间
 export function modifyWorkshopList(workshopList: number[]) {
   return http.request({

+ 777 - 0
src/views/message/systemNotifications/components/ManageContent.vue

@@ -0,0 +1,777 @@
+<template>
+  <div class="manage_content">
+    <div class="content_row" style="margin-bottom: 32px">
+      <div class="content_panel" style="margin-right: 40px">
+        <div class="panel_top">
+          <div class="top_content">
+            <div class="circle">1</div>
+            <div class="title_name">问题审核阶段</div>
+            <div class="title_explain">问题审核阶段</div>
+          </div>
+          <el-button
+            v-show="toAuthEdit.openEdit === false"
+            link
+            type="primary"
+            size="small"
+            @click="openToAuth"
+            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="toAuthEdit.openEdit === true"
+            type="primary"
+            style="height: 24px; margin-left: auto; width: 48px"
+            @click="saveToAuth"
+            >保存</el-button
+          >
+        </div>
+        <div class="subtitle"
+          >问题审核时
+          <el-switch
+            :disabled="!toAuthEdit.openEdit"
+            v-model="toAuthEdit.atProcessing"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div class="subtitle_explain_disable">自动推送给问题发生地点的所有审核员</div>
+        <div class="subtitle"
+          >超期未审核
+          <el-switch
+            :disabled="!toAuthEdit.openEdit"
+            v-model="toAuthEdit.atExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="toAuthEdit.openEdit === true && toAuthEdit.atExpiry === true ? 'editable' : ''"
+          style="margin-top: 10px"
+          >超过<el-input-number
+            v-model="toAuthEdit.expiryTime"
+            :disabled="!toAuthEdit.openEdit || !toAuthEdit.atExpiry"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right"
+          />小时未审核继续推送给审核员</div
+        >
+        <div class="subtitle"
+          >长期未审核
+          <el-switch
+            :disabled="!toAuthEdit.openEdit"
+            v-model="toAuthEdit.atLongTimeExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="
+            toAuthEdit.openEdit === true && toAuthEdit.atLongTimeExpiry === true ? 'editable' : ''
+          "
+          style="margin-top: 10px"
+          >超过<el-input-number
+            v-model="toAuthEdit.longTimeValue"
+            :disabled="!toAuthEdit.openEdit || !toAuthEdit.atLongTimeExpiry"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right" />小时未审核再次推送给审核员并抄送给<el-select
+            :disabled="!toAuthEdit.openEdit || !toAuthEdit.atLongTimeExpiry"
+            v-model="toAuthEdit.copyTo"
+            value-key="realname"
+            @click="setCurrentProcess(QuestionStatus.toAuth)"
+            placeholder="Select"
+            size="small"
+            style="width: 105px"
+            multiple
+            collapse-tags
+            collapse-tags-tooltip
+          >
+            <el-option
+              v-for="item in directors"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            /> </el-select
+        ></div>
+        <div class="subtitle">推送文案 </div>
+        <el-input
+          :disabled="!toAuthEdit.openEdit"
+          v-model="toAuthEdit.textToPush"
+          style="width: 364px; margin-left: 30px; margin-top: 4px"
+        />
+      </div>
+      <div class="content_panel">
+        <div class="panel_top">
+          <div class="top_content">
+            <div class="circle">2</div>
+            <div class="title_name">问题整改阶段</div>
+            <div class="title_explain">问题整改阶段</div>
+          </div>
+          <el-button
+            v-show="todoEdit.openEdit === false"
+            link
+            type="primary"
+            size="small"
+            @click="opentodo"
+            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="todoEdit.openEdit === true"
+            type="primary"
+            @click="savetodo"
+            style="height: 24px; margin-left: auto; width: 48px"
+            >保存</el-button
+          >
+        </div>
+        <div class="subtitle"
+          >问题整改时
+          <el-switch
+            :disabled="!todoEdit.openEdit"
+            v-model="todoEdit.atProcessing"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div class="subtitle_explain_disable">自动推送给问题发生地点的所有整改员</div>
+        <div class="subtitle"
+          >超期未整改
+          <el-switch
+            :disabled="!todoEdit.openEdit"
+            v-model="todoEdit.atExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="todoEdit.openEdit === true && todoEdit.atExpiry === true ? 'editable' : ''"
+          style="margin-top: 10px"
+          >超过<el-input-number
+            v-model="todoEdit.expiryTime"
+            :disabled="!todoEdit.openEdit || !todoEdit.atExpiry"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right"
+          />小时未整改继续推送给整改员</div
+        >
+        <div class="subtitle"
+          >长期未整改
+          <el-switch
+            :disabled="!todoEdit.openEdit"
+            v-model="todoEdit.atLongTimeExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="
+            todoEdit.openEdit === true && todoEdit.atLongTimeExpiry === true ? 'editable' : ''
+          "
+          style="margin-top: 10px"
+          >超过<el-input-number
+            v-model="todoEdit.longTimeValue"
+            :disabled="!todoEdit.openEdit || !todoEdit.atLongTimeExpiry"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right" />小时未整改再次推送给整改员并抄送给<el-select
+            :disabled="!todoEdit.openEdit || !todoEdit.atLongTimeExpiry"
+            v-model="todoEdit.copyTo"
+            value-key="realname"
+            @click="setCurrentProcess(QuestionStatus.todo)"
+            placeholder="Select"
+            size="small"
+            style="width: 105px"
+            multiple
+            collapse-tags
+            collapse-tags-tooltip
+          >
+            <el-option
+              v-for="item in directors"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            /> </el-select
+        ></div>
+        <div class="subtitle">推送文案 </div>
+        <el-input
+          :disabled="!todoEdit.openEdit"
+          v-model="todoEdit.textToPush"
+          style="width: 364px; margin-left: 30px; margin-top: 4px"
+        />
+      </div>
+    </div>
+    <div class="content_row">
+      <div class="content_panel" style="margin-right: 40px">
+        <div class="panel_top">
+          <div class="top_content">
+            <div class="circle">3</div>
+            <div class="title_name">问题复核阶段</div>
+            <div class="title_explain">问题复核阶段</div>
+          </div>
+          <el-button
+            v-show="toReviewEdit.openEdit === false"
+            link
+            type="primary"
+            size="small"
+            @click="openToReview"
+            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="toReviewEdit.openEdit === true"
+            type="primary"
+            style="height: 24px; margin-left: auto; width: 48px"
+            @click="saveToReview"
+            >保存</el-button
+          >
+        </div>
+        <div class="subtitle"
+          >问题复核时
+          <el-switch
+            :disabled="!toReviewEdit.openEdit"
+            v-model="toReviewEdit.atProcessing"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div class="subtitle_explain_disable">自动推送给问题发生地点的所有复核员</div>
+        <div class="subtitle"
+          >超期未复核
+          <el-switch
+            :disabled="!toReviewEdit.openEdit"
+            v-model="toReviewEdit.atExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="
+            toReviewEdit.openEdit === true && toReviewEdit.atExpiry === true ? 'editable' : ''
+          "
+          style="margin-top: 10px"
+          >超过<el-input-number
+            :disabled="!toReviewEdit.openEdit || !toReviewEdit.atExpiry"
+            v-model="toReviewEdit.expiryTime"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right"
+          />小时未复核继续推送给复核员</div
+        >
+        <div class="subtitle"
+          >长期未复核
+          <el-switch
+            :disabled="!toReviewEdit.openEdit"
+            v-model="toReviewEdit.atLongTimeExpiry"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div
+          class="subtitle_explain_disable"
+          :class="
+            toReviewEdit.openEdit === true && toReviewEdit.atLongTimeExpiry === true
+              ? 'editable'
+              : ''
+          "
+          style="margin-top: 10px"
+          >超过<el-input-number
+            v-model="toReviewEdit.longTimeValue"
+            :disabled="!toReviewEdit.openEdit || !toReviewEdit.atLongTimeExpiry"
+            :min="0"
+            size="small"
+            style="width: 70px"
+            controls-position="right" />小时未复核再次推送给复核员并抄送给<el-select
+            :disabled="!toReviewEdit.openEdit || !toReviewEdit.atLongTimeExpiry"
+            v-model="toReviewEdit.copyTo"
+            value-key="realname"
+            @click="setCurrentProcess(QuestionStatus.toReview)"
+            placeholder="Select"
+            size="small"
+            style="width: 105px"
+            multiple
+            collapse-tags
+            collapse-tags-tooltip
+          >
+            <el-option
+              v-for="item in directors"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            /> </el-select
+        ></div>
+        <div class="subtitle">推送文案 </div>
+        <el-input
+          :disabled="!toReviewEdit.openEdit"
+          v-model="toReviewEdit.textToPush"
+          style="width: 364px; margin-left: 30px; margin-top: 4px"
+        />
+      </div>
+      <div class="content_panel">
+        <div class="panel_top">
+          <div class="top_content">
+            <div class="circle">4</div>
+            <div class="title_name">问题完结阶段</div>
+            <div class="title_explain">问题完结阶段</div>
+          </div>
+          <el-button
+            v-show="finisheEdit.openEdit === false"
+            link
+            type="primary"
+            size="small"
+            @click="openFinish"
+            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="finisheEdit.openEdit === true"
+            type="primary"
+            style="height: 24px; margin-left: auto; width: 48px"
+            @click="saveFinish"
+            >保存</el-button
+          >
+        </div>
+        <div class="subtitle"
+          >问题完结时
+          <el-switch
+            :disabled="!finisheEdit.openEdit"
+            v-model="finisheEdit.atProcessing"
+            size="small"
+            style="height: 16px; margin-left: 12px"
+          />
+        </div>
+        <div class="subtitle_explain_disable">自动推送给问题发生地点的所有当事人</div>
+        <div class="subtitle">推送文案 </div>
+        <el-input
+          :disabled="!finisheEdit.openEdit"
+          v-model="finisheEdit.textToPush"
+          style="width: 364px; margin-left: 30px; margin-top: 4px"
+        />
+      </div>
+    </div>
+  </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 { ref, onMounted } from 'vue';
+  import { EditPen } from '@element-plus/icons-vue';
+  import { ElMessage } from 'element-plus';
+  import {
+    getIssueProcessMessage,
+    updateIssueProcessMessage,
+  } from '@/api/message/system-notifications';
+  import { QuestionStatus, PushTypeStatus, issueDetilasType, issueProcessType } from '../type';
+  import PersonFilterSelection from '@/views/message/components/PersonFilterSelection.vue';
+  import { SelectedFilterPersonInfo } from '@/api/message/person-group';
+
+  const dialogVisible = ref<boolean>(false);
+  const selectedUser = ref<SelectedFilterPersonInfo[]>([]);
+
+  const currentProcess = ref(1);
+  const setCurrentProcess = (num: number) => {
+    currentProcess.value = num;
+    switch (currentProcess.value) {
+      case QuestionStatus.toAuth:
+        selectedUser.value = toAuthEdit.value.copyTo!;
+        break;
+      case QuestionStatus.todo:
+        selectedUser.value = todoEdit.value.copyTo!;
+      case QuestionStatus.toReview:
+        selectedUser.value = toReviewEdit.value.copyTo!;
+        break;
+    }
+    dialogVisible.value = true;
+  };
+
+  const handleSubmit = (selectedData: SelectedFilterPersonInfo[]) => {
+    // selectedUser.value = selectedData;
+    switch (currentProcess.value) {
+      case QuestionStatus.toAuth:
+        toAuthEdit.value.copyTo = selectedData;
+        break;
+      case QuestionStatus.todo:
+        todoEdit.value.copyTo = selectedData;
+      case QuestionStatus.toReview:
+        toReviewEdit.value.copyTo = selectedData;
+        break;
+    }
+    dialogVisible.value = false;
+  };
+  const handleCancle = () => {
+    dialogVisible.value = false;
+  };
+
+  const openToAuth = () => {
+    toAuthEdit.value.openEdit = true;
+  };
+  const saveToAuth = () => {
+    saveUpdate(toAuthEdit.value, QuestionStatus.toAuth);
+    toAuthEdit.value.openEdit = false;
+  };
+
+  interface panelDetails {
+    openEdit: boolean;
+    atProcessing: boolean;
+    atExpiry?: boolean;
+    expiryTime?: number;
+    atLongTimeExpiry?: boolean;
+    longTimeValue?: number;
+    copyTo?: SelectedFilterPersonInfo[];
+    textToPush: string;
+  }
+
+  const toAuthEdit = ref<panelDetails>({
+    openEdit: false,
+    atProcessing: true,
+    atExpiry: true,
+    expiryTime: 5,
+    atLongTimeExpiry: true,
+    longTimeValue: 5,
+    copyTo: [],
+    textToPush: '您有一条【待审核/超期未审核/长期未审核】的问题单,请及时关注并审核',
+  });
+  const directors = [
+    {
+      value: '陆顶',
+      label: '陆顶',
+    },
+    {
+      value: '孙权',
+      label: '孙权',
+    },
+    {
+      value: '马如龙',
+      label: '马如龙',
+    },
+  ];
+  const opentodo = () => {
+    todoEdit.value.openEdit = true;
+  };
+  const savetodo = () => {
+    saveUpdate(todoEdit.value, QuestionStatus.todo);
+    todoEdit.value.openEdit = false;
+  };
+
+  const todoEdit = ref<panelDetails>({
+    openEdit: false,
+    atProcessing: true,
+    atExpiry: true,
+    expiryTime: 5,
+    atLongTimeExpiry: true,
+    longTimeValue: 5,
+    copyTo: [],
+    textToPush: '您有一条【待复核/超期未复核/长期未复核】的问题单,请及时关注并整改',
+  });
+
+  const openToReview = () => {
+    toReviewEdit.value.openEdit = true;
+  };
+  const saveToReview = () => {
+    saveUpdate(toReviewEdit.value, QuestionStatus.toReview);
+    toReviewEdit.value.openEdit = false;
+  };
+
+  const toReviewEdit = ref<panelDetails>({
+    openEdit: false,
+    atProcessing: true,
+    atExpiry: true,
+    expiryTime: 5,
+    atLongTimeExpiry: true,
+    longTimeValue: 5,
+    copyTo: [],
+    textToPush: '您有一条【待整改/超期未整改/长期未整改】的问题单,请及时关注并整改',
+  });
+
+  const openFinish = () => {
+    finisheEdit.value.openEdit = true;
+  };
+  const saveFinish = () => {
+    saveUpdate(finisheEdit.value, QuestionStatus.finishe);
+    finisheEdit.value.openEdit = false;
+  };
+  const finisheEdit = ref<panelDetails>({
+    openEdit: false,
+    atProcessing: true,
+    textToPush: '流程已完结',
+  });
+
+  //根据流程的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,
+          ccRecipients: obj.copyTo,
+        });
+      }
+    }
+    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;
+      console.log(res);
+      console.log(savedData.value);
+      // return;
+      //问题审核阶段,后端拉取保存数据
+      let subData = savedData.value.find((item) => item.issuePhase === QuestionStatus.toAuth);
+      let atonceData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.atonce,
+      );
+      toAuthEdit.value.atProcessing = atonceData?.isEnabled === 1 ? true : false;
+      let expireData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.expire,
+      );
+      toAuthEdit.value.atExpiry = expireData?.isEnabled === 1 ? true : false;
+      toAuthEdit.value.expiryTime = expireData!.overtime;
+      let longTimeData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.longtime,
+      );
+      toAuthEdit.value.atLongTimeExpiry = longTimeData?.isEnabled === 1 ? true : false;
+      toAuthEdit.value.longTimeValue = longTimeData!.overtime;
+      toAuthEdit.value.copyTo = longTimeData!.ccRecipients;
+      //问题整改阶段,后端拉取保存数据
+      subData = savedData.value.find((item) => item.issuePhase === QuestionStatus.todo);
+      atonceData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.atonce,
+      );
+      todoEdit.value.atProcessing = atonceData?.isEnabled === 1 ? true : false;
+      expireData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.expire,
+      );
+      todoEdit.value.atExpiry = expireData?.isEnabled === 1 ? true : false;
+      todoEdit.value.expiryTime = expireData!.overtime;
+      longTimeData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.longtime,
+      );
+      todoEdit.value.atLongTimeExpiry = longTimeData?.isEnabled === 1 ? true : false;
+      todoEdit.value.longTimeValue = longTimeData!.overtime;
+      todoEdit.value.copyTo = longTimeData!.ccRecipients;
+      //问题复核阶段,后端拉取保存数据
+      subData = savedData.value.find((item) => item.issuePhase === QuestionStatus.toReview);
+      atonceData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.atonce,
+      );
+      toReviewEdit.value.atProcessing = atonceData?.isEnabled === 1 ? true : false;
+      expireData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.expire,
+      );
+      toReviewEdit.value.atExpiry = expireData?.isEnabled === 1 ? true : false;
+      toReviewEdit.value.expiryTime = expireData!.overtime;
+      longTimeData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.longtime,
+      );
+      toReviewEdit.value.atLongTimeExpiry = longTimeData?.isEnabled === 1 ? true : false;
+      toReviewEdit.value.longTimeValue = longTimeData!.overtime;
+      toReviewEdit.value.copyTo = longTimeData!.ccRecipients;
+      //问题完成阶段,后端拉取保存数据
+      subData = savedData.value.find((item) => item.issuePhase === QuestionStatus.finishe);
+      atonceData = subData?.issueProcessMessageList.find(
+        (item) => item.pushType === PushTypeStatus.atonce,
+      );
+      finisheEdit.value.atProcessing = atonceData?.isEnabled === 1 ? true : false;
+    });
+  };
+
+  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>

+ 76 - 22
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,49 @@
   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/system-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 {
+    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,7 +157,7 @@
   const treeRef = ref<InstanceType<typeof ElTree>>();
   const total = ref<number>(0);
   const selected = ref<number>(0);
-  interface treeSelected {
+  export interface treeSelected {
     code: number | string;
     name: string;
     id: number;
@@ -139,7 +176,22 @@
     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();
+      });
+
+    // emit('submit', selectedPeople.value);
   };
   const handleCheckChange = (node, checked) => {
     if (!node.children) {
@@ -166,25 +218,27 @@
       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;
     });
+    getCameraData({});
+    // 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;
+    // });
   });
-  watch(queryStr, (query) => {
-    treeRef.value!.filter(query);
-  });
+  // watch(queryStr, (query) => {
+  //   treeRef.value!.filter(query);
+  // });
 </script>
 
 <style lang="scss" scoped>

+ 123 - 130
src/views/message/systemNotifications/components/problemHandleTable.vue

@@ -1,86 +1,39 @@
 <template>
   <div>
-    <div style="padding-bottom: 20px; border-bottom: 1px solid rgba(0, 0, 0, 0.06)">
+    <div style="padding-bottom: 24px; border-top: 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>
+        <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-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-scrollbar>
+      <ManageContent />
+    </el-scrollbar>
 
-      <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="添加推送车间"
+      title="选择相机范围"
       align-center
       :close-on-click-modal="false"
       style="height: 583px"
@@ -88,7 +41,12 @@
       :destroy-on-close="true"
       class="workShopDialog"
     >
-      <WorkShopTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" />
+      <!-- <WorkShopTree @cancel="handleCancle" @submit="handleSubmit" :selectedUser="selectedUser" /> -->
+      <WorkShopTree
+        @cancel="handleCancle"
+        :selectedUser="selectedUser"
+        :re-fresh-camera="setCameraChosen"
+      />
     </el-dialog>
     <el-dialog
       v-model="showDialog"
@@ -125,33 +83,59 @@
     modifyContent,
     modifyWorkshopList,
     queryWorkshopNamebyIds,
+    getPushRange,
   } from '@/api/message/system-notifications';
+
   import WorkShopTree from './WorkShopTree.vue';
+  import { treeSelected } from './WorkShopTree.vue';
+  import ManageContent from './ManageContent.vue';
   import { ElMessage } from 'element-plus';
-  const problemTableData = ref<problemPhase[]>([]);
+  // const problemTableData = ref<problemPhase[]>([]);
+
+  const cameraChosen = ref<any>([]);
+
+  const options = [
+    {
+      value: 'Option1',
+      label: 'Option1',
+    },
+    {
+      value: 'Option2',
+      label: 'Option2',
+    },
+    {
+      value: 'Option3',
+      label: 'Option3',
+    },
+  ];
+
+  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 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 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 handleProbleEdit = (id: number, _content: string) => {
+  //   showDialog.value = true;
+  //   editId.value = id;
+  //   content.value = _content;
+  // };
 
   const submitDialog = () => {
     modifyContent(content.value, editId.value)
@@ -162,7 +146,7 @@
           plain: true,
         });
         closeDialog();
-        queryIssueData();
+        // queryIssueData();
       })
       .catch((error) => {
         console.error(error);
@@ -178,27 +162,28 @@
     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 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(() => {
@@ -208,41 +193,49 @@
         plain: true,
       });
       workDialog.value = false;
-      queryIssueData();
+      // queryIssueData();
+    });
+  };
+
+  const initCameraChosen = () => {
+    getPushRange().then((res) => {
+      cameraChosen.value = res.map((res) => res.name);
     });
   };
   onMounted(() => {
-    queryIssueData();
+    // queryIssueData();
+    initCameraChosen();
   });
 </script>
 <style lang="scss" scoped>
   .pushWorkShopBar {
-    margin: 24px 0 0 0;
+    margin: 11px 0 8px 0;
     display: flex;
     img {
       cursor: pointer;
     }
-    :nth-of-type(1) {
-      margin-right: 12px;
-      margin-bottom: 16px;
-    }
+    // :nth-of-type(1) {
+    //   margin-right: 12px;
+    //   margin-bottom: 16px;
+    // }
     .pushiWorkShopSpan {
-      width: 56px;
-      height: 20px;
+      // width: 56px;
+      height: 32px;
       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;
+      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 {

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

@@ -1,3 +1,4 @@
+import { SelectedFilterPersonInfo } from '@/api/message/person-group';
 export interface systemTableData {
   id: number;
   // 消息标题
@@ -124,5 +125,39 @@ export const issueStateMapping = {
 
 export enum PushStatusEnum {
   PEDDING = 0, // 等待推送
-  FULFILL = 1  // 已完成
+  FULFILL = 1, // 已完成
+}
+
+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[];
 }