Browse Source

feat: 打回管理

zhangjie 1 week ago
parent
commit
271da4d639

+ 27 - 0
src/api/reject.ts

@@ -0,0 +1,27 @@
+import axios from 'axios';
+import {
+  RejectListPageParams,
+  RejectListPageRes,
+  RejectStatisticsListParams,
+  RejectStatisticsListRes,
+} from './types/reject';
+
+// 获取打回列表
+export function getRejectList(
+  params: RejectListPageParams
+): Promise<RejectListPageRes> {
+  return axios.post('/api/student/list', { params });
+}
+// 获取打回记录列表
+export function getRejectRecordList(
+  params: RejectListPageParams
+): Promise<RejectListPageRes> {
+  return axios.post('/api/student/list', { params });
+}
+
+// 获取打回统计列表
+export function getRejectStatisticsList(
+  params: RejectStatisticsListParams
+): Promise<RejectStatisticsListRes> {
+  return axios.post('/api/student/list', { params });
+}

+ 63 - 0
src/api/types/reject.ts

@@ -0,0 +1,63 @@
+import { PageResult, PageParams } from './common';
+
+export interface RejectItem {
+  id: number;
+  /** 科目 */
+  subject: string;
+  /** 分组序号 */
+  groupNo: number;
+  /** 准考证号 */
+  examNo: string;
+  /** 密号 */
+  secretNo: string;
+  /** 打回原因 */
+  rejectReason: string;
+  /** 评卷员 */
+  marker: string;
+  /** 给分明细 */
+  scoreDetails: string;
+  /** 打回人 */
+  rejectBy: string;
+  /** 打回时间 */
+  rejectTime: number;
+}
+export type RejectListPageRes = PageResult<RejectItem>;
+
+export interface RejectListFilter {
+  /** 科目 */
+  subject: number | null;
+  /** 分组序号 */
+  groupNo: number;
+  /** 打回原因 */
+  rejectReason: string;
+  /** 评卷员 */
+  marker: string;
+  /** 打回人 */
+  rejectBy: string;
+}
+export type RejectListPageParams = PageParams<RejectListFilter>;
+
+// 打回统计
+export interface RejectStatisticsItem {
+  /** 科目 */
+  subject: string;
+  /** 分组序号 */
+  groupNo: number;
+  /** 任务总数 */
+  totalTasks: number;
+  /** 已完成数量 */
+  completedTasks: number;
+  /** 打回数量 */
+  rejectCount: number;
+  /** 打回率 */
+  rejectRate: number;
+}
+export type RejectStatisticsListRes = PageResult<RejectStatisticsItem>;
+
+export interface RejectStatisticsFilter {
+  /** 科目 */
+  subject: number | null;
+  /** 显示分组 */
+  showGroupNo: boolean;
+}
+export type RejectStatisticsListParams = PageParams<RejectStatisticsFilter>;

+ 27 - 0
src/router/routes/modules/base.ts

@@ -36,6 +36,33 @@ const BASE: AppRouteRecordRaw = {
         requiresAuth: true,
         requiresAuth: true,
       },
       },
     },
     },
