Browse Source

feat: test-科目分析

zhangjie 3 days ago
parent
commit
3a96a2a877

+ 20 - 2
src/api/analysis.ts

@@ -20,6 +20,9 @@ import {
   ScoreSegmentFilter,
   AnalysisListPageParam,
   AnalysisListFilter,
+  TeacherClassAnalysisListParam,
+  TeacherClassAnalysisFilter,
+  TeacherClassAnalysisListRes,
 } from './types/analysis';
 
 // 总量分析列表
@@ -91,17 +94,32 @@ export function exportTeacherAnalysisList(
   });
 }
 
+// 任课老师班级分析列表
+export function getTeacherClassAnalysisList(
+  params: TeacherClassAnalysisListParam
+): Promise<TeacherClassAnalysisListRes> {
+  return axios.post('/api/admin/report/teacher/class', params);
+}
+// 任课老师班级分析导出
+export function exportTeacherClassAnalysisList(
+  params: TeacherClassAnalysisFilter
+): Promise<AxiosResponse<Blob>> {
+  return axios.post('/api/admin/report/teacher/class/export', params, {
+    responseType: 'blob',
+  });
+}
+
 // 班级分析列表
 export function getClassAnalysisList(
   params: ClassAnalysisListParam
 ): Promise<ClassAnalysisListRes> {
-  return axios.post('/api/admin/report/teacher/class', params);
+  return axios.post('/api/admin/report/class', params);
 }
 // 班级分析导出
 export function exportClassAnalysisList(
   params: ClassAnalysisFilter
 ): Promise<AxiosResponse<Blob>> {
-  return axios.post('/api/admin/report/teacher/class/export', params, {
+  return axios.post('/api/admin/report/class/export', params, {
     responseType: 'blob',
   });
 }

+ 34 - 0
src/api/types/analysis.ts

@@ -126,6 +126,40 @@ export interface TeacherAnalysisFilter {
 }
 export type TeacherAnalysisListParam = PageParams<TeacherAnalysisFilter>;
 
+// 教师班级分析
+export interface TeacherClassAnalysisItem {
+  // 班级
+  className: string;
+  // 有效人数
+  realityCount: number;
+  // 及格人数
+  passCount: number;
+  // 优秀人数
+  excellentCount: number;
+  // 最高分
+  maxScore: number;
+  // 最低分
+  minScore: number;
+  // 及格率
+  passRate: number;
+  // 优秀率
+  excellentRate: number;
+  // 平均分
+  avgScore: number;
+  // 平均相对分
+  relativeAvgScore: number;
+}
+export type TeacherClassAnalysisListRes = PageResult<TeacherClassAnalysisItem>;
+
+export interface TeacherClassAnalysisFilter {
+  // 科目
+  subjectCode: string;
+  // 任课老师
+  teacherName: string;
+}
+export type TeacherClassAnalysisListParam =
+  PageParams<TeacherClassAnalysisFilter>;
+
 // 班级分析项目
 export interface ClassAnalysisItem {
   // 班级

+ 3 - 0
src/assets/style/base.scss

@@ -3,6 +3,9 @@
   align-items: center;
   justify-content: space-between;
 }
+.flex-start {
+  align-items: flex-start;
+}
 
 /* part */
 .part-box {

+ 3 - 2
src/components/select-exam/index.vue

@@ -40,10 +40,10 @@
       clearable: true,
       disabled: false,
       placeholder: '请选择考试',
-      defaultFirst: true,
+      defaultFirst: false,
     }
   );
-  const emit = defineEmits(['update:modelValue', 'change']);
+  const emit = defineEmits(['update:modelValue', 'change', 'defaultSelected']);
   const attrs = useAttrs();
 
   interface OptionListItem {
@@ -63,6 +63,7 @@
       selected.value = optionList.value[0].value;
       emit('update:modelValue', selected.value || undefined);
       emit('change', optionList.value[0]);
+      emit('defaultSelected', optionList.value[0]);
     }
   };
   search(); // Initial load

