|
@@ -0,0 +1,243 @@
|
|
|
+<template>
|
|
|
+ <div class="review-tabs">
|
|
|
+ <div
|
|
|
+ :class="['review-tab', { 'is-active': tabKey === 'review' }]"
|
|
|
+ @click="switchTab('review')"
|
|
|
+ >
|
|
|
+ 复核校验
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ :class="['review-tab', { 'is-active': tabKey === 'history' }]"
|
|
|
+ @click="switchTab('history')"
|
|
|
+ >
|
|
|
+ 历史记录
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-show="tabKey === 'review'" class="review-tab-body">
|
|
|
+ <a-collapse v-model:activeKey="reviewKey">
|
|
|
+ <a-collapse-panel key="1">
|
|
|
+ <template #header><FilterFilled />搜索条件 </template>
|
|
|
+ <a-space>
|
|
|
+ <span>科目:</span>
|
|
|
+ <a-select
|
|
|
+ v-model:value="searchCourseCode"
|
|
|
+ placeholder="请选择科目"
|
|
|
+ :options="courses"
|
|
|
+ filter-option
|
|
|
+ style="width: 200px"
|
|
|
+ ></a-select>
|
|
|
+ <a-button type="primary" @click="onSearch">搜索</a-button>
|
|
|
+ </a-space>
|
|
|
+ </a-collapse-panel>
|
|
|
+ <a-collapse-panel key="2">
|
|
|
+ <template #header><WarningFilled />导出异常 </template>
|
|
|
+
|
|
|
+ <a-space>
|
|
|
+ <span>科目:</span>
|
|
|
+ <a-select
|
|
|
+ v-model:value="exportCourseCode"
|
|
|
+ placeholder="请选择科目"
|
|
|
+ :options="courses"
|
|
|
+ filter-option
|
|
|
+ style="width: 200px"
|
|
|
+ ></a-select>
|
|
|
+ <a-button :loading="downloading" @click="onExport">导出</a-button>
|
|
|
+ </a-space>
|
|
|
+ </a-collapse-panel>
|
|
|
+ <a-collapse-panel key="3">
|
|
|
+ <template #header><PushpinFilled />复核标记 </template>
|
|
|
+
|
|
|
+ <a-radio-group v-model:value="result" @change="onMark">
|
|
|
+ <a-radio :value="1">正常</a-radio>
|
|
|
+ <a-radio :value="0">异常</a-radio>
|
|
|
+ </a-radio-group>
|
|
|
+ </a-collapse-panel>
|
|
|
+ <a-collapse-panel key="4">
|
|
|
+ <template #header><RightSquareFilled />重置 </template>
|
|
|
+
|
|
|
+ <a-space>
|
|
|
+ <span>科目:</span>
|
|
|
+ <a-select
|
|
|
+ v-model:value="resetCourseCode"
|
|
|
+ placeholder="请选择科目"
|
|
|
+ :options="courses"
|
|
|
+ filter-option
|
|
|
+ style="width: 200px"
|
|
|
+ ></a-select>
|
|
|
+ <qm-button type="danger" @click="onReset">重置</qm-button>
|
|
|
+ </a-space>
|
|
|
+ </a-collapse-panel>
|
|
|
+ </a-collapse>
|
|
|
+ </div>
|
|
|
+ <div v-show="tabKey === 'history'" class="review-tab-body">
|
|
|
+ <a-collapse collapsible="disabled">
|
|
|
+ <a-collapse-panel>
|
|
|
+ <template #header><PushpinFilled />复核标记 </template>
|
|
|
+
|
|
|
+ <a-radio-group v-model:value="historyResult" @change="onHistoryMark">
|
|
|
+ <a-radio :value="1">正常</a-radio>
|
|
|
+ <a-radio :value="0">异常</a-radio>
|
|
|
+ </a-radio-group>
|
|
|
+ </a-collapse-panel>
|
|
|
+ </a-collapse>
|
|
|
+ <div class="history-list">
|
|
|
+ <table>
|
|
|
+ <colgroup>
|
|
|
+ <col />
|
|
|
+ <col width="60" />
|
|
|
+ </colgroup>
|
|
|
+ <tr>
|
|
|
+ <th>准考证号</th>
|
|
|
+ <th>状态</th>
|
|
|
+ </tr>
|
|
|
+ <tr
|
|
|
+ v-for="(item, index) in dataList"
|
|
|
+ :key="item.id"
|
|
|
+ @click="setCurTask(index)"
|
|
|
+ >
|
|
|
+ <td>{{ item.examNumber }}</td>
|
|
|
+ <td :class="item.markStatus ? 'color-success' : 'color-error'">
|
|
|
+ {{ item.markStatus ? "正常" : "异常" }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ <div class="history-footer">
|
|
|
+ <SimplePagination
|
|
|
+ :total="pagination.total"
|
|
|
+ :page-size="pagination.pageSize"
|
|
|
+ @change="toPage"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from "vue";
|
|
|
+import { subjectList } from "@/ap/base";
|
|
|
+import {
|
|
|
+ FilterFilled,
|
|
|
+ WarningFilled,
|
|
|
+ RightSquareFilled,
|
|
|
+ PushpinFilled,
|
|
|
+} from "@ant-design/icons-vue";
|
|
|
+import { showConfirm } from "@/utils/uiUtils";
|
|
|
+
|
|
|
+import {
|
|
|
+ reviewWarningTaskExport,
|
|
|
+ reviewTaskHistory,
|
|
|
+ reviewTaskSave,
|
|
|
+} from "@/ap/review";
|
|
|
+import { ReviewTaskListItem } from "@/ap/types/review";
|
|
|
+
|
|
|
+import { downloadByApi } from "@/utils/download";
|
|
|
+import useTable from "@/hooks/useTable";
|
|
|
+import useLoading from "@/hooks/useLoading";
|
|
|
+import { useUserStore, useReviewStore } from "@/store";
|
|
|
+
|
|
|
+import SimplePagination from "@/components/SimplePagination/index.vue";
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "ReviewAction",
|
|
|
+});
|
|
|
+const emit = defineEmits(["search", "reset", "mark"]);
|
|
|
+
|
|
|
+const userStore = useUserStore();
|
|
|
+const reviewStore = useReviewStore();
|
|
|
+
|
|
|
+// tab
|
|
|
+const reviewKey = ref("1");
|
|
|
+
|
|
|
+async function switchTab(key: "review" | "history") {
|
|
|
+ reviewStore.setInfo({ tabKey: key });
|
|
|
+
|
|
|
+ if (key === "history") {
|
|
|
+ await toPage(1);
|
|
|
+ setCurTask(0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// course data
|
|
|
+interface optionListItem {
|
|
|
+ value: string;
|
|
|
+ label: string;
|
|
|
+}
|
|
|
+const courses = ref<optionListItem[]>([]);
|
|
|
+async function getCourses() {
|
|
|
+ const res = await subjectList({ examId: userStore.curExam.id });
|
|
|
+ courses.value = (res || []).map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.subjectCode,
|
|
|
+ label: item.subjectName,
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+const searchCourseCode = ref("");
|
|
|
+const exportCourseCode = ref("");
|
|
|
+const resetCourseCode = ref("");
|
|
|
+const result = ref(1);
|
|
|
+const historyResult = ref(1);
|
|
|
+
|
|
|
+// history
|
|
|
+const curHistoryTaskIndex = res(0);
|
|
|
+const { dataList, pagination, loading, getList, toPage, setPageSize } =
|
|
|
+ useTable<ReviewTaskListItem>(
|
|
|
+ reviewTaskHistory,
|
|
|
+ { examId: userStore.curExam.id },
|
|
|
+ false
|
|
|
+ );
|
|
|
+setPageSize(30);
|
|
|
+
|
|
|
+function setCurTask(index: number) {
|
|
|
+ curHistoryTaskIndex.value = index;
|
|
|
+ reviewStore.setInfo({ curTask: dataList.value[index] });
|
|
|
+}
|
|
|
+
|
|
|
+// actions
|
|
|
+function onSearch() {
|
|
|
+ emit("search", searchCourseCode.value);
|
|
|
+}
|
|
|
+
|
|
|
+function onReset() {
|
|
|
+ emit("reset", resetCourseCode.value);
|
|
|
+}
|
|
|
+
|
|
|
+function onMark() {
|
|
|
+ emit("mark", result.value);
|
|
|
+}
|
|
|
+
|
|
|
+async function onHistoryMark() {
|
|
|
+ if (!reviewStore.curTask) return;
|
|
|
+
|
|
|
+ const res = await reviewTaskSave({
|
|
|
+ id: reviewStore.curTask.id,
|
|
|
+ result: historyResult.value,
|
|
|
+ });
|
|
|
+ reviewStore.setInfo({
|
|
|
+ curTask: Object.assign({}, reviewStore.curTask, {
|
|
|
+ markStatus: historyResult.value,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+const { loading: downloading, setLoading } = useLoading();
|
|
|
+async function onExport() {
|
|
|
+ if (downloading.value) return;
|
|
|
+
|
|
|
+ setLoading(true);
|
|
|
+ const res = await downloadByApi(() =>
|
|
|
+ reviewWarningTaskExport({
|
|
|
+ examId: userStore.curExam.id,
|
|
|
+ subjectCode: exportCourseCode.value,
|
|
|
+ })
|
|
|
+ ).catch((e: Error) => {
|
|
|
+ message.error(e.message || "下载失败,请重新尝试!");
|
|
|
+ });
|
|
|
+ setLoading(false);
|
|
|
+
|
|
|
+ if (!res) return;
|
|
|
+ message.success("导出成功!");
|
|
|
+}
|
|
|
+</script>
|