+    {
+      path: '/reject-manage',
+      name: 'RejectManage',
+      component: () => import('@/views/reject/RejectManage.vue'),
+      meta: {
+        title: '打回卷',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: '/rejcect-record',
+      name: 'RejectRecord',
+      component: () => import('@/views/reject/RejectRecord.vue'),
+      meta: {
+        title: '打回记录',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: 'reject-statistics',
+      name: 'RejectStatistics',
+      component: () => import('@/views/reject/RejectStatistics.vue'),
+      meta: {
+        title: '打回统计',
+        requiresAuth: true,
+      },
+    },
     {
     {
       path: '/issue-paper',
       path: '/issue-paper',
       name: 'IssuePaper',
       name: 'IssuePaper',

+ 11 - 2
src/store/modules/app/menuData.ts

@@ -123,7 +123,16 @@ export const adminMenus = [
   {
   {
     id: 11,
     id: 11,
     name: '打回卷',
     name: '打回卷',
-    url: 'BackManage',
+    url: 'RejectManage',
+    type: 'MENU',
+    parentId: 10,
+    sequence: 1,
+    enable: true,
+  },
+  {
+    id: 111,
+    name: '打回记录',
+    url: 'RejectRecord',
     type: 'MENU',
     type: 'MENU',
     parentId: 10,
     parentId: 10,
     sequence: 1,
     sequence: 1,
@@ -132,7 +141,7 @@ export const adminMenus = [
   {
   {
     id: 12,
     id: 12,
     name: '打回统计',
     name: '打回统计',
-    url: 'BackStatistics',
+    url: 'RejectStatistics',
     type: 'MENU',
     type: 'MENU',
     parentId: 10,
     parentId: 10,
     sequence: 2,
     sequence: 2,

+ 4 - 4
src/views/issue-paper/IssuePaper.vue

@@ -88,17 +88,17 @@
             type="primary"
             type="primary"
             link
             link
             size="small"
             size="small"
-            @click="resetSingle(scope.row)"
+            @click="viewDetail(scope.row)"
           >
           >
-            重置
+            试卷详情
           </el-button>
           </el-button>
           <el-button
           <el-button
             type="primary"
             type="primary"
             link
             link
             size="small"
             size="small"
-            @click="viewDetail(scope.row)"
+            @click="resetSingle(scope.row)"
           >
           >
-            查看
+            重置
           </el-button>
           </el-button>
         </template>
         </template>
       </el-table-column>
       </el-table-column>

+ 122 - 0
src/views/reject/RejectManage.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item label="分组">
+        <el-input-number
+          v-model.number="searchModel.groupNo"
+          :min="1"
+          :max="999"
+          :step="1"
+          :precision="0"
+          :controls="false"
+          step-strictly
+          placeholder="请输入"
+          style="width: 120px"
+        >
+        </el-input-number>
+      </el-form-item>
+      <el-form-item label="打回原因">
+        <el-input
+          v-model.trim="searchModel.rejectReason"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item label="评卷员">
+        <el-input
+          v-model.trim="searchModel.marker"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item label="打回人">
+        <el-input
+          v-model.trim="searchModel.rejectBy"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column property="subject" label="科目" min-width="100" />
+      <el-table-column property="groupNo" label="分组序号" width="100" />
+      <el-table-column property="examNo" label="准考证号" width="120" />
+      <el-table-column property="secretNo" label="密号" width="100" />
+      <el-table-column
+        property="rejectReason"
+        label="打回原因"
+        min-width="150"
+      />
+      <el-table-column property="marker" label="评卷员" width="100" />
+      <el-table-column
+        property="scoreDetails"
+        label="给分明细"
+        min-width="120"
+      />
+      <el-table-column property="rejectBy" label="打回人" width="100" />
+      <el-table-column label="打回时间" width="180">
+        <template #default="scope">
+          {{ timestampFilter(scope.row.rejectTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="100" fixed="right">
+        <template #default="scope">
+          <el-button size="small" link @click="onViewDetail(scope.row)">
+            查看详情
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="pagination.pageNumber"
+      v-model:page-size="pagination.pageSize"
+      :layout="pagination.layout"
+      :total="pagination.total"
+      @size-change="pageSizeChange"
+      @current-change="toPage"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive } from 'vue';
+  import { getRejectList } from '@/api/reject';
+  import { RejectItem, RejectListFilter } from '@/api/types/reject';
+  import useTable from '@/hooks/table';
+  import { timestampFilter } from '@/utils/filter';
+
+  defineOptions({
+    name: 'RejectManage',
+  });
+
+  const searchModel = reactive<RejectListFilter>({
+    subject: null,
+    groupNo: undefined,
+    rejectReason: '',
+    marker: '',
+    rejectBy: '',
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<RejectItem>(getRejectList, searchModel, false);
+
+  function onViewDetail(row: RejectItem) {
+    // TODO: 实现查看详情功能
+    console.log('查看详情:', row);
+  }
+</script>

+ 116 - 0
src/views/reject/RejectRecord.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item label="分组">
+        <el-input-number
+          v-model.number="searchModel.groupNo"
+          :min="1"
+          :max="999"
+          :step="1"
+          :precision="0"
+          :controls="false"
+          step-strictly
+          placeholder="请输入"
+          style="width: 120px"
+        >
+        </el-input-number>
+      </el-form-item>
+      <el-form-item label="打回原因">
+        <el-input
+          v-model.trim="searchModel.rejectReason"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item label="评卷员">
+        <el-input
+          v-model.trim="searchModel.marker"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item label="打回人">
+        <el-input
+          v-model.trim="searchModel.rejectBy"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        >
+        </el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button @click="exportData">导出</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column property="subject" label="科目" min-width="100" />
+      <el-table-column property="groupNo" label="分组序号" width="100" />
+      <el-table-column property="examNo" label="准考证号" width="120" />
+      <el-table-column property="secretNo" label="密号" width="100" />
+      <el-table-column
+        property="rejectReason"
+        label="打回原因"
+        min-width="150"
+      />
+      <el-table-column property="marker" label="评卷员" width="100" />
+      <el-table-column
+        property="scoreDetails"
+        label="给分明细"
+        min-width="120"
+      />
+      <el-table-column property="rejectBy" label="打回人" width="100" />
+      <el-table-column label="打回时间" width="180">
+        <template #default="scope">
+          {{ timestampFilter(scope.row.rejectTime) }}
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="pagination.pageNumber"
+      v-model:page-size="pagination.pageSize"
+      :layout="pagination.layout"
+      :total="pagination.total"
+      @size-change="pageSizeChange"
+      @current-change="toPage"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive } from 'vue';
+  import { getRejectRecordList } from '@/api/reject';
+  import { RejectItem, RejectListFilter } from '@/api/types/reject';
+  import useTable from '@/hooks/table';
+  import { timestampFilter } from '@/utils/filter';
+
+  defineOptions({
+    name: 'RejectRecord',
+  });
+
+  const searchModel = reactive<RejectListFilter>({
+    subject: null,
+    groupNo: undefined,
+    rejectReason: '',
+    marker: '',
+    rejectBy: '',
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<RejectItem>(getRejectRecordList, searchModel, false);
+
+  function exportData() {
+    // TODO: 实现导出功能
+    console.log('导出问题卷数据');
+  }
+</script>

+ 100 - 0
src/views/reject/RejectStatistics.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item>
+        <el-checkbox v-model="searchModel.showGroupNo">显示分组</el-checkbox>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button @click="exportData">导出</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column
+        property="subject"
+        label="科目"
+        min-width="200"
+        sortable
+      />
+      <el-table-column
+        v-if="searchModel.showGroupNo"
+        property="groupNo"
+        label="分组"
+        min-width="100"
+      />
+      <el-table-column
+        property="totalTasks"
+        label="任务总数"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        property="completedTasks"
+        label="已完成"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        property="rejectCount"
+        label="打回数量"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        property="rejectRate"
+        label="打回率"
+        min-width="100"
+        sortable
+      >
+        <template #default="scope">
+          {{ formatPercentage(scope.row.rejectRate) }}
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="pagination.pageNumber"
+      v-model:page-size="pagination.pageSize"
+      :layout="pagination.layout"
+      :total="pagination.total"
+      @size-change="pageSizeChange"
+      @current-change="toPage"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive } from 'vue';
+  import { getRejectStatisticsList } from '@/api/reject';
+  import {
+    RejectStatisticsItem,
+    RejectStatisticsFilter,
+  } from '@/api/types/reject';
+  import useTable from '@/hooks/table';
+
+  defineOptions({
+    name: 'RejectStatistics',
+  });
+
+  const searchModel = reactive<RejectStatisticsFilter>({
+    subject: null,
+    showGroupNo: false,
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<RejectStatisticsItem>(getRejectStatisticsList, searchModel, false);
+
+  function formatPercentage(rate: number): string {
+    if (rate === 0) return '0%';
+    return `${(rate * 100).toFixed(1)}%`;
+  }
+
+  function exportData() {
+    // TODO: 实现导出功能
+    console.log('导出打回统计数据');
+  }
+</script>

+ 26 - 0
src/views/reject/index.vue

@@ -0,0 +1,26 @@
+<template>
+  <el-tabs v-model="activeTab" type="border-card">
+    <el-tab-pane label="打回页面" name="reject">
+      <RejectManage />
+    </el-tab-pane>
+    <el-tab-pane label="打回记录" name="record">
+      <RejectRecord />
+    </el-tab-pane>
+    <el-tab-pane label="打回统计" name="statistics">
+      <RejectStatistics />
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import RejectManage from './RejectManage.vue';
+  import RejectRecord from './RejectRecord.vue';
+  import RejectStatistics from './RejectStatistics.vue';
+
+  defineOptions({
+    name: 'RejectIndex',
+  });
+
+  const activeTab = ref('reject');
+</script>