+ 15 - 11
src/components/select-subject/index.vue

@@ -36,16 +36,16 @@
       clearable?: boolean;
       disabled?: boolean;
       placeholder?: string;
-      multiple?: boolean;
+      defaultFirst?: boolean;
     }>(),
     {
       clearable: true,
       disabled: false,
       placeholder: '请选择科目',
-      multiple: false,
+      defaultFirst: false,
     }
   );
-  const emit = defineEmits(['update:modelValue', 'change']);
+  const emit = defineEmits(['update:modelValue', 'change', 'defaultSelected']);
   const attrs = useAttrs();
 
   interface OptionListItem {
@@ -61,18 +61,22 @@
     optionList.value = res.map((item) => {
       return { ...item, value: item.code, label: `${item.code}-${item.name}` };
     });
+
+    if (props.defaultFirst && optionList.value[0]) {
+      selected.value = optionList.value[0].value;
+      emit('update:modelValue', selected.value || undefined);
+      emit('change', optionList.value[0]);
+      emit('defaultSelected', optionList.value[0]);
+    }
   };
   search(); // Initial load
 
   const onChange = () => {
-    const selectedData = props.multiple
-      ? optionList.value.filter(
-          (item) =>
-            selected.value && (selected.value as number[]).includes(item.value)
-        )
-      : optionList.value.filter((item) => selected.value === item.value);
-    emit('update:modelValue', selected.value || null);
-    emit('change', props.multiple ? selectedData : selectedData[0]);
+    const selectedData = optionList.value.filter(
+      (item) => selected.value === item.value
+    );
+    emit('update:modelValue', selected.value || undefined);
+    emit('change', selectedData[0]);
   };
 
   watch(

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

@@ -147,6 +147,100 @@ const BASE: AppRouteRecordRaw = {
         title: '科目分析',
         requiresAuth: true,
       },
+      children: [
+        {
+          path: 'total-analysis',
+          name: 'TotalAnalysis',
+          component: () => import('@/views/analysis/TotalAnalysis.vue'),
+          meta: {
+            title: '总量分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'segment-analysis',
+          name: 'SegmentAnalysis',
+          component: () => import('@/views/analysis/SegmentAnalysis.vue'),
+          meta: {
+            title: '分段统计',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'college-analysis',
+          name: 'CollegeAnalysis',
+          component: () => import('@/views/analysis/CollegeAnalysis.vue'),
+          meta: {
+            title: '学院分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'teacher-analysis',
+          name: 'TeacherAnalysis',
+          component: () => import('@/views/analysis/TeacherAnalysis.vue'),
+          meta: {
+            title: '任课老师分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'teacher-class-analysis',
+          name: 'TeacherClassAnalysis',
+          component: () => import('@/views/analysis/TeacherClassAnalysis.vue'),
+          meta: {
+            title: '教师班级分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage', 'TeacherAnalysis'],
+          },
+        },
+        {
+          path: 'class-analysis',
+          name: 'ClassAnalysis',
+          component: () => import('@/views/analysis/ClassAnalysis.vue'),
+          meta: {
+            title: '班级分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'objective-analysis',
+          name: 'ObjectiveQuestionAnalysis',
+          component: () =>
+            import('@/views/analysis/ObjectiveQuestionAnalysis.vue'),
+          meta: {
+            title: '客观题分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'subjective-analysis',
+          name: 'SubjectiveQuestionAnalysis',
+          component: () =>
+            import('@/views/analysis/SubjectiveQuestionAnalysis.vue'),
+          meta: {
+            title: '主观题分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+        {
+          path: 'big-question-analysis',
+          name: 'BigQuestionAnalysis',
+          component: () => import('@/views/analysis/BigQuestionAnalysis.vue'),
+          meta: {
+            title: '大题分析',
+            requiresAuth: true,
+            relationRoutes: ['AnalysisManage'],
+          },
+        },
+      ],
     },
     {
       path: '/log-manage',

+ 8 - 0
src/store/modules/app/index.ts

@@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
 import { UserMenuItem } from '@/api/types/user';
 import { getUserPrivilegeMenu } from '@/api/user';
 import { AppState, AppMenuItem } from './types';
+import { relateRoutes } from './relation';
 
 // import { sysAdminMenus as menus } from './menuData';
 import { adminMenus as menus } from './menuData';
@@ -21,6 +22,13 @@ function getMenu(privilegeData: UserMenuItem[]): {
     validRoutes.push(item.privilegeUri);
   });
 
+  // 关联路由权限添加
+  Object.keys(relateRoutes).forEach((key) => {
+    if (validRoutes.includes(key)) {
+      validRoutes.push(...relateRoutes[key]);
+    }
+  });
+
   // 构建树形结构
   privilegeData.forEach((item) => {
     const node = map.get(item.code);

+ 23 - 0
src/store/modules/app/relation.ts

@@ -0,0 +1,23 @@
+export const relateRoutes = {
+  AnalysisManage: [
+    'TotalAnalysis',
+    'SegmentAnalysis',
+    'CollegeAnalysis',
+    'TeacherAnalysis',
+    'TeacherClassAnalysis',
+    'ClassAnalysis',
+    'ObjectiveQuestionAnalysis',
+    'SubjectiveQuestionAnalysis',
+    'BigQuestionAnalysis',
+  ],
+  MarkManage: [
+    'GroupManage',
+    'GroupEdit',
+    'MarkerManage',
+    'TrialManage',
+    'TaskManage',
+    'ArbitrationManage',
+    'QualityMonitor',
+    'ScoreCurve',
+  ],
+};

+ 65 - 79
src/views/analysis/AnalysisManage.vue

@@ -1,90 +1,76 @@
 <template>
-  <el-tabs v-model="activeTab" type="card" class="page-tab">
-    <el-tab-pane name="total">
-      <template #label>
-        <el-button :type="activeTab === 'total' ? 'primary' : 'default'"
-          >总量分析</el-button
-        >
-      </template>
-      <TotalAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="segment">
-      <template #label>
-        <el-button :type="activeTab === 'segment' ? 'primary' : 'default'"
-          >分段统计</el-button
-        >
-      </template>
-      <SegmentAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="college">
-      <template #label>
-        <el-button :type="activeTab === 'college' ? 'primary' : 'default'"
-          >学院分析</el-button
-        >
-      </template>
-      <CollegeAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="teacher">
-      <template #label>
-        <el-button :type="activeTab === 'teacher' ? 'primary' : 'default'"
-          >任课老师分析</el-button
-        >
-      </template>
-      <TeacherAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="class">
-      <template #label>
-        <el-button :type="activeTab === 'class' ? 'primary' : 'default'"
-          >班级分析</el-button
-        >
-      </template>
-      <ClassAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="objective">
-      <template #label>
-        <el-button :type="activeTab === 'objective' ? 'primary' : 'default'"
-          >客观题分析</el-button
-        >
-      </template>
-      <ObjectiveQuestionAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="subjective">
-      <template #label>
-        <el-button :type="activeTab === 'subjective' ? 'primary' : 'default'"
-          >主观题分析</el-button
-        >
-      </template>
-      <SubjectiveQuestionAnalysis />
-    </el-tab-pane>
-    <el-tab-pane name="bigQuestion">
-      <template #label>
-        <el-button :type="activeTab === 'bigQuestion' ? 'primary' : 'default'"
-          >大题分析</el-button
-        >
-      </template>
-      <BigQuestionAnalysis />
-    </el-tab-pane>
-  </el-tabs>
+  <div class="analysis-manage">
+    <!-- 顶部标签导航 -->
+    <el-tabs
+      v-model="activeMenu"
+      type="card"
+      class="page-tab"
+      @tab-click="handleTabClick"
+    >
+      <el-tab-pane v-for="tab in tabs" :key="tab.name" :name="tab.name">
+        <template #label>
+          <el-button :type="activeMenu === tab.name ? 'primary' : 'default'">{{
+            tab.label
+          }}</el-button>
+        </template>
+      </el-tab-pane>
+    </el-tabs>
+
+    <!-- 子页面内容区域 -->
+    <router-view />
+  </div>
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
-  import { useRoute } from 'vue-router';
-
-  import TotalAnalysis from './TotalAnalysis.vue';
-  import SegmentAnalysis from './SegmentAnalysis.vue';
-  import CollegeAnalysis from './CollegeAnalysis.vue';
-  import TeacherAnalysis from './TeacherAnalysis.vue';
-  import ClassAnalysis from './ClassAnalysis.vue';
-  import ObjectiveQuestionAnalysis from './ObjectiveQuestionAnalysis.vue';
-  import SubjectiveQuestionAnalysis from './SubjectiveQuestionAnalysis.vue';
-  import BigQuestionAnalysis from './BigQuestionAnalysis.vue';
+  import { ref, watch, onMounted } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
 
   defineOptions({
     name: 'AnalysisManage',
   });
 
   const route = useRoute();
-  // 从路由参数获取tab值,默认为total
-  const activeTab = ref((route.query.tab as string) || 'total');
+  const router = useRouter();
+
+  // 标签页配置
+  const tabs = [
+    { name: 'TotalAnalysis', label: '总量分析' },
+    { name: 'SegmentAnalysis', label: '分段统计' },
+    { name: 'CollegeAnalysis', label: '学院分析' },
+    { name: 'TeacherAnalysis', label: '任课老师分析' },
+    { name: 'ClassAnalysis', label: '班级分析' },
+    { name: 'ObjectiveQuestionAnalysis', label: '客观题分析' },
+    { name: 'SubjectiveQuestionAnalysis', label: '主观题分析' },
+    { name: 'BigQuestionAnalysis', label: '大题分析' },
+  ];
+
+  // 当前激活的菜单项
+  const activeMenu = ref<string>('');
+
+  // 标签点击处理
+  function handleTabClick(tab: any) {
+    const tabName = tab.paneName || tab.props.name;
+    if (tabName !== route.name) {
+      router.push({ name: tabName });
+    }
+  }
+
+  // 监听路由变化,更新激活菜单
+  watch(
+    () => route.name,
+    (newName) => {
+      activeMenu.value = newName as string;
+    },
+    { immediate: true }
+  );
+
+  // 组件挂载时初始化
+  onMounted(() => {
+    // 如果当前路径是父路由,默认跳转到总量分析
+    if (route.name === 'AnalysisManage') {
+      router.replace({ name: 'TotalAnalysis' });
+    } else {
+      activeMenu.value = route.name as string;
+    }
+  });
 </script>

+ 6 - 1
src/views/analysis/BigQuestionAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item>
         <el-space wrap>

+ 6 - 1
src/views/analysis/ClassAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item label="班级">
         <el-input

+ 6 - 1
src/views/analysis/CollegeAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item label="学生院系">
         <el-input

+ 6 - 1
src/views/analysis/ObjectiveQuestionAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item label="试卷类型">
         <el-select

+ 7 - 3
src/views/analysis/SegmentAnalysis.vue

@@ -2,13 +2,17 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item label="分数间隔类型">
         <el-select
-          v-model="searchModel.intervalType"
+          v-model="searchModel.range"
           placeholder="请选择"
-          clearable
           style="width: 150px"
         >
           <el-option label="5分间隔" value="5" />

+ 6 - 1
src/views/analysis/SubjectiveQuestionAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item>
         <el-space wrap>

+ 27 - 2
src/views/analysis/TeacherAnalysis.vue

@@ -2,7 +2,12 @@
   <div class="part-box is-border">
     <el-form inline>
       <el-form-item label="科目">
-        <select-subject v-model="searchModel.subjectCode"></select-subject>
+        <select-subject
+          v-model="searchModel.subjectCode"
+          :clearable="false"
+          default-first
+          @default-selected="toPage(1)"
+        ></select-subject>
       </el-form-item>
       <el-form-item label="任课老师">
         <el-input
@@ -29,7 +34,13 @@
       stripe
     >
       <el-table-column type="index" label="序号" width="60" />
-      <el-table-column prop="teacherName" label="任课老师" min-width="120" />
+      <el-table-column prop="teacherName" label="任课老师" min-width="120">
+        <template #default="scope">
+          <el-button type="primary" link @click="toTeacherClass(scope.row)">
+            <span>{{ scope.row.teacherName }}</span>
+          </el-button>
+        </template>
+      </el-table-column>
       <el-table-column prop="totalCount" label="报考人数" min-width="100" />
       <el-table-column prop="realityCount" label="有效人数" min-width="100" />
       <el-table-column prop="passCount" label="及格人数" min-width="100" />
@@ -74,17 +85,20 @@
 
 <script setup lang="ts">
   import { reactive } from 'vue';
+  import { useRouter } from 'vue-router';
   import { getTeacherAnalysisList } from '@/api/analysis';
   import {
     TeacherAnalysisItem,
     TeacherAnalysisFilter,
   } from '@/api/types/analysis';
   import useTable from '@/hooks/table';
+  import ls from '@/utils/storage';
   import { downloadExport } from '@/utils/download-export';
 
   defineOptions({
     name: 'TeacherAnalysis',
   });
+  const router = useRouter();
 
   const searchModel = reactive<TeacherAnalysisFilter>({
     subjectCode: null,
@@ -97,4 +111,15 @@
   async function onExport() {
     await downloadExport('exportTeacherAnalysisList', searchModel);
   }
+
+  function toTeacherClass(row: TeacherAnalysisItem) {
+    ls.set('teacherClassAnalysis', {
+      subjectCode: row.subjectCode,
+      subjectName: row.subjectName,
+      teacherName: row.teacherName,
+    });
+    router.push({
+      name: 'TeacherClassAnalysis',
+    });
+  }
 </script>

+ 117 - 0
src/views/analysis/TeacherClassAnalysis.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="part-box is-border">
+    <el-form inline>
+      <el-form-item label="科目">
+        {{ teacherInfo.subjectCode }}-{{ teacherInfo.subjectName }}
+      </el-form-item>
+      <el-form-item label="班级">
+        {{ teacherInfo.teacherName }}
+      </el-form-item>
+      <el-form-item>
+        <el-space wrap>
+          <el-button type="primary" @click="toPage(1)">查询</el-button>
+          <el-button @click="onExport">导出</el-button>
+          <el-button @click="onExport">查看统计图</el-button>
+        </el-space>
+      </el-form-item>
+    </el-form>
+    <el-divider class="form-divider" />
+
+    <el-table
+      class="page-table"
+      :data="dataList"
+      :loading="loading"
+      border
+      stripe
+    >
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column prop="className" label="班级" min-width="150" />
+      <el-table-column prop="realityCount" label="有效人数" min-width="100" />
+      <el-table-column prop="passCount" label="及格人数" min-width="100" />
+      <el-table-column prop="excellentCount" label="优秀人数" min-width="100" />
+      <el-table-column prop="maxScore" label="最高分" min-width="100" />
+      <el-table-column prop="minScore" label="最低分" min-width="100" />
+      <el-table-column label="及格率" min-width="100">
+        <template #default="scope">
+          {{ (scope.row.passRate * 100).toFixed(2) }}%
+        </template>
+      </el-table-column>
+      <el-table-column label="优秀率" min-width="100">
+        <template #default="scope">
+          {{ (scope.row.excellentRate * 100).toFixed(2) }}%
+        </template>
+      </el-table-column>
+      <el-table-column prop="avgScore" label="平均分" min-width="100">
+        <template #default="scope">
+          {{ scope.row.avgScore?.toFixed(2) }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="relativeAvgScore"
+        label="平均相对分"
+        min-width="100"
+      >
+        <template #default="scope">
+          {{ scope.row.relativeAvgScore?.toFixed(2) }}
+        </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, onUnmounted } from 'vue';
+  import { getTeacherClassAnalysisList } from '@/api/analysis';
+  import {
+    TeacherClassAnalysisItem,
+    TeacherClassAnalysisFilter,
+  } from '@/api/types/analysis';
+  import useTable from '@/hooks/table';
+  import ls from '@/utils/storage';
+  import { downloadExport } from '@/utils/download-export';
+
+  defineOptions({
+    name: 'TeacherClassAnalysis',
+  });
+
+  interface TeacherInfo {
+    subjectCode: string;
+    subjectName: string;
+    teacherName: string;
+  }
+  const teacherInfo = reactive<TeacherInfo>(
+    ls.get('teacherClassAnalysis', {
+      subjectCode: '',
+      subjectName: '',
+      teacherName: '',
+    } as TeacherInfo)
+  );
+
+  const searchModel = reactive<TeacherClassAnalysisFilter>({
+    subjectCode: teacherInfo.subjectCode,
+    teacherName: teacherInfo.teacherName,
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<TeacherClassAnalysisItem>(
+      getTeacherClassAnalysisList,
+      searchModel,
+      false
+    );
+
+  async function onExport() {
+    await downloadExport('exportClassAnalysisList', searchModel);
+  }
+
+  onUnmounted(() => {
+    ls.remove('teacherClassAnalysis');
+  });
+</script>

+ 2 - 2
src/views/analysis/TotalAnalysis.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="part-box is-border">
-    <div class="box-justify">
+    <div class="box-justify flex-start">
       <el-form inline>
         <el-form-item label="科目">
           <select-subject v-model="searchModel.subjectCode"></select-subject>
@@ -83,7 +83,7 @@
   });
 
   const { dataList, pagination, loading, toPage, pageSizeChange } =
-    useTable<TotalAnalysisItem>(getTotalAnalysisList, searchModel, false);
+    useTable<TotalAnalysisItem>(getTotalAnalysisList, searchModel, true);
 
   async function onExport() {
     await downloadExport('exportTotalAnalysisList', searchModel);

+ 15 - 6
src/views/log/LogManage.vue

@@ -56,11 +56,19 @@
           {{ scope.row.marker ? '评卷员' : '管理员' }}
         </template>
       </el-table-column>
-      <el-table-column prop="type" label="操作类型" width="100" />
-      <el-table-column prop="ipAddress" label="登录IP" width="140" />
-      <el-table-column prop="menu" label="功能模块" min-width="200" />
-      <el-table-column prop="createTime" label="操作时间" width="180" />
-      <el-table-column prop="description" label="详情" width="180" />
+      <el-table-column prop="type" label="操作类型" width="100">
+        <template #default="scope">
+          {{ dictFilter.logType(scope.row.type) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="ipAddress" label="登录IP" width="160" />
+      <el-table-column prop="menu" label="功能模块" min-width="100" />
+      <el-table-column prop="createTime" label="操作时间" width="180">
+        <template #default="scope">
+          {{ timestampFilter(scope.row.createTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="description" label="详情" min-width="200" />
     </el-table>
     <el-pagination
       v-model:current-page="pagination.pageNumber"
@@ -79,6 +87,7 @@
   import { LogItem, LogListFilter } from '@/api/types/log';
   import useTable from '@/hooks/table';
   import { LOG_TYPE } from '@/constants/enumerate';
+  import { dictFilter, timestampFilter } from '@/utils/filter';
   import { downloadExport } from '@/utils/download-export';
 
   defineOptions({
@@ -92,7 +101,7 @@
   });
 
   const { dataList, pagination, loading, toPage, pageSizeChange } =
-    useTable<LogItem>(getLogList, searchModel, false);
+    useTable<LogItem>(getLogList, searchModel, true);
 
   function onExport() {
     downloadExport('exportLog', searchModel);

+ 1 - 0
src/views/login/SwitchExam.vue

@@ -15,6 +15,7 @@
         <SelectExam
           v-model="formData.examId"
           size="large"
+          default-first
           @change="onExamChange"
         />
       </el-form-item>