Explorar el Código

feat: 图片导出流程

zhangjie hace 1 año
padre
commit
f7a607d0cd

+ 41 - 2
electron/db/modelApi/trackTask.ts

@@ -9,11 +9,12 @@ import {
   TrackTaskStatusKey,
   TrackTaskDetailStatusKey,
   TRACK_TASK_STATUS,
+  TRACK_TASK_DETAIL_STATUS,
 } from '../enumerate';
 
-export async function getUnfinishTrackTask() {
+export async function getUnfinishTrackTask(schoolId: string) {
   const task = await TrackTask.findOne({
-    where: { status: { [Op.ne]: TRACK_TASK_STATUS.FINISH } },
+    where: { status: { [Op.ne]: TRACK_TASK_STATUS.FINISH }, schoolId },
   }).catch((err) => {
     console.dir(err);
   });
@@ -98,3 +99,41 @@ export async function getTrackTaskDetailCount(data: {
   });
   return count;
 }
+
+export async function getUnfinishTrackTaskDetail(trackTaskId: number) {
+  const task = await TrackTaskDetail.findOne({
+    where: { status: TRACK_TASK_DETAIL_STATUS.INIT, trackTaskId },
+  }).catch((err) => {
+    console.dir(err);
+  });
+  if (!task) return null;
+
+  await TrackTaskDetail.update(
+    {
+      status: TRACK_TASK_DETAIL_STATUS.RUNNING,
+    },
+    {
+      where: {
+        id: task.id,
+      },
+    }
+  );
+
+  return task.dataValues;
+}
+
+export async function updateTrackTaskDetailStatus(data: {
+  id: number;
+  status: TrackTaskDetailStatusKey;
+}) {
+  await TrackTaskDetail.update(
+    {
+      status: TRACK_TASK_DETAIL_STATUS[data.status],
+    },
+    {
+      where: {
+        id: data.id,
+      },
+    }
+  );
+}

+ 7 - 2
electron/preload/api.ts

@@ -98,8 +98,7 @@ async function downloadFile(url: string, outputPath: string) {
   });
 }
 
