zhangjie 1 vuosi sitten
vanhempi
commit
48a6975e14

+ 7 - 0
src/api/system.js

@@ -226,3 +226,10 @@ export const noticeMessageExportApi = (data) =>
     params: data,
     download: true,
   });
+
+// task-manage
+export const taskQueryApi = (data) =>
+  request({
+    url: '/api/admin/task/query',
+    params: data,
+  });

+ 6 - 0
src/api/user.js

@@ -128,3 +128,9 @@ export const fileUploadApi = (data) =>
       'md5': data.get('md5'),
     },
   });
+
+export const getAttachmentFile = (taskId, type) =>
+  request({
+    url: '/api/admin/common/file/download',
+    params: { taskId, type },
+  });

+ 14 - 1
src/config/constants.js

@@ -52,7 +52,20 @@ export const PUBLISH_STATUS = {
   PUBLISH: '已发布',
   UN_PUBLISH: '未发布',
 };
-
+// 数据管理
+export const DATA_TASK_STATUS = {
+  INIT: '未开始',
+  RUNNING: '进行中',
+  FINISH: '已完成',
+};
+export const DATA_TASK_RESULT = {
+  SUCCESS: '成功',
+  ERROR: '失败',
+};
+export const DATA_TASK_TYPE = {
+  USER_ARCHIVES_IMPORT: '人员档案导入',
+  USER_ARCHIVES_EXPORT: '档案导出',
+};
 // 服务单元管理 ------->
 // 服务单元管理
 export const SERVICE_UNIT_STATUS = {

+ 23 - 0
src/router/modules/system.js

@@ -114,5 +114,28 @@ export default {
         },
       ],
     },
+    {
+      name: 'Task',
+      path: '/system/task',
+      redirect: { name: 'TaskManage' },
+      meta: {
+        title: '任务管理',
+        sort: 1,
+        icon: 'root-list',
+        alias: 'taskManage',
+      },
+      children: [
+        {
+          name: 'TaskManage',
+          path: '/system/task/task-manage',
+          component: () => import('@/views/system/task/task-manage/index.vue'),
+          meta: {
+            title: '任务管理',
+            sort: 1,
+            alias: 'task',
+          },
+        },
+      ],
+    },
   ],
 };

+ 12 - 0
src/utils/filter.js

@@ -16,6 +16,9 @@ import {
   INOUT_TYPE,
   GENDER_TYPE,
   EDUCATION_TYPE,
+  DATA_TASK_STATUS,
+  DATA_TASK_RESULT,
+  DATA_TASK_TYPE,
 } from '@/config/constants';
 import { dateFormat } from './tool';
 
