소스 검색

单任务轨迹图

Michael Wang 4 년 전
부모
커밋
c8cdac8ee3

+ 8 - 0
src/api/libraryTrackPage.ts

@@ -0,0 +1,8 @@
+import { httpApp } from "@/plugins/axiosApp";
+
+/** 查看单个评卷任务的试卷轨迹 */
+export async function getSingleLibraryTask(libraryId: string) {
+  const form = new FormData();
+  libraryId && form.append("libraryId", libraryId);
+  return httpApp.post("/admin/exam/track/library", form);
+}

+ 15 - 0
src/components/404.vue

@@ -0,0 +1,15 @@
+<template>
+  <div class="tw-text-center tw-text-3xl">页面没找到(404)</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+  setup() {
+    return {};
+  },
+});
+</script>
+
+<style scoped></style>

+ 98 - 0
src/features/library/libraryTrack/LibraryTrack.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="my-container">
+    <mark-header />
+    <div class="tw-flex tw-gap-1">
+      <mark-body />
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted } from "vue";
+import { store } from "./store";
+import MarkHeader from "./MarkHeader.vue";
+import { useRoute } from "vue-router";
+import MarkBody from "./MarkBody.vue";
+import { Task } from "@/types";
+import { message } from "ant-design-vue";
+import { getSingleLibraryTask } from "@/api/libraryTrackPage";
+
+export default defineComponent({
+  name: "LibraryTrack",
+  components: {
+    MarkHeader,
+    MarkBody,
+  },
+  setup: () => {
+    const route = useRoute();
+    let libraryId = route.query.libraryId;
+
+    async function updateTask() {
+      // const mkey = "fetch_task_key";
+      message.info({ content: "获取任务中...", duration: 2 });
+      let res = await getSingleStuTask();
+      // message.success({ content: "获取成功", key: mkey });
+
+      if (res.data.task) {
+        store.setting.fileServer = res.data.fileServer;
+        store.setting.splitConfig = res.data.splitConfig;
+        store.setting.uiSetting = {
+          "answer.paper.scale": 1,
+          "score.board.collapse": false,
+        };
+
+        let task = res.data.task as Task;
+
+        task.sliceUrls = task.sliceUrls.map(
+          (s: string) => store.setting.fileServer + s
+        );
+
+        store.currentTask = task;
+        console.log(store.currentTask);
+        if (store.currentTask)
+          store.setting.subject = store.currentTask.subject;
+      } else {
+        store.message = res.data.message;
+      }
+    }
+
+    async function fetchTask() {
+      await updateTask();
+    }
+
+    onMounted(async () => {
+      await fetchTask();
+    });
+
+    async function getSingleStuTask() {
+      return getSingleLibraryTask(libraryId as string);
+    }
+
+    return {
+      store,
+      fetchTask,
+    };
+  },
+});
+</script>
+
+<style scoped>
+.my-container {
+  width: 100%;
+}
+a {
+  color: #42b983;
+}
+
+label {
+  margin: 0 0.5em;
+  font-weight: bold;
+}
+
+code {
+  background-color: #eee;
+  padding: 2px 4px;
+  border-radius: 4px;
+  color: #304455;
+}
+</style>

+ 26 - 0
src/features/library/libraryTrack/MarkBody.vue

@@ -0,0 +1,26 @@
+<template>
+  <CommonMarkBody
+    v-if="store"
+    :useMarkResult="false"
+    :store="store"
+    uniquePropName="libraryId"
+    @error="$emit('error')"
+  />
+</template>
+
+<script lang="ts">
+import CommonMarkBody from "@/features/mark/CommonMarkBody.vue";
+import { defineComponent, watch } from "vue";
+import { store } from "./store";
+
+export default defineComponent({
+  name: "MarkBody",
+  components: { CommonMarkBody },
+  emits: ["error"],
+  setup() {
+    return { store };
+  },
+});
+</script>
+
+<style scoped></style>

+ 132 - 0
src/features/library/libraryTrack/MarkHeader.vue