-async function downloadImage(url: string, outputFilename: string) {
-  const outputPath = path.join(getTempPath(), outputFilename);
+async function downloadImage(url: string, outputPath: string) {
   await downloadFile(url, outputPath);
   const size = sizeOf(outputPath);
 
@@ -114,11 +113,17 @@ function joinPath(paths: string[]) {
   return path.join(...paths);
 }
 
+async function combinePdf(urls: string[], outpath: string) {
+  // TODO:
+  console.log(urls, outpath);
+}
+
 const commonApi = {
   cropImage,
   drawTrack,
   joinPath,
   downloadImage,
+  combinePdf,
 };
 
 export type CommonApi = typeof commonApi;

+ 4 - 0
electron/preload/apiDb.ts

@@ -6,6 +6,8 @@ import {
   createTrackTaskDetails,
   updateTrackTaskStatus,
   getTrackTaskDetailCount,
+  getUnfinishTrackTaskDetail,
+  updateTrackTaskDetailStatus,
 } from '../db/modelApi/trackTask';
 
 createDb();
@@ -18,6 +20,8 @@ const dbApi = {
   createTrackTaskDetails,
   updateTrackTaskStatus,
   getTrackTaskDetailCount,
+  getUnfinishTrackTaskDetail,
+  updateTrackTaskDetailStatus,
 };
 
 export type DbApi = typeof dbApi;

+ 1 - 0
src/router/constants.ts

@@ -11,5 +11,6 @@ export const WHITE_LIST = [
   { name: 'Login' },
   { name: 'Setting' },
   { name: 'TestPage' },
+  { name: 'TrackTaskExport' },
   { name: HOME_PAGE_ROUTE },
 ];

+ 28 - 17
src/router/routes/modules/base.ts

@@ -1,24 +1,35 @@
 import { DEFAULT_LAYOUT } from '../base';
 import { AppRouteRecordRaw } from '../types';
 
-const routes: AppRouteRecordRaw = {
-  path: '/base',
-  name: 'base',
-  component: DEFAULT_LAYOUT,
-  meta: {
-    requiresAuth: true,
-  },
-  children: [
-    {
-      path: 'track-export',
-      name: 'TrackExport',
-      component: () => import('@/views/base/track-export/index.vue'),
-      meta: {
-        title: '轨迹图导出',
-        requiresAuth: true,
+const routes: AppRouteRecordRaw[] = [
+  {
+    path: '/base',
+    name: 'base',
+    component: DEFAULT_LAYOUT,
+    meta: {
+      requiresAuth: true,
+    },
+    children: [
+      {
+        path: 'track-export',
+        name: 'TrackExport',
+        component: () => import('@/views/base/track-export/index.vue'),
+        meta: {
+          title: '轨迹图导出',
+          requiresAuth: true,
+        },
       },
+    ],
+  },
+  {
+    path: '/track-task-export',
+    name: 'TrackTaskExport',
+    component: () => import('@/views/base/track-export/trackTaskExport.vue'),
+    meta: {
+      title: '导出详情',
+      requiresAuth: false,
     },
-  ],
-};
+  },
+];
 
 export default routes;

+ 3 - 1
src/views/base/track-export/taskProgress.vue

@@ -79,7 +79,9 @@
 
   /* init modal */
   async function modalBeforeOpen() {
-    const res = await window.db.getUnfinishTrackTask();
+    const res = await window.db.getUnfinishTrackTask(
+      userStore.curSchoolInfo.id
+    );
     if (!res) {
       Message.warning('没有未完成任务');
       close();

+ 77 - 0
src/views/base/track-export/trackTaskExport.vue

@@ -0,0 +1,77 @@
+<template>
+  <div>导出中……</div>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, ref } from 'vue';
+  import { useRoute } from 'vue-router';
+
+  import { UserState } from '@/store/modules/user/types';
+  import { useUserStore } from '@/store';
+  import useTimeout from '@/hooks/timout';
+  import useDraw from './useDraw';
+
+  defineOptions({
+    name: 'TrackTaskExport',
+  });
+
+  const route = useRoute();
+  const TASK_KEY = 'task';
+
+  const winId = Number(route.query.winId);
+  const userInfo = JSON.stringify(window.atob(route.query.user)) as UserState;
+  const userStore = useUserStore();
+  userStore.setInfo(userInfo);
+
+  const { runTask, getTrackTask, getTrackTaskDetail } = useDraw();
+  const { addSetTimeout, clearSetTimeout } = useTimeout();
+
+  const stop = ref(false);
+
+  async function getTask() {
+    clearSetTimeout(TASK_KEY);
+    if (stop.value) {
+      stopTask();
+      return;
+    }
+
+    const res = await getTrackTaskDetail();
+    if (!res) {
+      addSetTimeout(TASK_KEY, getTask, 1 * 1000);
+      return;
+    }
+
+    let result = true;
+    await runTask(res.studentId).catch(() => {
+      result = false;
+    });
+
+    const status = result ? 'FINISH' : 'INIT';
+    await window.db.updateTrackTaskDetailStatus({
+      id: res.trackTaskId,
+      status,
+    });
+
+    addSetTimeout(TASK_KEY, getTask, 0);
+  }
+
+  function stopTask() {
+    window.electron.closeProcessWindow(winId);
+  }
+
+  function registEvent() {
+    window.electron.onStopRunning(() => {
+      stop.value = true;
+    });
+  }
+
+  async function startTask() {
+    registEvent();
+    await getTrackTask(userStore.curSchoolInfo.id);
+    getTask();
+  }
+
+  onMounted(() => {
+    startTask();
+  });
+</script>

+ 71 - 14
src/views/base/track-export/useDraw.ts

@@ -1,11 +1,16 @@
+import { ref } from 'vue';
+
 import {
   getSingleStudentTaskOfStudentTrack,
   studentObjectiveConfirmData,
   getSingleStudentCardData,
 } from '@/api/task';
 import { Task, Track, SpecialTag } from '@/api/types/task';
-import { calcSum, maxNum, randomCode, strGbLen } from '@/utils/utils';
+import { TrackConfigType } from '@/store/modules/app/types';
+import { PictureTypeEnum } from '@/constants/enumerate';
+import { calcSum, maxNum, strGbLen } from '@/utils/utils';
 import { DrawTrackItem } from '../../../../electron/preload/types';
+import { TrackTaskData } from '../../../../electron/db/models/trackTask';
 
 type AnswerMap = Record<string, { answer: string; isRight: boolean }>;
 
@@ -74,13 +79,45 @@ export default function useDraw() {
   let recogDatas: string[] = [];
   let rawTask = {} as Task;
   let trackData = [] as TrackTtemType[];
+  let isOnlyOrigin = false;
+  let hasPdf = false;
+  const task = ref({} as TrackTaskData);
+  const trackConfig = ref({} as TrackConfigType);
+
+  async function getTrackTask(schoolId: string) {
+    const res = await window.db.getUnfinishTrackTask(schoolId);
+    if (!res) return;
+    task.value = res;
+    trackConfig.value = {
+      pictureType: res.pictureType.split(','),
+      outputDir: res.outputDir,
+      curOutputDir: res.outputDir,
+      outputDirIsDefault: false,
+    };
+    isOnlyOrigin = checkOnlyOrigin();
+    hasPdf = trackConfig.value.pictureType.includes('pdf');
+  }
+
+  function getTrackTaskDetail() {
+    return window.db.getUnfinishTrackTaskDetail(task.value.id);
+  }
 
   async function runTask(studentId: string) {
     initData();
+
     try {
       await getTaskData(studentId);
+      await downloadImages(rawTask.sheetUrls);
+      if (isOnlyOrigin) {
+        return true;
+      }
+
       await parseDrawList();
-      await drawTask();
+
+      const trackFiles = await drawTask();
+      if (hasPdf) {
+        await window.api.combinePdf(trackFiles, getOutputPath('pdf'));
+      }
     } catch (error) {
       console.log(error);
       return Promise.reject(error);
@@ -96,6 +133,13 @@ export default function useDraw() {
     answerMap = {} as AnswerMap;
   }
 
+  function checkOnlyOrigin() {
+    return (
+      trackConfig.value.pictureType.length === 1 &&
+      trackConfig.value.pictureType[0] === 'track'
+    );
+  }
+
   async function getTaskData(studentId: string) {
     rawTask = await getSingleStudentTaskOfStudentTrack(studentId);
     if (!rawTask) return;
@@ -103,6 +147,8 @@ export default function useDraw() {
     // rawTask.sheetUrls = ["/1-1.jpg", "/1-2.jpg"];
     if (!rawTask.sheetUrls) rawTask.sheetUrls = [];
 
+    if (isOnlyOrigin) return;
+
     // 获取客观题选项信息
     const objectiveData = await studentObjectiveConfirmData(studentId);
     objectiveData.answers.forEach((item) => {
@@ -121,14 +167,29 @@ export default function useDraw() {
     cardData = cardContent.pages;
   }
 
+  function getOutputPath(type: PictureTypeEnum, index: number | undefined) {
+    let filename = rawTask.studentCode;
+    if (index !== undefined) {
+      filename += `-${index}`;
+    }
+    filename += type === 'pdf' ? '.pdf' : '.jpg';
+    const paths = [
+      trackConfig.value.curOutputDir,
+      task.value.semesterName,
+      task.value.examName,
+      `${rawTask.courseName}(${rawTask.courseCode})`,
+      rawTask.paperNumber,
+      type === 'pdf' ? '' : type,
+      filename,
+    ];
+    return window.api.joinPath(paths);
+  }
+
   async function downloadImages(urls: string[]) {
     const downloads: Promise<ImageItem>[] = [];
     for (let i = 0; i < urls.length; i++) {
       downloads.push(
-        window.api.downloadImage(
-          urls[i],
-          `${rawTask.studentId}-${i}-${randomCode(8)}.jpg`
-        )
+        window.api.downloadImage(urls[i], getOutputPath('origin', i + 1))
       );
     }
 
@@ -186,24 +247,18 @@ export default function useDraw() {
     const tasks: Promise<string>[] = [];
     for (let i = 0; i < trackData.length; i++) {
       const item = trackData[i];
-      const outpath = getOutputPath(i + 1);
+      const outpath = getOutputPath('track', i + 1);
       tasks.push(window.api.drawTrack(item.url, item.drawTrackList, outpath));
     }
     const res = await Promise.all(tasks).catch((error) => {
       console.log(error);
     });
     if (!res) {
-      return Promise.reject(res);
+      return Promise.reject(new Error('绘制轨迹错误'));
     }
     return res;
   }
 
-  function getOutputPath(ind: number) {
-    // TODO:
-    const no = `000${ind}`.slice(-3);
-    return window.api.joinPath(['', `${no}.jpg`]);
-  }
-
   // track ----- start->
   function getDrawTrackItem(track: Track): DrawTrackItem {
     return {
@@ -501,5 +556,7 @@ export default function useDraw() {
 
   return {
     runTask,
+    getTrackTask,
+    getTrackTaskDetail,
   };
 }