zhangjie 1 жил өмнө
parent
commit
34d490ee87

+ 23 - 3
src/api/types/task.ts

@@ -197,18 +197,38 @@ export type TrackExportListPageRes = PageResult<TrackExportItem>;
 export interface TrackExportDetailListFilter {
   examId: string;
   paperNumber: string;
+  college?: string;
+  majorName?: string;
+  className?: string;
+  studentName?: string;
+  studentCode?: string;
+  startStudentCode?: string;
+  endStudentCode?: string;
+  startScore?: number;
+  endScore?: number;
+  objectiveStartScore?: number;
+  objectiveEndScore?: number;
+  subjectiveStartScore?: number;
+  subjectiveEndScore?: number;
+  orderType?: string;
+  orderField?: string;
 }
 
 export type TrackExportDetailListParams =
   PageParams<TrackExportDetailListFilter>;
 
 export interface TrackExportDetailItem {
-  studentId: string;
+  className: string;
+  college: string;
   courseCode: string;
   courseName: string;
-  studentName: string;
+  examEndTime: number;
+  examStartTime: number;
+  majorName: number;
   studentCode: string;
+  studentId: string;
+  studentName: string;
   sheetUrls: string[] | null;
-  className: string;
+  totalScore: number;
 }
 export type TrackExportDetailListPageRes = PageResult<TrackExportDetailItem>;

+ 89 - 4
src/views/base/track-export/index.vue

@@ -7,6 +7,7 @@
         select-default
         placeholder="请选择"
         prefix
+        :style="{ width: '260px' }"
         @change="semesterChange"
       />
       <SelectExam
@@ -27,6 +28,7 @@
         placeholder="请选择"
         prefix
         clearable
+        :style="{ width: '260px' }"
         @change="courseChange"
       />
       <a-button type="primary" @click="toPage(1)">查询</a-button>
@@ -62,6 +64,9 @@
         <a-button type="text" class="btn-primary" @click="toDownload(record)"
           >下载</a-button
         >
+        <a-button type="text" class="btn-primary" @click="toDetail(record)"
+          >查看详情</a-button
+        >
       </template>
     </a-table>
   </div>
@@ -76,6 +81,13 @@
     :task-id="trackTaskId"
     :task-stop="detailBuildStop"
   />
+  <!-- IndexDetail -->
+  <IndexDetail
+    ref="indexDetailRef"
+    :row-data="curRow"
+    @download-by-students="toDownloadByStudents"
+    @download-by-filter="toDownloadByFilter"
+  />
 </template>
 
 <script setup lang="ts">
@@ -86,7 +98,12 @@
   import useLoading from '@/hooks/loading';
 
   import { courseNameCodeFilter } from '@/utils/filter';
-  import { CourseItem, TrackExportItem } from '@/api/types/task';
+  import {
+    CourseItem,
+    TrackExportItem,
+    TrackExportDetailItem,
+    TrackExportDetailListFilter,
+  } from '@/api/types/task';
   import { trackExportListPage } from '@/api/task';
   import { TrackConfigType } from '@/store/modules/app/types';
   import { useAppStore, useUserStore } from '@/store';
@@ -97,6 +114,7 @@
   import TaskDetailBuildProgess from './taskDetailBuildProgess.vue';
   import ModifySet from './modifySet.vue';
   import TaskProgress from './taskProgress.vue';
+  import IndexDetail from './indexDetail.vue';
 
   defineOptions({
     name: 'TrackExport',
@@ -111,6 +129,8 @@
     courseCode: '',
   });
 
+  const curRow = ref({} as TrackExportItem);
+
   const columns: TableColumnData[] = [
     {
       title: '课程(代码)',
@@ -128,7 +148,7 @@
     {
       title: '操作',
       slotName: 'action',
-      width: 80,
+      width: 140,
       fixed: 'right',
       cellClass: 'action-column',
     },
@@ -174,6 +194,7 @@
     updateTrackTaskReady,
     getTrackExportDetailList,
     getTrackExportList,
+    createTrackTaskDetailsFromStudents,
   } = useTask();
   const taskProgressRef = ref();
   const taskDetailBuildProgessRef = ref();
@@ -264,7 +285,18 @@
   }
 
   // 单个课程下载