@@ -0,0 +1,132 @@
+<template>
+  <div
+    class="
+      tw-flex tw-gap-4 tw-justify-between tw-items-center
+      header-container
+      tw-px-1
+    "
+    v-if="store.setting"
+  >
+    <ul class="tw-flex tw-gap-2 tw-mb-0">
+      <li @click="upScale" title="放大">
+        <ZoomInOutlined
+          class="icon-font icon-font-size-20 tw-cursor-pointer"
+          :style="{
+            color: greaterThanOneScale ? 'red' : 'white',
+          }"
+        />
+      </li>
+      <li @click="downScale" title="缩小">
+        <ZoomOutOutlined
+          class="icon-font icon-font-size-20 tw-cursor-pointer"
+          :style="{
+            color: lessThanOneScale ? 'red' : 'white',
+          }"
+        />
+      </li>
+      <li @click="normalScale" title="适应">
+        <FullscreenOutlined
+          class="icon-font icon-font-size-20 tw-cursor-pointer"
+        />
+      </li>
+    </ul>
+    <div
+      class="tw-flex tw-place-items-center tw-cursor-pointer"
+      @click="closeWindow"
+    >
+      <PoweroffOutlined class="icon-font icon-with-text" />关闭
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent } from "vue";
+import { store } from "./store";
+import {
+  ZoomInOutlined,
+  ZoomOutOutlined,
+  FullscreenOutlined,
+  HistoryOutlined,
+  UserOutlined,
+  PoweroffOutlined,
+  AlertOutlined,
+  QuestionCircleOutlined,
+} from "@ant-design/icons-vue";
+import { useRoute } from "vue-router";
+
+export default defineComponent({
+  name: "MarkHeader",
+  components: {
+    ZoomInOutlined,
+    ZoomOutOutlined,
+    FullscreenOutlined,
+    HistoryOutlined,
+    UserOutlined,
+    PoweroffOutlined,
+    AlertOutlined,
+    QuestionCircleOutlined,
+  },
+  setup() {
+    const route = useRoute();
+
+    const upScale = () => {
+      const s = store.setting.uiSetting["answer.paper.scale"];
+      if (s < 3)
+        store.setting.uiSetting["answer.paper.scale"] = +(s + 0.2).toFixed(1);
+    };
+    const downScale = () => {
+      const s = store.setting.uiSetting["answer.paper.scale"];
+      if (s > 0.2)
+        store.setting.uiSetting["answer.paper.scale"] = +(s - 0.2).toFixed(1);
+    };
+    const normalScale = () => {
+      store.setting.uiSetting["answer.paper.scale"] = 1;
+    };
+    const greaterThanOneScale = computed(() => {
+      return store.setting.uiSetting["answer.paper.scale"] > 1;
+    });
+    const lessThanOneScale = computed(() => {
+      return store.setting.uiSetting["answer.paper.scale"] < 1;
+    });
+
+    const closeWindow = async () => {
+      window.close();
+    };
+
+    return {
+      store,
+      upScale,
+      downScale,
+      normalScale,
+      greaterThanOneScale,
+      lessThanOneScale,
+      closeWindow,
+    };
+  },
+});
+</script>
+
+<style scoped>
+.header-container {
+  /* z-index: 10000; */
+  position: relative;
+  font-size: 16px;
+  height: 40px;
+
+  background-color: #5d6d7d;
+  color: white;
+}
+.highlight-text {
+  color: #ffe400;
+}
+.icon-font {
+  display: block;
+}
+.icon-font-size-20 {
+  font-size: 20px;
+}
+.icon-with-text {
+  font-size: 18px;
+  line-height: 18px;
+}
+</style>

+ 26 - 0
src/features/library/libraryTrack/store.ts

@@ -0,0 +1,26 @@
+import { InspectStore, Task } from "@/types";
+import { reactive } from "vue";
+
+const obj = {
+  setting: {
+    fileServer: "",
+    userName: "",
+    subject: { name: "", code: "" },
+    uiSetting: {
+      "answer.paper.scale": 1,
+      "score.board.collapse": false,
+    },
+    splitConfig: [],
+  },
+  status: {
+    totalCount: 0,
+  },
+  currentTask: undefined,
+  historyOpen: false,
+  MarkBoardTrackCollapse: false,
+  historyTasks: [],
+  focusTracks: [],
+  message: null,
+} as InspectStore;
+
+export const store = reactive(obj);

+ 9 - 0
src/router/index.ts

@@ -20,6 +20,15 @@ const routes = [
     path: "/admin/exam/track/student",
     path: "/admin/exam/track/student",
     component: () => import("@/features/student/studentTrack/StudentTrack.vue"),
     component: () => import("@/features/student/studentTrack/StudentTrack.vue"),
   },
   },
+  {
+    path: "/admin/exam/track/library",
+    component: () => import("@/features/library/libraryTrack/LibraryTrack.vue"),
+  },
+  {
+    path: "/:pathMatch(.*)*",
+    name: "NotFound",
+    component: () => import("@/components/404.vue"),
+  },
 ];
 ];
 
 
 // 3. Create the router instance and pass the `routes` option
 // 3. Create the router instance and pass the `routes` option