1. 初步完成评估任务整体逻辑;
2. 新增评估记录下载功能(待完善)
已修改18个文件
已添加3个文件
1013 ■■■■■ 文件已修改
src/api/fysp/downloadApi.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/evaluateApi.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bg-task/FYBgTaskCard.vue 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bg-task/FYBgTaskDialog.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bg-task/FYBgTaskItem.vue 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/core/Header.vue 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/table/FYTable.vue 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enum/bgTask.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/element/base.scss 96 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/element/layout.js 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/DataSource.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/ResultManage.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/CompEvaTask.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/CompQuickSet.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/CompPreCheck.vue 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/components/CompCheckConfirm.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/components/CompCheckExemption.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/precheck/components/CompCheckSource.vue 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/downloadApi.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
import { $fysp } from '../index';
export default {
  downloadFile(url) {
    return $fysp.get(`files/${url}`);
  }
};
src/api/fysp/evaluateApi.js
@@ -1,3 +1,4 @@
import { Base64 } from 'js-base64';
import { $fysp } from '../index';
export default {
@@ -15,5 +16,26 @@
   */
  fetchEvaluationRule(param) {
    return $fysp.post(`evaluationrule/find`, param).then((res) => res.data);
  }
  },
  autoEvaluate(param) {
    return $fysp.post(`evaluation/auto`, param).then((res) => res.data);
  },
  downloadAutoEvaluation(param) {
    return $fysp
      .post(`evaluation/auto/record/download`, param, { responseType: 'blob' })
      .then((res) => {
        // return res.data;
        const name = Base64.decode(res.headers.get('filename'));
        const url = window.URL.createObjectURL(res.data);
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', name);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
      });
  },
};
src/api/index.js
@@ -9,9 +9,9 @@
let ip2_file = 'https://fyami.com.cn/';
if (debug) {
  ip1 = 'http://192.168.0.138:8082/';
  ip1 = 'http://192.168.1.9:8082/';
  // ip1_file = 'http://47.100.191.150:9005/';
  ip2 = 'http://192.168.0.138:8080/';
  ip2 = 'http://192.168.1.9:8080/';
  // ip2_file = 'https://fyami.com.cn/';
}
src/components.d.ts
@@ -13,6 +13,7 @@
    ElAside: typeof import('element-plus/es')['ElAside']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElBacktop: typeof import('element-plus/es')['ElBacktop']
    ElBadge: typeof import('element-plus/es')['ElBadge']
    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
    ElButton: typeof import('element-plus/es')['ElButton']
@@ -68,6 +69,8 @@
    Footer: typeof import('./components/core/Footer.vue')['default']
    FormCol: typeof import('./components/layout/FormCol.vue')['default']
    FYBgTaskCard: typeof import('./components/bg-task/FYBgTaskCard.vue')['default']
    FYBgTaskDialog: typeof import('./components/bg-task/FYBgTaskDialog.vue')['default']
    FYBgTaskItem: typeof import('./components/bg-task/FYBgTaskItem.vue')['default']
    FYForm: typeof import('./components/form/FYForm.vue')['default']
    FYOptionLocation: typeof import('./components/search-option/FYOptionLocation.vue')['default']
    FYOptionOnlineStatus: typeof import('./components/search-option/FYOptionOnlineStatus.vue')['default']
src/components/bg-task/FYBgTaskCard.vue
@@ -1,200 +1,160 @@
<template>
  <el-card class="m-b-8" shadow="always" :body-style="{ padding: '8px' }">
    <el-row>
      <el-col :span="4">
        <div class="status-btn">
          <el-icon v-if="waiting" color="var(--el-color-info)" :size="50"><VideoPlay /></el-icon>
          <el-icon v-else-if="running" color="var(--el-color-primary)" :size="50" class="is-loading"
            ><Loading
          /></el-icon>
          <el-icon v-else-if="success" color="var(--el-color-success)" :size="50"
            ><CircleCheck
          /></el-icon>
          <el-icon v-else-if="fail" color="var(--el-color-error)" :size="50"
            ><CircleClose
          /></el-icon>
          <el-icon v-else color="var(--el-color-warning)" :size="50"><Warning /></el-icon>
          <el-text type="info" size="small" style="position: absolute; bottom: 0">{{
            nameToLabel(model.status)
          }}</el-text>
        </div>
      </el-col>
      <el-col :span="20" class="p-l-8">
  <el-card shadow="never" :body-style="{ padding: 0 }">
    <template #header>
        <el-row justify="space-between">
          <el-text class="m-l-4px w-300px" tag="b" size="large" truncated>{{ model.name }}</el-text>
          <el-tag>{{ nameToLabel(model.type) }}</el-tag>
        </el-row>
        <el-row class="p-v-8" align="bottom">
          <el-col :span="12">
            <span class="timer">{{ time }}</span>
            <el-text type="info" size="small" tag="div">运行时长</el-text>
          </el-col>
          <el-col :span="12">
            <el-text type="default" size="default" tag="div"
              >开始:{{ $fm.formatYMDH(model.startTime) }}</el-text
            >
            <el-text type="default" size="default" tag="div"
              >结束:{{ $fm.formatYMDH(model.endTime) }}</el-text
            >
          </el-col>
        </el-row>
        <el-row justify="end" align="bottom">
          <!-- <span class="f-s color-i">ID:{{ model.id }}</span> -->
          <el-row>
            <FYReconfrimButton v-if="waiting" @confirm="startTask" v-model="startConfirm">
        <div>
          <div><el-text tag="b" size="large">后台任务</el-text></div>
          <el-text size="small" type="info">显示当前正在进行的后台耗时任务状态</el-text>
        </div>
              <el-button
                plain
                icon="VideoPlay"
          icon="Refresh"
                type="primary"
                size="small"
                :loading="false"
                @click="startConfirm = true"
                >开始任务</el-button
          size="default"
          :loading="loading"
          @click="fetchTask"
          >刷新任务</el-button
              >
            </FYReconfrimButton>
            <FYReconfrimButton v-if="running" @confirm="stopTask" v-model="stopConfirm">
              <el-button
                icon="VideoPause"
                plain
                type="danger"
                size="small"
                :loading="false"
                @click="stopConfirm = true"
                >强制结束</el-button
      </el-row>
      <!-- <el-row>
        <el-button type="default" size="default" @click="newTestTask">新增测试任务</el-button>
        <el-button type="default" size="default" @click="startNewTestTask"
          >新建并运行一个测试任务</el-button
              >
            </FYReconfrimButton>
            <FYReconfrimButton v-if="!running" @confirm="removeTask" v-model="removeConfirm">
              <el-button
                icon="Delete"
                plain
                type="danger"
                size="small"
                :loading="false"
                @click="removeConfirm = true"
                >移除任务</el-button
        <el-button type="default" size="default" @click="shutDownTask"
          >强制关闭所有测试任务</el-button
              >
            </FYReconfrimButton>
            <el-button
              v-if="success"
              plain
              type="success"
              size="small"
              :loading="false"
              @click="gotoResult"
              >查看结果<el-icon class="m-l-4"><Right /></el-icon
            ></el-button>
          </el-row>
        </el-row>
      </el-col>
    </el-row>
      </el-row> -->
    </template>
    <el-scrollbar height="70vh" class="scrollbar">
      <template v-for="(v, i) in taskList" :key="i">
        <FYBgTaskItem
          :model="v"
          :index="i"
          @start="startTask"
          @shutDown="shutDownTask"
          @remove="removeTask"
          @gotoResult="gotoResult"
        ></FYBgTaskItem>
      </template>
    </el-scrollbar>
  </el-card>