@@ -56,6 +59,15 @@ export function noticeTypeFilter(val) {
 export function publishStatusFilter(val) {
   return PUBLISH_STATUS[val] || DEFAULT_FIELD;
 }
+export function dataTaskStatusFilter(val) {
+  return DATA_TASK_STATUS[val] || DEFAULT_FIELD;
+}
+export function dataTaskResultFilter(val) {
+  return DATA_TASK_RESULT[val] || DEFAULT_FIELD;
+}
+export function dataTaskTypeFilter(val) {
+  return DATA_TASK_TYPE[val] || DEFAULT_FIELD;
+}
 // 服务单元管理
 export function serviceUnitStatusFilter(val) {
   return SERVICE_UNIT_STATUS[val] || DEFAULT_FIELD;

+ 7 - 1
src/utils/request.js

@@ -7,12 +7,13 @@ import { h } from 'vue';
 import { LoadingPlugin } from 'tdesign-vue-next';
 import { getAuthorization, DEVICE_ID } from './crypto';
 import router from '@/router';
+import { initSyncTime, fetchTime } from './syncServerTime';
 
 function setAuth(config) {
   let userSession = sessionStorage.getItem('user');
   if (userSession && !config.noAuth) {
     let user = JSON.parse(userSession).user;
-    const timestamp = new Date().getTime();
+    const timestamp = fetchTime();
     const authorization = getAuthorization(
       {
         method: config.method,
@@ -53,6 +54,8 @@ function createService() {
   // HTTP response 拦截器
   service.interceptors.response.use(
     (response) => {
+      initSyncTime(new Date(response.headers.date).getTime());
+
       if (response.config.loading) {
         LoadingPlugin(false);
       }
@@ -69,6 +72,9 @@ function createService() {
       return response.data?.data;
     },
     (error) => {
+      if (error.response) {
+        initSyncTime(new Date(error.response.headers.date).getTime());
+      }
       if (error.config?.loading) {
         LoadingPlugin(false);
       }

+ 28 - 0
src/utils/syncServerTime.js

@@ -0,0 +1,28 @@
+let initLocalTime = null;
+let initServerTime = null;
+
+function getStorgeTime() {
+  const st = localStorage.getItem('st');
+  const unvalidVals = ['Infinity', 'NaN', 'null', 'undefined'];
+  if (unvalidVals.includes(st + '')) {
+    return [Date.now(), Date.now()];
+  } else {
+    const [s, t] = st.split('_');
+    return [s * 1, t * 1];
+  }
+}
+
+const [serverTime, localTime] = getStorgeTime();
+initSyncTime(serverTime, localTime);
+
+function initSyncTime(serverTime, localTime = Date.now()) {
+  initLocalTime = localTime;
+  initServerTime = serverTime;
+  localStorage.setItem('st', `${initServerTime}_${initLocalTime}`);
+}
+
+function fetchTime() {
+  return Date.now() + initServerTime - initLocalTime;
+}
+
+export { initSyncTime, fetchTime };

+ 20 - 0
src/utils/tool.js

@@ -261,6 +261,26 @@ export const download = (res, downName = '') => {
   URL.revokeObjectURL(aLink.href);
 };
 
+/**
+ * 下载url
+ * @param {String} url 文件下载地址
+ * @param {String}} filename 文件名
+ */
+export function downloadByUrl(url, filename) {
+  const tempLink = document.createElement('a');
+  tempLink.style.display = 'none';
+  tempLink.href = url;
+  const fileName = filename || url.split('/').pop().split('?')[0];
+  tempLink.setAttribute('download', fileName);
+  if (tempLink.download === 'undefined') {
+    tempLink.setAttribute('target', '_blank');
+  }
+  document.body.appendChild(tempLink);
+  tempLink.click();
+  document.body.removeChild(tempLink);
+  window.URL.revokeObjectURL(url);
+}
+
 /**
  * 对象转url参数
  * @param {*} data

+ 163 - 0
src/views/system/task/task-manage/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="registration-query flex flex-col h-full">
+    <SearchForm :fields="fields" :params="params"></SearchForm>
+    <div class="flex-1 page-wrap">
+      <t-table
+        size="small"
+        row-key="id"
+        :columns="columns"
+        :data="tableData"
+        bordered
+        :pagination="{
+          defaultCurrent: 1,
+          defaultPageSize: 10,
+          onChange,
+          total: pagination.total,
+          current: pagination.pageNumber,
+        }"
+        v-loading="tableLoading"
+      >
+        <template #create-time="{ col, row }">
+          {{ timestampFilter(row[col.colKey]) }}
+        </template>
+        <template #operate="{ col, row }">
+          <div class="table-operations">
+            <t-link
+              v-if="row.hasReportFile"
+              theme="primary"
+              hover="color"
+              @click="handleDownload(row, 'REPORT_FILE')"
+            >
+              导出日志
+            </t-link>
+            <t-link
+              v-if="row.hasResultFile"
+              theme="primary"
+              hover="color"
+              @click="handleDownload(row, 'EXPORT_FILE')"
+            >
+              下载文件
+            </t-link>
+            <!-- <t-link
+              v-if="row.hasImportFile"
+              theme="primary"
+              hover="color"
+              @click="handleDownload(row, 'IMPORT_FILE')"
+            >
+              下载导入文件
+            </t-link> -->
+          </div>
+        </template>
+      </t-table>
+    </div>
+  </div>
+</template>
+
+<script setup name="TaskManage">
+import { ref, reactive } from 'vue';
+import { MessagePlugin } from 'tdesign-vue-next';
+import useFetchTable from '@/hooks/useFetchTable';
+
+import { taskQueryApi } from '@/api/system';
+import { getAttachmentFile } from '@/api/user';
+import { dictToOptionList, downloadByUrl } from '@/utils/tool';
+import {
+  DATA_TASK_STATUS,
+  DATA_TASK_RESULT,
+  DATA_TASK_TYPE,
+} from '@/config/constants';
+import { timestampFilter } from '@/utils/filter';
+
+const fields = ref([
+  {
+    prop: 'type',
+    label: '任务类型',
+    type: 'select',
+    labelWidth: 100,
+    colSpan: 5,
+    options: dictToOptionList(DATA_TASK_TYPE),
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'status',
+    label: '任务状态',
+    type: 'select',
+    labelWidth: 100,
+    colSpan: 5,
+    options: dictToOptionList(DATA_TASK_STATUS),
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    prop: 'result',
+    label: '任务结果',
+    type: 'select',
+    labelWidth: 100,
+    colSpan: 5,
+    options: dictToOptionList(DATA_TASK_RESULT),
+    attrs: {
+      clearable: true,
+    },
+  },
+  {
+    type: 'buttons',
+    colSpan: 2,
+    children: [
+      {
+        type: 'button',
+        text: '查询',
+        onClick: () => {
+          search();
+        },
+      },
+    ],
+  },
+]);
+const params = reactive({
+  status: '',
+  type: '',
+  result: '',
+});
+
+const columns = [
+  { colKey: 'type', title: '任务类型' },
+  { colKey: 'status', title: '任务状态', width: 100 },
+  { colKey: 'result', title: '任务结果', width: 80 },
+  { colKey: 'createName', title: '创建人', width: 120 },
+  { colKey: 'createTime', title: '创建时间', cell: 'create-time', width: 170 },
+  {
+    title: '操作',
+    colKey: 'operate',
+    fixed: 'right',
+    width: 160,
+    align: 'center',
+    cell: 'operate',
+  },
+];
+const {
+  loading: tableLoading,
+  pagination,
+  tableData,
+  search,
+  onChange,
+} = useFetchTable(taskQueryApi, {
+  params,
+});
+
+async function handleDownload(row, type) {
+  const res = await getAttachmentFile(row.id, type).catch(() => {});
+  if (!res) return;
+
+  const url = res.url;
+  if (url.endsWith('.txt')) {
+    window.open(url);
+    return;
+  }
+
+  downloadByUrl(url);
+  MessagePlugin.success('文件下载成功!');
+}
+</script>