-  async function toDownload(row: TrackExportItem) {
+  function toDownload(row: TrackExportItem) {
+    toDownloadByFilter(row, {
+      examId: row.examId,
+      paperNumber: row.paperNumber,
+    });
+  }
+
+  // 设置筛选条件下载
+  async function toDownloadByFilter(
+    row: TrackExportItem,
+    filterData: TrackExportDetailListFilter
+  ) {
     detailBuildStop.value = false;
     if (loading.value) return;
 
@@ -297,7 +329,7 @@
 
     // 开始构建任务
     let tRes = true;
-    await getTrackExportDetailList(row).catch((error) => {
+    await getTrackExportDetailList(filterData).catch((error) => {
       console.log(error);
       tRes = false;
     });
@@ -314,6 +346,54 @@
     taskProgressRef.value?.open();
   }
 
+  // 选定学生下载
+  async function toDownloadByStudents(
+    row: TrackExportItem,
+    students: TrackExportDetailItem[]
+  ) {
+    if (loading.value) return;
+
+    const res = await downloadCheck();
+    if (!res) return;
+
+    setLoading(true);
+    let result = true;
+    await createTrackTask({
+      ...searchModel,
+      ...seNames,
+      courseCode: row.courseCode,
+      courseName: row.courseName,
+      courseId: '',
+      paperNumber: row.paperNumber,
+      schoolId: userStore.curSchoolInfo.id,
+      trackConfig: JSON.stringify(appStore.trackConfig),
+      status: 0,
+    }).catch(() => {
+      result = false;
+      setLoading(false);
+    });
+    if (!result) {
+      Message.error('创建任务错误!');
+      return;
+    }
+
+    // 开始构建任务
+    let tRes = true;
+    await createTrackTaskDetailsFromStudents(students).catch((error) => {
+      console.log(error);
+      tRes = false;
+    });
+    setLoading(false);
+
+    if (!tRes) {
+      Message.error('创建任务详情错误!');
+      return;
+    }
+
+    await updateTrackTaskReady();
+    taskProgressRef.value?.open();
+  }
+
   async function initData() {
     const res = await window.db.getDict('trackConfig');
     if (res) {
@@ -344,6 +424,11 @@
     return false;
   }
 
+  // 详情
+  function toDetail(row: TrackExportItem) {
+    console.log(row);
+  }
+
   onMounted(() => {
     initData();
   });

+ 342 - 0
src/views/base/track-export/indexDetail.vue

@@ -0,0 +1,342 @@
+<template>
+  <a-modal
+    v-model:visible="visible"
+    :width="500"
+    title-align="start"
+    :align-center="false"
+    :mask-closable="false"
+    :esc-to-close="false"
+    :footer="false"
+    fullscreen
+    @before-open="modalBeforeOpen"
+  >
+    <template #title> 班级详情 </template>
+
+    <div class="part-box is-filter">
+      <a-space class="filter-line" :size="12" wrap>
+        <a-input
+          v-model.trim="searchModel.college"
+          placeholder="学院"
+          allow-clear
+        >
+        </a-input>
+        <a-input
+          v-model.trim="searchModel.majorName"
+          placeholder="专业"
+          allow-clear
+        >
+        </a-input>
+        <a-input
+          v-model.trim="searchModel.className"
+          placeholder="班级"
+          allow-clear
+        >
+        </a-input>
+        <a-input
+          v-model.trim="searchModel.studentName"
+          placeholder="姓名"
+          allow-clear
+        >
+        </a-input>
+        <a-input
+          v-model.trim="searchModel.studentCode"
+          placeholder="学号"
+          allow-clear
+        >
+        </a-input>
+        <a-input-group>
+          <a-input
+            v-model.trim="searchModel.startStudentCode"
+            placeholder="起始学号"
+            :style="{ width: '160px' }"
+            allow-clear
+          >
+          </a-input>
+          <span class="mlr-1">-</span>
+          <a-input
+            v-model.trim="searchModel.endStudentCode"
+            placeholder="终止学号"
+            :style="{ width: '160px' }"
+            allow-clear
+          >
+          </a-input>
+        </a-input-group>
+        <a-input-group>
+          <a-input-number
+            v-model.trim="searchModel.startScore"
+            :style="{ width: '100px' }"
+            placeholder="成绩最低分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+          <a-input-number
+            v-model.trim="searchModel.endScore"
+            :style="{ width: '100px' }"
+            placeholder="成绩最高分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+        </a-input-group>
+        <a-input-group>
+          <a-input-number
+            v-model.trim="searchModel.objectiveStartScore"
+            :style="{ width: '100px' }"
+            placeholder="客观题最低总分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+          <a-input-number
+            v-model.trim="searchModel.objectiveEndScore"
+            :style="{ width: '100px' }"
+            placeholder="客观题最高总分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+        </a-input-group>
+        <a-input-group>
+          <a-input-number
+            v-model.trim="searchModel.subjectiveStartScore"
+            :style="{ width: '100px' }"
+            placeholder="主观题最低总分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+          <a-input-number
+            v-model.trim="searchModel.subjectiveEndScore"
+            :style="{ width: '100px' }"
+            placeholder="主观题最高总分"
+            :step="0.01"
+            :precision="2"
+            :min="0"
+            :max="9999"
+            hide-button
+          />
+        </a-input-group>
+        <a-button type="primary" @click="search">查询</a-button>
+      </a-space>
+    </div>
+
+    <div class="part-box">
+      <a-space class="part-action" :size="6">
+        <a-button
+          type="text"
+          :disabled="loading || !dataList?.length"
+          @click="toDownloadFilter"
+        >
+          <template #icon>
+            <svg-icon name="icon-import" />
+          </template>
+          批量下载查询数据
+        </a-button>
+        <a-button
+          type="text"
+          :disabled="!multipleSelections.length"
+          @click="toDownloadSelection"
+        >
+          <template #icon>
+            <svg-icon name="icon-add" />
+          </template>
+          批量下载选择数据
+        </a-button>
+      </a-space>
+      <a-table
+        class="page-table"
+        :columns="columns"
+        :data="dataList"
+        :pagination="pagination"
+        :scroll="{ x: 600 }"
+        :bordered="false"
+        row-key="studentId"
+        :row-selection="{
+          type: 'checkbox',
+          showCheckedAll: true,
+          onlyCurrent: false,
+        }"
+        @selection-change="selectionChange"
+      >
+        <template #courseCode="{ record }">
+          {{ courseNameCodeFilter(record) }}
+        </template>
+        <template #checkTime="{ record }">
+          {{ checkTimeFilter(record) }}
+        </template>
+        <template #action="{ record }">
+          <a-button type="text" class="btn-primary" @click="toDownload(record)"
+            >下载</a-button
+          >
+        </template>
+      </a-table>
+    </div>
+  </a-modal>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref } from 'vue';
+  import { TableColumnData } from '@arco-design/web-vue';
+
+  import useModal from '@/hooks/modal';
+  import useTable from '@/hooks/table';
+  import { courseNameCodeFilter } from '@/utils/filter';
+  import { objModifyAssign, parseTimeRangeDateAndTime } from '@/utils/utils';
+
+  import { trackExportDetailListPage } from '@/api/task';
+  import {
+    TrackExportDetailItem,
+    TrackExportItem,
+    TrackExportDetailListFilter,
+  } from '@/api/types/task';
+  import useLoading from '@/hooks/loading';
+
+  defineOptions({
+    name: 'TrackExportDetail',
+  });
+
+  const props = defineProps<{
+    rowData: TrackExportItem;
+  }>();
+
+  const emit = defineEmits<{
+    (
+      e: 'downloadByFilter',
+      row: TrackExportItem,
+      filterData: TrackExportDetailListFilter
+    ): any;
+    (
+      e: 'downloadByStudents',
+      row: TrackExportItem,
+      students: TrackExportDetailItem[]
+    ): any;
+  }>();
+
+  /* modal */
+  const { visible, open, close } = useModal();
+  defineExpose({ open, close });
+
+  const defaultSearchModel = {
+    examId: '',
+    paperNumber: '',
+    college: '',
+    majorName: '',
+    className: '',
+    studentName: '',
+    studentCode: '',
+    startStudentCode: '',
+    endStudentCode: '',
+    startScore: undefined,
+    endScore: undefined,
+    objectiveStartScore: undefined,
+    objectiveEndScore: undefined,
+    subjectiveStartScore: undefined,
+    subjectiveEndScore: undefined,
+    orderType: undefined,
+    orderField: undefined,
+  };
+
+  const searchModel = reactive({ ...defaultSearchModel });
+  let searchedModel = { ...searchModel };
+  const multipleSelections = ref<TrackExportDetailItem[]>([]);
+
+  const columns: TableColumnData[] = [
+    {
+      title: '姓名',
+      dataIndex: 'studentName',
+    },
+    {
+      title: '学号',
+      dataIndex: 'studentCode',
+    },
+    {
+      title: '院系',
+      dataIndex: 'collegeName',
+    },
+    {
+      title: '专业',
+      dataIndex: 'majorName',
+    },
+    {
+      title: '班级',
+      dataIndex: 'className',
+    },
+    {
+      title: '考试时间',
+      slotName: 'checkTime',
+      width: 180,
+    },
+    {
+      title: '课程(代码)',
+      slotName: 'courseCode',
+    },
+    {
+      title: '成绩',
+      dataIndex: 'totalScore',
+      width: 80,
+    },
+    {
+      title: '操作',
+      slotName: 'action',
+      width: 80,
+      fixed: 'right',
+      cellClass: 'action-column',
+    },
+  ];
+  const { dataList, pagination, toPage } = useTable<TrackExportDetailItem>(
+    trackExportDetailListPage,
+    searchModel,
+    false
+  );
+  const { loading, setLoading } = useLoading();
+  async function search() {
+    setLoading(true);
+    searchedModel = { ...searchModel };
+    await toPage(1).catch(() => {});
+    setLoading(false);
+  }
+
+  function checkTimeFilter(row: TrackExportDetailItem) {
+    const { date, time } = parseTimeRangeDateAndTime(
+      row.examStartTime,
+      row.examEndTime
+    );
+    return `${date || '--'} ${time || '--'}`;
+  }
+
+  function selectionChange(keys: (string | number)[]) {
+    if (!dataList.value) return;
+    console.log(keys);
+    multipleSelections.value = dataList.value.filter((item) =>
+      keys.includes(item.studentId)
+    );
+  }
+
+  function toDownloadFilter() {
+    emit('downloadByFilter', props.rowData, searchedModel);
+  }
+  function toDownloadSelection() {
+    emit('downloadByStudents', props.rowData, multipleSelections.value);
+  }
+
+  function toDownload(row: TrackExportDetailItem) {
+    emit('downloadByStudents', props.rowData, [row]);
+  }
+
+  function modalBeforeOpen() {
+    objModifyAssign(searchModel, defaultSearchModel);
+    objModifyAssign(searchModel, props.rowData);
+    search();
+  }
+</script>

+ 0 - 19
src/views/base/track-export/taskProgress.vue

@@ -12,25 +12,6 @@
   >
     <template #title> 任务进度 </template>
 
-    <a-descriptions
-      :data="taskInfo"
-      title="任务详情"
-      :align="{ label: 'right' }"
-      :column="1"
-    />
-
-    <a-descriptions title="进度" :column="1" :align="{ label: 'right' }">
-      <a-descriptions-item label="任务总数">
-        {{ total }}
-      </a-descriptions-item>
-      <a-descriptions-item label="完成数量">
-        {{ finishCount }}
-      </a-descriptions-item>
-      <a-descriptions-item label="总体进度">
-        <a-progress :percent="progressNum" :stroke-width="10" />
-      </a-descriptions-item>
-    </a-descriptions>
-
     <template #footer>
       <a-button v-if="progressNum >= 1" type="primary" @click="close"
         >确定</a-button

+ 27 - 4
src/views/base/track-export/useTask.ts

@@ -1,6 +1,8 @@
 import { ref } from 'vue';
 import { trackExportDetailListPage, trackExportListPage } from '@/api/task';
 import {
+  TrackExportDetailItem,
+  TrackExportDetailListFilter,
   TrackExportDetailListParams,
   TrackExportItem,
   TrackExportListFilter,
@@ -59,15 +61,17 @@ export default function useTask() {
 
     // get details
     const detailFetchFunc = trackExportList.map((item) =>
-      getTrackExportDetailList(item)
+      getTrackExportDetailList({
+        examId: item.examId,
+        paperNumber: item.paperNumber,
+      })
     );
     await Promise.all(detailFetchFunc);
   }
 
-  async function getTrackExportDetailList(data: TrackExportItem) {
+  async function getTrackExportDetailList(params: TrackExportDetailListFilter) {
     const filterData = {
-      examId: data.examId,
-      paperNumber: data.paperNumber,
+      ...params,
       pageSize,
     };
     const res = await createTrackTaskDetails({
@@ -113,11 +117,30 @@ export default function useTask() {
     };
   }
 
+  async function createTrackTaskDetailsFromStudents(
+    datas: TrackExportDetailItem[]
+  ) {
+    const details = datas
+      .filter((item) => item.sheetUrls)
+      .map((item) => {
+        return {
+          trackTaskId: trackTaskId.value,
+          studentId: item.studentId,
+          studentName: item.studentName,
+          studentCode: item.studentCode,
+          className: item.className,
+          status: TRACK_TASK_DETAIL_STATUS.INIT,
+        };
+      });
+    await window.db.createTrackTaskDetails(details);
+  }
+
   return {
     trackTaskId,
     createTrackTask,
     updateTrackTaskReady,
     getTrackExportList,
     getTrackExportDetailList,
+    createTrackTaskDetailsFromStudents,
   };
 }