</template>
<script>
import { nTlBgTask, BG_TASK_STATUS } from '@/enum/bgTask';
import { useTimer } from '@/composables/timer';
// import { useTimer } from '@/composables/timer2';
/**
 * è‡ªåŠ¨è¯„ä¼°ä»»åŠ¡ç®¡ç†
 */
import { useFetchData } from '@/composables/fetchData';
import bgtaskApi from '@/api/fysp/bgtaskApi';
import { enumBgTask, BG_TASK_TYPE, BG_TASK_STATUS } from '@/enum/bgTask';
export default {
  setup() {
    const { time, startTimer, pauseTimer, stopTimer, count } = useTimer();
    return { time, startTimer, pauseTimer, stopTimer, count };
    const { loading, fetchData } = useFetchData();
    return { loading, fetchData };
  },
  props: {
    model: Object,
    index: Number
    modelValue: Number
  },
  emits: ['start', 'shutDown', 'remove', 'gotoResult'],
  emits: ['update:modelValue'],
  data() {
    return {
      startConfirm: false,
      stopConfirm: false,
      removeConfirm: false
      taskList: [],
      taskIndex: 0
    };
  },
  watch: {
    'model.status': {
    taskList: {
      handler(nV) {
        switch (nV) {
          case BG_TASK_STATUS.WAITING.name:
            this.stopTimer();
            break;
          case BG_TASK_STATUS.RUNNING.name:
            this.startTimer();
            break;
          case BG_TASK_STATUS.SUCCESS.name:
          case BG_TASK_STATUS.FAIL.name:
          case BG_TASK_STATUS.SHUTDOWN.name:
            this.pauseTimer();
            break;
          default:
            this.stopTimer();
            break;
        let count = 0;
        for (const e of nV) {
          if (e.status == BG_TASK_STATUS.RUNNING.name) {
            count++;
        }
        this.count = this.model.runTime
      },
      immediate: true
    }
        this.$emit('update:modelValue', count);
  },
  computed: {
    waiting() {
      return this.model.status == BG_TASK_STATUS.WAITING.name;
    },
    running() {
      return this.model.status == BG_TASK_STATUS.RUNNING.name;
    },
    success() {
      return this.model.status == BG_TASK_STATUS.SUCCESS.name;
    },
    fail() {
      return this.model.status == BG_TASK_STATUS.FAIL.name;
    },
    shutdown() {
      return this.model.status == BG_TASK_STATUS.SHUTDOWN.name;
      deep: true
    }
  },
  methods: {
    nameToLabel(name) {
      const t = nTlBgTask(name);
      return t.label;
    },
    startTask() {
      this.$emit('start', this.index, (res) => {
        if (res) {
          this.startTimer();
        }
    addTask() {},
    newTestTask() {
      this.fetchData((page, pageSize) => {
        return bgtaskApi.newTestTask(`Test-Task-${++this.taskIndex}`).then((res) => {
          this.taskList.push(res.data);
        });
      });
    },
    stopTask() {
      this.$emit('shutDown', this.index, (res) => {
        if (res) {
          this.stopTimer();
        }
    startNewTestTask() {
      this.fetchData((page, pageSize) => {
        return bgtaskApi.startNewTestTask(`Test-Task-${++this.taskIndex}`).then((res) => {
          this.taskList.push(res.data);
        });
      });
    },
    removeTask() {
      this.$emit('remove', this.index, (res) => {
        if (res) {
          // this.stopTimer();
        }
    _getParam(taskStatus) {
      return {
        type: taskStatus.type,
        id: taskStatus.id
      };
    },
    fetchTask() {
      this.fetchData((page, pageSize) => {
        return bgtaskApi
          .fetchTaskStatus({
            // type: BG_TASK_TYPE.AUTO_SCORE.name
          })
          .then((res) => {
            this.taskList = res.data;
          });
      });
    },
    gotoResult() {
      this.$emit('gotoResult', this.index);
    startTask(index, callback) {
      this.fetchData((page, pageSize) => {
        const param = this._getParam(this.taskList[index]);
        return bgtaskApi.startTask(param).then((res) => {
          this.taskList[index] = res.data;
          callback(true);
        });
      });
    },
    shutDownTask(index, callback) {
      this.fetchData((page, pageSize) => {
        const param = this._getParam(this.taskList[index]);
        return bgtaskApi.shutDownTask(param).then((res) => {
          if (index && res.data && res.data.length == 1) {
            this.taskList[index] = res.data[0];
          } else {
            res.data.forEach((e) => {
              let v = this.taskList.find((value) => {
                return value.id == e.id;
              });
              const i = this.taskList.indexOf(v);
              this.taskList[i] = e;
            });
    }
          callback(true);
        });
      });
    },
    removeTask(index, callback) {
      this.fetchData((page, pageSize) => {
        const param = this._getParam(this.taskList[index]);
        return bgtaskApi.removeTask(param).then((res) => {
          if (res.data) {
            this.taskList.splice(index, 1);
            callback(true);
          }
        });
      });
    },
    gotoResult(index) {}
  }
};
</script>
<style scoped>
.status-btn {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: var(--el-border);
  border-radius: var(--el-border-radius-base);
}
.timer {
  font-size: 30px;
.scrollbar {
  padding: 8px;
}
</style>
src/components/bg-task/FYBgTaskDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
<template>
  <el-popover placement="bottom" :width="600" trigger="click">
    <template #reference>
      <el-badge :value="runningNum" :hidden="runningNum == 0" class="m-r-16">
        <el-button circle>
          <el-icon v-if="runningNum > 0" color="red" class="is-loading"><Clock /></el-icon>
          <el-icon v-else><Clock /></el-icon>
        </el-button>
      </el-badge>
    </template>
    <!-- <el-button circle icon="Close" type="danger" @click=</el-button> -->
    <FYBgTaskCard v-model="runningNum"></FYBgTaskCard>
  </el-popover>
</template>
<script>
export default {
  data() {
    return {
      runningNum: 0
    };
  }
};
</script>
<style scoped></style>
src/components/bg-task/FYBgTaskItem.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,218 @@
<template>
  <el-card class="m-b-8" shadow="always" :body-style="{ padding: '8px' }">
    <el-row>
      <el-col :span="4">
        <div class="status-btn">
          <el-icon v-if="waiting" color="var(--el-color-info)" :size="50"><VideoPlay /></el-icon>
          <el-icon v-else-if="running" color="var(--el-color-primary)" :size="50" class="is-loading"
            ><Loading
          /></el-icon>
          <el-icon v-else-if="success" color="var(--el-color-success)" :size="50"
            ><CircleCheck
          /></el-icon>
          <el-icon v-else-if="fail" color="var(--el-color-error)" :size="50"
            ><CircleClose
          /></el-icon>
          <el-icon v-else color="var(--el-color-warning)" :size="50"><Warning /></el-icon>
          <el-text type="info" size="small" style="position: absolute; bottom: 0">{{
            nameToLabel(model.status)
          }}</el-text>
        </div>
      </el-col>
      <el-col :span="20" class="p-l-8">
        <el-row justify="space-between">
          <el-text class="m-l-4px w-300px" tag="b" size="large" truncated>{{ model.name }}</el-text>
          <el-tag>{{ nameToLabel(model.type) }}</el-tag>
        </el-row>
        <el-row class="p-v-8" align="bottom">
          <el-col :span="12">
            <span class="timer">{{ time }}</span>
            <el-text type="info" size="small" tag="div">运行时长</el-text>
          </el-col>
          <el-col :span="12">
            <el-text type="default" size="default" tag="div"
              >开始:{{ $fm.formatYMDH(model.startTime) }}</el-text
            >
            <el-text type="default" size="default" tag="div"
              >结束:{{ $fm.formatYMDH(model.endTime) }}</el-text
            >
          </el-col>
        </el-row>
        <el-row justify="end" align="bottom">
          <!-- <span class="f-s color-i">ID:{{ model.id }}</span> -->
          <el-row>
            <FYReconfrimButton v-if="waiting" @confirm="startTask" v-model="startConfirm">
              <el-button
                plain
                icon="VideoPlay"
                type="primary"
                size="small"
                :loading="false"
                @click="startConfirm = true"
                >开始任务</el-button
              >
            </FYReconfrimButton>
            <FYReconfrimButton v-if="running" @confirm="stopTask" v-model="stopConfirm">
              <el-button
                icon="VideoPause"
                plain
                type="danger"
                size="small"
                :loading="false"
                @click="stopConfirm = true"
                >强制结束</el-button
              >
            </FYReconfrimButton>
            <FYReconfrimButton v-if="!running" @confirm="removeTask" v-model="removeConfirm">
              <el-button
                icon="Delete"
                plain
                type="danger"
                size="small"
                :loading="false"
                @click="removeConfirm = true"
                >移除任务</el-button
              >
            </FYReconfrimButton>
            <template v-if="success">
              <el-button
                v-if="btnType"
                plain
                type="success"
                size="small"
                :loading="false"
                @click="download"
                >下载文件<el-icon class="m-l-4"><Right /></el-icon
              ></el-button>
              <el-button
                v-else
                plain
                type="success"
                size="small"
                :loading="false"
                @click="gotoResult"
                >查看结果<el-icon class="m-l-4"><Right /></el-icon
              ></el-button>
            </template>
          </el-row>
        </el-row>
      </el-col>
    </el-row>
  </el-card>
</template>
<script>
import { nTlBgTask, BG_TASK_STATUS, BG_TASK_TYPE } from '@/enum/bgTask';
import { useTimer } from '@/composables/timer';
import downloadApi from '@/api/fysp/downloadApi';
// import { useTimer } from '@/composables/timer2';
export default {
  setup() {
    const { time, startTimer, pauseTimer, stopTimer, count } = useTimer();
    return { time, startTimer, pauseTimer, stopTimer, count };
  },
  props: {
    model: Object,
    index: Number
  },
  emits: ['start', 'shutDown', 'remove', 'gotoResult'],
  data() {
    return {
      startConfirm: false,
      stopConfirm: false,
      removeConfirm: false
    };
  },
  watch: {
    'model.status': {
      handler(nV) {
        switch (nV) {
          case BG_TASK_STATUS.WAITING.name:
            this.stopTimer();
            break;
          case BG_TASK_STATUS.RUNNING.name:
            this.startTimer();
            break;
          case BG_TASK_STATUS.SUCCESS.name:
          case BG_TASK_STATUS.FAIL.name:
          case BG_TASK_STATUS.SHUTDOWN.name:
            this.pauseTimer();
            break;
          default:
            this.stopTimer();
            break;
        }
        this.count = this.model.runTime;
      },
      immediate: true
    }
  },
  computed: {
    btnType() {
      return this.model.type == BG_TASK_TYPE.DOCUMENT.name;
    },
    waiting() {
      return this.model.status == BG_TASK_STATUS.WAITING.name;
    },
    running() {
      return this.model.status == BG_TASK_STATUS.RUNNING.name;
    },
    success() {
      return this.model.status == BG_TASK_STATUS.SUCCESS.name;
    },
    fail() {
      return this.model.status == BG_TASK_STATUS.FAIL.name;
    },
    shutdown() {
      return this.model.status == BG_TASK_STATUS.SHUTDOWN.name;
    }
  },
  methods: {
    nameToLabel(name) {
      const t = nTlBgTask(name);
      return t.label;
    },
    startTask() {
      this.$emit('start', this.index, (res) => {
        if (res) {
          this.startTimer();
        }
      });
    },
    stopTask() {
      this.$emit('shutDown', this.index, (res) => {
        if (res) {
          this.stopTimer();
        }
      });
    },
    removeTask() {
      this.$emit('remove', this.index, (res) => {
        if (res) {
          // this.stopTimer();
        }
      });
    },
    gotoResult() {
      this.$emit('gotoResult', this.index);
    },
    download() {
      downloadApi.downloadFile(this.model.extra);
    }
  }
};
</script>
<style scoped>
.status-btn {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: var(--el-border);
  border-radius: var(--el-border-radius-base);
}
.timer {
  font-size: 30px;
}
</style>
src/components/core/Header.vue
@@ -1,19 +1,15 @@
<template>
  <el-row align="middle" class="header">
    <el-col :span="1">
      <el-button
        @click="collapsedSider"
        icon="Fold"
        circle
        :class="rotateIcon"
      />
    </el-col>
    <el-col :span="18">
      <el-breadcrumb separator="/" style="white-space: nowrap;">
    <el-col :span="12">
      <el-space>
        <el-button @click="collapsedSider" icon="Fold" circle :class="rotateIcon" />
        <el-breadcrumb separator="/" style="white-space: nowrap">
        <el-breadcrumb-item v-for="(t, i) in navTitles" :key="i">{{t}}</el-breadcrumb-item>
      </el-breadcrumb>
      </el-space>
    </el-col>
    <el-col :span="5" class="logout">
    <el-col :span="12" class="logout">
      <FYBgTaskDialog></FYBgTaskDialog>
        <el-button icon="SwitchButton">退出登录</el-button>
    </el-col>
  </el-row>
@@ -25,16 +21,16 @@
  props: {
    collapse: {
      type: Boolean,
      default: false,
      default: false
    },
    navTitles: {
      type: Array,
      default: () => ['home','promotion list', 'promotion detail'],
      default: () => ['home', 'promotion list', 'promotion detail']
    }
  },
  data() {
    return {
      isCollapsed: this.collapse,
      isCollapsed: this.collapse
    };
  },
  watch: {},
@@ -44,7 +40,7 @@
     */
    rotateIcon() {
      return ['menu-icon', this.isCollapsed ? 'rotate-icon' : ''];
    },
    }
  },
  methods: {
    /**
@@ -53,8 +49,8 @@
    collapsedSider() {
      this.isCollapsed = !this.isCollapsed;
      this.$emit('collapsedSider', this.isCollapsed);
    },
  },
    }
  }
};
</script>
src/components/table/FYTable.vue
@@ -58,18 +58,18 @@
      currentPage: 1,
      pageSize: 20,
      loading: false
    }
    };
  },
  emits: ['search'],
  watch: {
    currentPage(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch()
        this.onSearch();
      }
    },
    pageSize(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch()
        this.onSearch();
      }
    }
  },
@@ -80,7 +80,7 @@
     * å›žè°ƒå‡½æ•°æŽ¥æ”¶ä¸€ä¸ªå¯¹è±¡ï¼ŒåŒ…括表格数据数组data和数据总数total
     */
    onSearch() {
      this.loading = true
      this.loading = true;
      this.$emit(
        'search',
        {
@@ -88,37 +88,37 @@
          pageSize: this.pageSize
        },
        (res) => {
          this.tableData = res.data
          this.total = res.total ? res.total : 0
          this.loading = false
          this.tableData = res.data;
          this.total = res.total ? res.total : 0;
          this.loading = false;
        }
      )
      );
    },
    calcTableHeight() {
      const h1 = this.$refs.searchRef.$el.offsetHeight
      const h2 = this.$refs.paginationRef ? this.$refs.paginationRef.$el.offsetHeight : 0
      const h3 = this.$refs.expandRef.$el.offsetHeight
      const h = h1 + h2 + h3
      const h1 = this.$refs.searchRef.$el.offsetHeight;
      const h2 = this.$refs.paginationRef ? this.$refs.paginationRef.$el.offsetHeight : 0;
      const h3 = this.$refs.expandRef.$el.offsetHeight;
      const h = h1 + h2 + h3;
      // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
      return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`
      return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
    },
    tableRowClassName({ row }) {
      if (this.rowClassName) {
        if (typeof this.rowClassName == 'string') {
          return this.rowClassName
          return this.rowClassName;
        } else if (typeof this.rowClassName == 'function') {
          return this.rowClassName({ row })
          return this.rowClassName({ row });
        }
      } else {
        return row.extension1 != '0' ? 'online-row' : 'offline-row'
        return row.extension1 != '0' ? 'online-row' : 'offline-row';
      }
    }
  },
  mounted() {
    this.tableHeight = this.calcTableHeight()
    this.onSearch()
    this.tableHeight = this.calcTableHeight();
    this.onSearch();
  }
}
};
</script>
<style>
src/enum/bgTask.js
@@ -11,6 +11,11 @@
    name: 'AUTO_SCORE',
    label: '自动评估任务',
    value: '1'
  },
  DOCUMENT: {
    name: 'DOCUMENT',
    label: '文档生成任务',
    value: '2'
  }
});
@@ -52,6 +57,8 @@
    return BG_TASK_TYPE[name];
  } else if (name in BG_TASK_STATUS) {
    return BG_TASK_STATUS[name];
  } else {
    return '-'
  }
}
src/router/index.js
@@ -82,13 +82,15 @@
    //评估数据源
    name: 'fyspDatasource',
    path: '/fysp/evaluation/datasource',
    component: () => import('@/views/fysp/evaluation/DataSource.vue')
    component: () => import('@/views/fysp/evaluation/DataSource.vue'),
    meta: { keepAlive: true }
  },
  {
    //评估管理
    name: 'fyspResultManage',
    path: '/fysp/evaluation/resultManage',
    component: () => import('@/views/fysp/evaluation/ResultManage.vue')
    component: () => import('@/views/fysp/evaluation/ResultManage.vue'),
    meta: { keepAlive: true }
  },
  {
    //场景信息
src/styles/element/base.scss
@@ -1,54 +1,34 @@
//
@mixin font-size($size) {
  font-size: var($size);
/************************************** font size **************************************/
$fontsize: (
  s: var(--el-font-size-small),
  b: var(--el-font-size-base),
  m: var(--el-font-size-medium),
  l: var(--el-font-size-large)
);
@each $dName, $dValue in $fontsize {
  .f-#{$dName} {
    font-size: #{$dValue};
  }
}
.f-s {
  @include font-size(--el-font-size-small);
}
/************************************** color **************************************/
$colors: (
  p: var(--el-color-primary),
  s: var(--el-color-success),
  w: var(--el-color-warning),
  d: var(--el-color-danger),
  e: var(--el-color-error),
  i: var(--el-color-info)
);
.f-b {
  @include font-size(--el-font-size-base);
@each $dName, $dValue in $colors {
  .color-#{$dName} {
    color: #{$dValue};
}
.f-m {
  @include font-size(--el-font-size-medium);
  .b-color-#{$dName} {
    background-color: #{$dValue};
}
.f-l {
  @include font-size(--el-font-size-large);
}
@mixin color($value) {
  color: var($value);
}
.color-p {
  @include color(--el-color-primary);
}
.color-s {
  @include color(--el-color-success);
}
.color-w {
  @include color(--el-color-warning);
}
.color-d {
  @include color(--el-color-danger);
}
.color-e {
  @include color(--el-color-error);
}
.color-i {
  @include color(--el-color-info);
}
.b-color-aqua {
  background-color: aqua;
}
/************************************** å†…外边距 **************************************/
@@ -85,8 +65,28 @@
  }
}
.w-300px {
  width: 300px;
/************************************** å®½é«˜ **************************************/
$csize: (
  small: var(--el-component-size-small),
  default: var(--el-component-size-default),
  large: var(--el-component-size-large)
);
$ws: (20px, 40px, 60px, 100px, 150px, 300px);
@each $name, $value in $csize {
  .w-#{$name} {
    width: #{$value};
  }
  .h-#{$name} {
    height: #{$value};
  }
}
@each $i in $ws {
  .w-#{$i} {
    width: #{$i};
  }
  .h-#{$i} {
    height: #{$i};
  }
}
//
src/styles/element/layout.js
@@ -14,19 +14,19 @@
      pull: 0,
    },
    md: {
      span: 16,
      offset: 3,
      span: 20,
      offset: 2,
      push: 0,
      pull: 0,
    },
    lg: {
      span: 14,
      offset: 4,
      span: 18,
      offset: 3,
      push: 0,
      pull: 0,
    },
    xl: {
      span: 12,
      span: 14,
      offset: 5,
      push: 0,
      pull: 0,
src/views/fysp/evaluation/DataSource.vue
@@ -1,10 +1,10 @@
<template>
  <el-row :gutter="16">
    <el-col :span="16">
      <CompPreCheck></CompPreCheck>
      <CompPreCheck @start-task="refreshTask"></CompPreCheck>
    </el-col>
    <el-col :span="8">
      <CompEvaTask></CompEvaTask>
      <CompEvaTask ref="refEvaTask"></CompEvaTask>
    </el-col>
  </el-row>
</template>
@@ -17,6 +17,11 @@
  components: { CompPreCheck, CompEvaTask },
  data() {
    return {};
  },
  methods:{
    refreshTask(){
      this.$refs.refEvaTask.fetchTask()
    }
  }
};
</script>
src/views/fysp/evaluation/ResultManage.vue
@@ -21,7 +21,9 @@
    </template>
    <template #options-expand>
      <el-form :inline="true">
      <CompQuickSet @quick-set="setOptions"></CompQuickSet>
      </el-form>
    </template>
    <template #table-column>
@@ -70,8 +72,8 @@
      <el-table-column prop="biManagementCompany" label="物业" min-width="110"/> -->
      <el-table-column fixed="right" align="right" label="操作" width="160">
        <template #header>
          <el-button icon="DocumentAdd" size="default" type="success" @click="drawer = true"
            >自动评估</el-button
          <el-button icon="Download" size="default" type="success" @click="download"
            >下载结果</el-button
          >
        </template>
        <template #default="{ row }">
@@ -102,23 +104,27 @@
    };
  },
  methods: {
    setOptions(param) {
      this.formSearch.locations = param.locations;
      this.formSearch.scenetype = param.scenetype;
      this.$refs.tableRef.onSearch()
    },
    onSearch(page, func) {
    _getParam() {
      const { locations, scenetype, time } = this.formSearch;
      const area = {
      return {
        provincecode: locations.pCode,
        provincename: locations.pName,
        citycode: locations.cCode,
        cityname: locations.cName,
        districtcode: locations.dCode,
        districtname: locations.dName,
        starttime: dayjs(time).format('YYYY-MM-DD'),
        starttime: dayjs(time).format('YYYY-MM-DD HH:mm:ss'),
        scensetypeid: scenetype.value
      };
    },
    setOptions(param) {
      this.formSearch.locations = param.locations;
      this.formSearch.scenetype = param.scenetype;
      this.formSearch.sourceType = param.sourceType;
      this.$refs.tableRef.onSearch();
    },
    onSearch(page, func) {
      const area = this._getParam()
      evaluateApi.fetchAutoEvaluation(area).then((res) => {
        if (typeof func === 'function') {
          func({ data: res.data });
@@ -127,6 +133,12 @@
          this.getFilters(res.data);
        }
      });
    },
    download() {
      const area = this._getParam()
      evaluateApi.downloadAutoEvaluation(area).then(res=>{
        this.$parent
      })
    },
    getFilters(data) {
      const townList = [];
@@ -166,5 +178,4 @@
  }
};
</script>
<style scoped>
</style>
<style scoped></style>
src/views/fysp/evaluation/components/CompEvaTask.vue
@@ -15,7 +15,7 @@
          >刷新任务</el-button
        >
      </el-row>
      <el-row>
      <!-- <el-row>
        <el-button type="default" size="default" @click="newTestTask">新增测试任务</el-button>
        <el-button type="default" size="default" @click="startNewTestTask"
          >新建并运行一个测试任务</el-button
@@ -23,18 +23,18 @@
        <el-button type="default" size="default" @click="shutDownTask"
          >强制关闭所有测试任务</el-button
        >
      </el-row>
      </el-row> -->
    </template>
    <el-scrollbar height="70vh" class="scrollbar">
      <template v-for="(v, i) in taskList" :key="i">
        <FYBgTaskCard
        <FYBgTaskItem
          :model="v"
          :index="i"
          @start="startTask"
          @shutDown="shutDownTask"
          @remove="removeTask"
          @gotoResult="gotoResult"
        ></FYBgTaskCard>
        ></FYBgTaskItem>
      </template>
    </el-scrollbar>
  </el-card>
@@ -59,6 +59,9 @@
    };
  },
  methods: {
    addTask(){
    },
    newTestTask() {
      this.fetchData((page, pageSize) => {
        return bgtaskApi.newTestTask(`Test-Task-${++this.taskIndex}`).then((res) => {
@@ -84,7 +87,7 @@
      this.fetchData((page, pageSize) => {
        return bgtaskApi
          .fetchTaskStatus({
            type: BG_TASK_TYPE.TEST.name
            type: BG_TASK_TYPE.AUTO_SCORE.name
          })
          .then((res) => {
            this.taskList = res.data;
src/views/fysp/evaluation/components/CompQuickSet.vue
@@ -87,19 +87,19 @@
            scenetype: { label: '搅拌站', value: '3' },
            sourceType: 2,
          },
          {
            name: '徐汇汽修',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310104',
              dName: '徐汇区'
            },
            scenetype: { label: '汽修', value: '7' },
            sourceType: 1,
          }
          // {
          //   name: '徐汇汽修',
          //   locations: {
          //     pCode: '31',
          //     pName: '上海市',
          //     cCode: '3100',
          //     cName: '上海市',
          //     dCode: '310104',
          //     dName: '徐汇区'
          //   },
          //   scenetype: { label: '汽修', value: '7' },
          //   sourceType: 1,
          // }
        ]
      }
    }
src/views/fysp/evaluation/components/precheck/CompPreCheck.vue
@@ -2,17 +2,18 @@
  <el-steps :active="stepIndex" finish-status="success" style="" align-center>
    <el-step title="评估范围" />
    <el-step title="数据源检查" />
    <el-step title="豁免条目" />
    <el-step title="条目豁免" />
    <el-step title="自动评估" />
  </el-steps>
  <CompCheckArea v-show="stepIndex == 0" v-model="stepIndex" @change="onAreaChange"></CompCheckArea>
  <CompCheckSource
    v-show="stepIndex == 1"
    v-model="stepIndex"
    ref="refSource"
  ></CompCheckSource>
  <CompCheckSource v-show="stepIndex == 1" v-model="stepIndex" ref="refSource"></CompCheckSource>
  <CompCheckExemption v-show="stepIndex == 2" v-model="stepIndex"></CompCheckExemption>
  <CompCheckConfirm v-show="stepIndex == 3" v-model="stepIndex"></CompCheckConfirm>
  <CompCheckConfirm
    v-show="stepIndex == 3"
    v-model="stepIndex"
    :area-info="area"
    @start="onNewTask"
  ></CompCheckConfirm>
</template>
<script>
@@ -29,7 +30,7 @@
  name: 'CompPreCheck',
  components: { CompCheckArea, CompCheckSource, CompCheckExemption, CompCheckConfirm },
  props: {},
  emits: ['preCheck'],
  emits: ['startTask'],
  data() {
    return {
      // æ“ä½œæ­¥éª¤ä¸‹æ ‡
@@ -41,16 +42,30 @@
    /**
     * ç›‘听评估范围变更
     */
    onAreaChange(v) {
      // this.area = v;
      this.$refs.refSource.startCheck(v);
    onAreaChange(val) {
      const v = val.value;
      this.area = {
        provincecode: v._locations.pCode,
        provincename: v._locations.pName,
        citycode: v._locations.cCode,
        cityname: v._locations.cName,
        districtcode: v._locations.dCode,
        districtname: v._locations.dName,
        towncode: v._locations.tCode,
        townname: v._locations.tName,
        starttime: this.$fm.formatYMDH(v.time),
        scensetypeid: v._scenetype.value,
        online: true,
        sourceType: v.sourceType
      };
      this.$refs.refSource.startCheck(this.area);
    },
    /**
     * è‡ªåŠ¨è¯„ä¼°å‰ç½®åˆè§„æ€§æ£€æŸ¥
     * æ£€æŸ¥æ‰€é€‰èŒƒå›´å†…各项评估数据源是否完整
     */
    preCheck() {
      // this.$emit('preCheck', param)
    onNewTask() {
      this.$emit('startTask');
    }
  }
};
src/views/fysp/evaluation/components/precheck/components/CompCheckConfirm.vue
@@ -1,36 +1,46 @@
<template>
  <el-card shadow="never">
    Never4
    <template #header>
      <div><el-text tag="b" size="large">自动评估确认</el-text></div>
      <el-text size="small" type="info">最终确认自动评估各配置项,并开始自动评估</el-text>
    </template>
    <div>评估范围</div>
    <div>评估数据源完整度</div>
    <div>是否有豁免条目</div>
    <template #footer>
      <el-row justify="space-around">
        <el-button type="primary" size="default" @click="lastStep">上一步</el-button>
        <!-- <el-button type="primary" size="default" @click="nextStep">下一步</el-button> -->
        <el-button type="primary" size="default" @click="startEvaluate">开始评估</el-button>
      </el-row>
    </template>
  </el-card>
</template>
<script>
import evaluateApi from '@/api/fysp/evaluateApi';
/**
 * è¯„估任务最终开启确认
 */
export default {
  props: {
    // æ­¥éª¤ä¸‹æ ‡
    modelValue: Number
    modelValue: Number,
    // åŒºåŸŸä¿¡æ¯
    areaInfo: Object
  },
  emits: ['update:modelValue'],
  emits: ['update:modelValue', 'start'],
  data() {
    return {
    };
    return {};
  },
  methods: {
    // è·³è½¬ä¸‹ä¸€æ­¥
    nextStep() {
      this.$emit('update:modelValue', this.modelValue + 1);
    // å¼€å¯è‡ªåŠ¨è¯„ä¼°ä»»åŠ¡
    startEvaluate() {
      evaluateApi.autoEvaluate(this.areaInfo).then((res) => {
        this.$emit('start', res.data);
      });
    },
    // è·³è½¬ä¸‹ä¸€æ­¥
    // è·³è½¬ä¸Šä¸€æ­¥
    lastStep() {
      this.$emit('update:modelValue', this.modelValue - 1);
    }
src/views/fysp/evaluation/components/precheck/components/CompCheckExemption.vue
@@ -1,5 +1,9 @@
<template>
  <el-card shadow="never">
    <template #header>
      <div><el-text tag="b" size="large">条目豁免</el-text></div>
      <el-text size="small" type="info">自定义设置此次评估不参与计算的条目</el-text>
    </template>
    è±å…æ¡ç›®åŠŸèƒ½æš‚æœªå®Œæˆï¼Œè¯·ç›´æŽ¥ç‚¹å‡»ä¸‹ä¸€æ­¥
    <template #footer>
      <el-row justify="space-around">
src/views/fysp/evaluation/components/precheck/components/CompCheckSource.vue
@@ -3,9 +3,11 @@
    <template #header>
      <div><el-text tag="b" size="large">数据源检查</el-text></div>
      <el-text size="small" type="info">检查评估所需数据源是否完整</el-text>
      <el-text size="small" type="info">检查评估所需数据源是否完整</el-text>
    </template>
    <FormCol>
      <el-form-item align="middle" v-for="(v, i) in checkResults" :key="i">
      <template v-for="(v, i) in checkResults" :key="i">
        <el-row class="h-small" align="middle">
        <el-col :span="14">
          <el-row align="middle">
            <el-text size="default" :class="v.required ? 'required' : 'not-required'">*</el-text>
@@ -36,17 +38,22 @@
        </el-col>
        <el-col :span="5">
          <el-button
            v-if="!v.pass"
            type="primary"
              v-show="!v.loading"
              :type="v.pass ? '' : 'danger'"
            size="small"
            @click="goto(v.path)"
            :disabled="v.path == ''"
          >
            åŽ»å®Œå–„
              {{ v.pass ? '去修改' : '去完善' }}
            <el-icon class="m-l-4"><Right /></el-icon>
          </el-button>
        </el-col>
      </el-form-item>
        </el-row>
        <el-row align="middle" class="m-b-16">
          <el-text size="small" class="not-required">*</el-text>
          <el-text size="small" class="m-l-4 color-i">{{ v.des }}</el-text>
        </el-row>
      </template>
    </FormCol>
    <template #footer>
      <el-row justify="space-around">
@@ -80,10 +87,13 @@
    loading: true,
    pass: false,
    path: _path,
    des: '',
    async fetch() {
      this.loading = true;
      setTimeout(async () => {
        this.pass = await _fetch();
        const res = await _fetch();
        this.pass = res ? res.pass : undefined;
        this.des = res ? res.des : undefined;
        this.loading = false;
      }, 1000);
    }
@@ -114,7 +124,19 @@
              ...this.areaInfo
            };
            return evaluateApi.fetchEvaluationRule(param).then((res) => {
              return res.data.length > 0;
              const pass = res.data.length > 0;
              let des = '';
              if (pass) {
                res.data.forEach((e) => {
                  if (des != '') {
                    des += '、';
                  }
                  des += `《${e.rulename}》`;
                });
              } else {
                des = '未找到相关评估规则表';
              }
              return { pass, des };
            });
          },
          true
@@ -122,15 +144,34 @@
        // åŒºåŸŸèŒƒå›´å†…的监管任务是否存在
        baseCheckItem('现场监管巡查总任务', '', () => {
          return taskApi.fetchTopTasks(this.areaInfo).then((res) => {
            return res.data.length > 0;
            const pass = res.data.length > 0;
            let des = '';
            if (pass) {
              res.data.forEach((e) => {
                if (des != '') {
                  des += '、';
                }
                des += e.name;
              });
            } else {
              des = '未找到相关巡查总任务';
            }
            return { pass, des };
          });
        }),
        // åŒºåŸŸèŒƒå›´å†…的监测数据是否存在、数据时间跨度是否完整、数据的初步分析是否完成
        baseCheckItem('现场监测数据', '', () => {}),
        // åŒºåŸŸèŒƒå›´å†…的每个监管点位与监测仪器的匹配记录是否存在,缺失情况等
        baseCheckItem('监管点位与监测点匹配', '', () => {
          userMapApi.fetchDeviceMap(this.areaInfo).then((res) => {
            return res.data.length > 0;
          return userMapApi.fetchDeviceMap(this.areaInfo).then((res) => {
            const pass = res.data.length > 0;
            let des = '';
            if (pass) {
              des = `找到匹配记录共${res.data.length}条`;
            } else {
              des = '未找到相关匹配记录';
            }
            return { pass, des };
          });
        }),
        // åŒºåŸŸèŒƒå›´å†…的监管问题配置表是否存在
@@ -139,18 +180,25 @@
            cityCode: this.areaInfo.citycode,
            districtCode: this.areaInfo.districtcode,
            sceneTypeId: this.areaInfo.scensetypeid
          };
          return problemApi.fetchProblemType(param).then((res) => {
            const pass = res.length > 0;
            let des = '';
            if (pass) {
              des = `找到问题类型共${res.length}条`;
            } else {
              des = '未找到相关问题类型';
          }
          problemApi.fetchProblemType(param).then((res) => {
            return res.length > 0;
            return { pass, des };
          });
        }),
        // åŒºåŸŸèŒƒå›´å†…的信访投诉记录是否存在,可随时补充
        baseCheckItem('信访投诉', '', () => {
          complaintApi.fetchComplaints();
          // complaintApi.fetchComplaints();
        }),
        // åŒºåŸŸèŒƒå›´å†…的行政处罚记录是否存在,可随时补充
        baseCheckItem('行政处罚', '', () => {
          complaintApi.fetchPunishment();
          // complaintApi.fetchPunishment();
        })
      ]
    };
@@ -189,21 +237,23 @@
    },
    // å¼€å§‹æ£€æŸ¥ä»»åŠ¡
    startCheck(value) {
      const v = value.value
      this.areaInfo = {
        provincecode: v._locations.pCode,
        provincename: v._locations.pName,
        citycode: v._locations.cCode,
        cityname: v._locations.cName,
        districtcode: v._locations.dCode,
        districtname: v._locations.dName,
        towncode: v._locations.tCode,
        townname: v._locations.tName,
        starttime: v.time,
        scensetypeid: v._scenetype.value,
        online: true,
        sourceType: v.sourceType,
      };
      // const v = value.value;
      // this.areaInfo = {
      //   provincecode: v._locations.pCode,
      //   provincename: v._locations.pName,
      //   citycode: v._locations.cCode,
      //   cityname: v._locations.cName,
      //   districtcode: v._locations.dCode,
      //   districtname: v._locations.dName,
      //   towncode: v._locations.tCode,
      //   townname: v._locations.tName,
      //   starttime: this.$fm.formatYMDH(v.time),
      //   scensetypeid: v._scenetype.value,
      //   online: true,
      //   sourceType: v.sourceType
      // };
      this.areaInfo = value
      this.checkResults.forEach((e) => {
        e.fetch();
      });