feiyu02
9 天以前 3471eeaf92a2ea32aa06ddfb231ee6c6d8b60bad
新增监管任务界面巡查人员统计信息
已修改13个文件
302 ■■■■ 文件已修改
src/api/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bg-task/FYBgTaskItem.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/list-item/ItemSubTask.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/map/BaseMap.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/time-util.js 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProRecent.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/config/DeviceMatch.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/CompPreCheck.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/components/CompCheckConfirm.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/components/CompCheckSource.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/task/TaskManage.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/task/components/CompMonitorPlan.vue 197 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/task/components/CompSubTaskList.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js
@@ -13,8 +13,8 @@
let ip2_file = 'https://fyami.com.cn/';
if (debug) {
  ip1 = 'http://192.168.0.103:9001/';
  // ip1 = 'http://localhost:9001/';
  // ip1 = 'http://192.168.0.103:9001/';
  ip1 = 'http://localhost:9001/';
  // ip1_file = 'http://192.168.0.138:8080/';
  // ip2 = 'http://192.168.0.138:8080/';
  // ip2_file = 'https://fyami.com.cn/';
src/components/bg-task/FYBgTaskItem.vue
@@ -31,10 +31,10 @@
          </el-col>
          <el-col :span="12">
            <el-text type="default" size="default" tag="div"
              >开始:{{ $fm.formatYMDH(model.startTime) }}</el-text
              >开始:{{ $fm.formatYMDHMS(model.startTime) }}</el-text
            >
            <el-text type="default" size="default" tag="div"
              >结束:{{ $fm.formatYMDH(model.endTime) }}</el-text
              >结束:{{ $fm.formatYMDHMS(model.endTime) }}</el-text
            >
          </el-col>
        </el-row>
src/components/list-item/ItemSubTask.vue
@@ -31,7 +31,7 @@
            <el-icon class="m-r-4" size="16"><AlarmClock /></el-icon>
            <span>时间:</span>
          </div>
          {{ $fm.formatYMD(item.planstarttime) }}
          {{ $fm.formatYMDHM(item.executionstarttime) }}至{{ $fm.formatYMDHM(item.executionendtime) }}
        </div>
        <div class="text-info">
          <div class="text-label">
src/components/map/BaseMap.vue
@@ -7,7 +7,9 @@
import { createMap, map } from '@/utils/map/index';
onMounted(() => {
  // 高德地图初始化
  setTimeout(() => {
  createMap('container');
  }, 1000);
});
onUnmounted(() => {
  map?.destroy();
src/utils/time-util.js
@@ -29,7 +29,15 @@
    }
  },
  formatYMDH(date) {
  formatYMDHM(date) {
    if (date) {
      return this.format(date, 'YYYY-MM-DD HH:mm');
    } else {
      return '----/--/-- --:--';
    }
  },
  formatYMDHMS(date) {
    if (date) {
      return this.format(date, 'YYYY-MM-DD HH:mm:ss');
    } else {
@@ -51,5 +59,47 @@
      format +
      (date < 10 ? '0' + date : date)
    );
  },
  /**
   * 将秒数转换为中文描述格式
   * @param {number} seconds - 秒数
   * @returns {string} 中文时间描述
   */
  formatSecondsToChinese(seconds) {
    if (!seconds || seconds < 0 || isNaN(seconds)) {
      return '--';
    }
    // 定义时间单位和对应的秒数
    const units = [
      { unit: '天', value: 24 * 60 * 60 },
      { unit: '小时', value: 60 * 60 },
      { unit: '分', value: 60 },
      { unit: '秒', value: 1 }
    ];
    let remainingSeconds = Math.floor(seconds);
    let result = '';
    // 遍历时间单位,计算每个单位的数量
    for (const { unit, value } of units) {
      if (remainingSeconds >= value) {
        const count = Math.floor(remainingSeconds / value);
        result += `${count}${unit}`;
        remainingSeconds %= value;
        // // 如果剩余秒数为0,且已经有结果,就可以结束了
        // if (remainingSeconds === 0 && result) {
        //   break;
        // }
        // 如果已经有结果,就可以结束了
        if (result) {
          break;
        }
      }
    }
    return result;
  }
};
src/views/fysp/check/components/CompProRecent.vue
@@ -145,11 +145,11 @@
      return {
        startTime:
          this.activeName === '近三个月'
            ? this.$fm.formatYMDH(threeMonthsAgo)
            ? this.$fm.formatYMDHMS(threeMonthsAgo)
            : this.activeName === '近半年'
              ? this.$fm.formatYMDH(sixMonthsAgo)
              : this.$fm.formatYMDH(oneYearAgo),
        endTime: this.$fm.formatYMDH(today),
              ? this.$fm.formatYMDHMS(sixMonthsAgo)
              : this.$fm.formatYMDHMS(oneYearAgo),
        endTime: this.$fm.formatYMDHMS(today),
        sceneId: this.deepCopyPro.sguid
      };
    },
src/views/fysp/config/DeviceMatch.vue
@@ -133,7 +133,7 @@
    districtname: formSearch.locations.dName,
    towncode: formSearch.locations.tCode,
    townname: formSearch.locations.tName,
    starttime: $fm.formatYMDH(formSearch.time),
    starttime: $fm.formatYMDHMS(formSearch.time),
    scensetypeid: formSearch.scenetype.value,
    online: true,
    // 此处数据来源固定为飞羽监管系统
@@ -157,7 +157,7 @@
function timeFormat(row) {
  const time = row.createTime;
  if (time) {
    return $fm.formatYMDH(time);
    return $fm.formatYMDHMS(time);
  } else {
    return '';
  }
src/views/fysp/evaluation/components/precheck/CompPreCheck.vue
@@ -71,8 +71,8 @@
        districtname: v._locations.dName,
        towncode: v._locations.tCode,
        townname: v._locations.tName,
        starttime: this.$fm.formatYMDH(v.time),
        endtime: this.$fm.formatYMDH(v.time),
        starttime: this.$fm.formatYMDHMS(v.time),
        endtime: this.$fm.formatYMDHMS(v.time),
        scensetypeid: v._scenetype.value,
        online: true,
        sourceType: v.sourceType
src/views/fysp/evaluation/components/precheck/components/CompCheckConfirm.vue
@@ -90,7 +90,7 @@
        districtname: v._locations.dName,
        towncode: v._locations.tCode,
        townname: v._locations.tName,
        starttime: this.$fm.formatYMDH(v.time),
        starttime: this.$fm.formatYMDHMS(v.time),
        scensetypeid: v._scenetype.value,
        online: true,
        sourceType: v.sourceType
src/views/fysp/evaluation/components/precheck/components/CompCheckSource.vue
@@ -309,7 +309,7 @@
      //   districtname: v._locations.dName,
      //   towncode: v._locations.tCode,
      //   townname: v._locations.tName,
      //   starttime: this.$fm.formatYMDH(v.time),
      //   starttime: this.$fm.formatYMDHMS(v.time),
      //   scensetypeid: v._scenetype.value,
      //   online: true,
      //   sourceType: v.sourceType
src/views/fysp/task/TaskManage.vue
@@ -37,10 +37,11 @@
            >计划调整</el-button
          > -->
          <el-tabs model-value="first">
            <el-tab-pane label="监管计划" name="first">
            <el-tab-pane label="巡查计划" name="first">
              <el-row>
                <el-col :span="curSubTaskList ? 16 : 24">
                  <CompMonitorPlan
                    :loading="daytaskLoading"
                    ref="planRef"
                    :task="curTask.data"
                    :day-task-list="curDayTaskList"
@@ -51,6 +52,7 @@
                  <CompSubTaskList
                    create
                    v-model="curSubTaskList"
                    :date="curDay"
                    :loading="subTaskLoading"
                    :create-loading="daytaskCreateLoading"
                    height="56vh"
@@ -60,7 +62,7 @@
                </el-col>
              </el-row>
            </el-tab-pane>
            <el-tab-pane label="监管地图" name="second">
            <el-tab-pane label="场景地图" name="second">
              <CompTaskMap :plans="curMonitorObjList"></CompTaskMap>
            </el-tab-pane>
          </el-tabs>
@@ -283,12 +285,16 @@
    fetchDayTasks() {
      // 获取日任务统计信息
      this.dayTaskLoading = true;
      this.mainLoading = true;
      return taskApi
        .fetchDayTasks(this.curTask.data.tguid)
        .then((res) => {
          this.curDayTaskList = res;
        })
        .finally(() => (this.dayTaskLoading = false));
        .finally(() => {
          this.dayTaskLoading = false;
          this.mainLoading = false;
        });
    },
    editTask() {
      this.$router.push({
src/views/fysp/task/components/CompMonitorPlan.vue
@@ -5,39 +5,141 @@
    @update:model-value="onDateChange"
  >
    <template #header="{ date }">
      <span>{{ title }}</span>
      <div>
        <el-row justify="space-between">
      <el-space>
        <el-tag>总计:{{ taskStatistic.total }}</el-tag>
        <el-tag>完成:{{ taskStatistic.complete }}</el-tag>
        <el-tag>整改:{{ taskStatistic.changed }}</el-tag>
            <el-tag type="default"
              >巡查量:{{
                `${taskStatistic.complete}/${taskStatistic.total}`
              }}</el-tag
            >
            <el-tag type="default"
              >综合整改率:{{
                formatPercent(
                  taskStatistic.changedProblemNum /
                    taskStatistic.totalProblemNum
                )
              }}</el-tag
            >
            <el-tag type="default">整改:{{ taskStatistic.changed }}</el-tag>
      </el-space>
      <!-- <span>{{ date }}</span> -->
          <el-space>
            <el-text>聚焦用户:</el-text>
            <el-select
              v-model="selectedUsers"
              multiple
              clearable
              style="width: 150px"
            >
              <el-option
                v-for="user in taskStatistic.progressPerUser"
                :key="user.userId"
                :label="user.userName"
                :value="user.userName"
              />
            </el-select>
          </el-space>
        </el-row>
        <el-row class="m-t-4">
          <el-space wrap>
            <el-tag
              type="default"
              v-for="user in taskStatistic.progressPerUser"
              :key="user.userId"
            >
              {{
                `${user.userName}:巡查量 ${
                  user.completeTaskNum
                },及时整改率 ${formatPercent(
                  user.changedProblemNumOnTime / user.totalProblemNum
                )},平均耗时 ${
                  user.avgInspectionTime ? user.avgInspectionTime : '--'
                }`
              }}
            </el-tag>
          </el-space>
        </el-row>
      </div>
    </template>
    <template #date-cell="{ data }">
      <div :class="calendarDayClz(data.day)">
        <div style="background-color: #f8f4f4">{{ getDay(data.day) }}</div>
        <template v-if="computeDayTask(data.day)">
          <!-- <el-divider></el-divider> -->
          <el-row justify="space-between" class="m-t-16">
          <el-row
            v-if="computeDayTask(data.day).totalTaskNum > 0"
            justify="space-between"
          >
            <el-space direction="vertical">
              <el-text size="small">总计</el-text>
              <el-text>{{ computeDayTask(data.day).totalTaskNum }}</el-text>
              <el-text size="small" tag="b">巡查量</el-text>
              <el-text size="small"
                >{{ computeDayTask(data.day).completeTaskNum }} /
                {{ computeDayTask(data.day).totalTaskNum }}</el-text
              >
            </el-space>
            <el-space direction="vertical">
              <el-text size="small">完成</el-text>
              <el-text>{{ computeDayTask(data.day).completeTaskNum }}</el-text>
              <el-text size="small" tag="b">综合整改率</el-text>
              <el-text size="small">{{
                formatPercent(
                  computeDayTask(data.day).changedProblemNum /
                    computeDayTask(data.day).totalProblemNum
                )
              }}</el-text>
            </el-space>
            <el-space direction="vertical">
              <el-text size="small">整改</el-text>
              <el-text>{{ computeDayTask(data.day).changedTaskNum }}</el-text>
            </el-space>
            <!-- <el-statistic title="总计" :value="computeDayTask(data.day).totalTaskNum" />
            <el-statistic title="完成" :value="computeDayTask(data.day).completeTaskNum" />
            <el-statistic title="整改" :value="computeDayTask(data.day).changedTaskNum" /> -->
          </el-row>
          <!-- <div>任务总计:{{ computeDayTask(data.day).totalTaskNum }}</div>
          <div>任务完成:{{ computeDayTask(data.day).completeTaskNum }}</div>
          <div>任务整改:{{ computeDayTask(data.day).changedTaskNum }}</div> -->
          <div style="border-top: 1px solid #0000002f" class="m-t-4"></div>
          <div
            v-for="item in computeDayTask(data.day).progressPerUser"
            :key="item.userId"
            class="m-t-4"
          >
            <el-row justify="space-between">
              <el-text
                size="small"
                :type="
                  selectedUsers.includes(item.userName) ? 'primary' : 'info'
                "
                :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'"
                >{{ item.userName }}</el-text
              >
            </el-row>
            <el-row justify="space-between">
              <!-- <el-space> -->
              <el-text
                size="small"
                style="text-align: center; flex: 1"
                :type="
                  selectedUsers.includes(item.userName) ? 'primary' : 'info'
                "
                :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'"
                >{{ item.completeTaskNum }}</el-text
              >
              <el-text
                size="small"
                style="text-align: center; flex: 1"
                :type="
                  selectedUsers.includes(item.userName) ? 'primary' : 'info'
                "
                :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'"
                >{{
                  formatPercent(
                    item.changedProblemNumOnTime / item.totalProblemNum
                  )
                }}</el-text
              >
              <el-text
                size="small"
                style="text-align: center; flex: 1"
                :type="
                  selectedUsers.includes(item.userName) ? 'primary' : 'info'
                "
                :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'"
                >{{
                  timeUtil.formatSecondsToChinese(item.avgInspectionTime)
                }}</el-text
              >
              <!-- </el-space> -->
            </el-row>
          </div>
        </template>
      </div>
    </template>
@@ -47,6 +149,7 @@
import { ref, computed, onMounted, watch } from 'vue';
import taskApi from '@/api/fysp/taskApi';
import dayjs from 'dayjs';
import timeUtil from '@/utils/time-util';
const props = defineProps({
  task: {
@@ -60,16 +163,9 @@
});
const emit = defineEmits(['dateChange']);
// 选中日期
const selectedUsers = ref([]);
// const dateValue = ref(new Date());
const dateValue = ref();
// 日历标题
const title = computed(() => {
  if (props.task) {
    // return `${props.task.name}计划`;
    return `巡查计划`;
  } else {
    return '';
  }
});
// 日历范围
const startDay = computed(() => dayjs(props.task.starttime));
const endDay = computed(() => dayjs(props.task.endtime));
@@ -77,7 +173,6 @@
  startDay.value.toDate(),
  endDay.value.toDate()
]);
// const dateRange = computed(() => [new Date(2024, 4, 27), new Date(2024, 5, 30)]);
// 日期是否在任务范围内
function isDayEnable(day) {
@@ -154,14 +249,52 @@
// 总任务统计
const taskStatistic = computed(() => {
  const res = { total: 0, complete: 0, changed: 0 };
  const res = {
    total: 0,
    complete: 0,
    changed: 0,
    changedProblemNum: 0,
    totalProblemNum: 0
  };
  const userMap = new Map();
  props.dayTaskList.forEach((e) => {
    res.total += e.totalTaskNum;
    res.complete += e.completeTaskNum;
    res.changed += e.changedTaskNum;
    res.changedProblemNum += e.changedProblemNum;
    res.totalProblemNum += e.totalProblemNum;
    e.progressPerUser.forEach((user) => {
      if (!userMap.has(user.userId)) {
        userMap.set(user.userId, {
          userName: user.userName,
          completeTaskNum: 0,
          changedProblemNumOnTime: 0,
          totalProblemNum: 0,
          totalInspectionTime: 0,
          dayTaskNum: 0
  });
      }
      const userItem = userMap.get(user.userId);
      userItem.completeTaskNum += user.completeTaskNum;
      userItem.changedProblemNumOnTime += user.changedProblemNumOnTime;
      userItem.totalProblemNum += user.totalProblemNum;
      userItem.totalInspectionTime += user.avgInspectionTime;
      userItem.dayTaskNum++;
    });
  });
  res.progressPerUser = Array.from(userMap.values()).map((user) => ({
    ...user,
    completeTaskNum: Math.round(user.completeTaskNum * 100) / 100,
    avgInspectionTime: timeUtil.formatSecondsToChinese(
      user.totalInspectionTime / user.dayTaskNum
    )
  }));
  return res;
});
const formatPercent = (num) => {
  return isNaN(num) ? '0%' : parseInt(num * 100) + '%';
};
/********************** 初始化 *********************************/
// watch(
src/views/fysp/task/components/CompSubTaskList.vue
@@ -1,6 +1,6 @@
<template>
  <el-row justify="space-between">
    <el-text>单日计划</el-text>
    <el-text>{{dateStr}}计划</el-text>
    <div v-show="create && data && data.length > 0">
      <el-button
        icon="IconPrinter"
@@ -63,6 +63,7 @@
      </div>
    </el-scrollbar>
  </div>
  <!-- 编辑巡查子任务 -->
  <el-dialog
    v-model="dialogVisible"
    width="600"
@@ -78,6 +79,7 @@
      @cancel="dialogVisible = false"
    ></CompSubTaskEdit>
  </el-dialog>
  <!-- 巡查单下载 -->
  <SceneInspectFile
    v-model="downloadDialog"
    :value="downloadSceneList"
@@ -85,13 +87,14 @@
</template>
<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { ElMessageBox, ElNotification, ElMessage } from 'element-plus';
import { ElMessageBox, ElNotification, ElMessage, dayjs } from 'element-plus';
import CompSubTaskEdit from './CompSubTaskEdit.vue';
import SceneInspectFile from '@/views/fysp/scene/SceneInspectFile.vue';
import subtaskApi from '@/api/fysp/subtaskApi';
const props = defineProps({
  modelValue: Array,
  date: Date,
  height: {
    type: String,
    default: '70vh'
@@ -110,6 +113,8 @@
const emit = defineEmits(['submit', 'add', 'remove', 'update:modelValue']);
const dateStr = computed(()=> dayjs(props.date).format('MM月DD日'))
function remove(item) {
  if (item.status == '未执行') {
    ElMessageBox.confirm('是否移除监管任务', `移除确认`, {