|
@@ -0,0 +1,404 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="part-box">
|
|
|
|
+ <!-- 科目总分统计图表 -->
|
|
|
|
+ <div class="chart-container">
|
|
|
|
+ <h3>科目总分统计</h3>
|
|
|
|
+ <Chart ref="chartRef" :option="chartOption" style="height: 300px" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="part-box is-filter">
|
|
|
|
+ <el-form :model="searchModel" inline>
|
|
|
|
+ <el-form-item label="科目">
|
|
|
|
+ <select-subject v-model="searchModel.subject"></select-subject>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="层次">
|
|
|
|
+ <el-select
|
|
|
|
+ v-model="searchModel.level"
|
|
|
|
+ placeholder="请选择"
|
|
|
|
+ clearable
|
|
|
|
+ style="width: 120px"
|
|
|
|
+ >
|
|
|
|
+ <el-option label="高中" value="高中" />
|
|
|
|
+ <el-option label="初中" value="初中" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="选做科目">
|
|
|
|
+ <el-select
|
|
|
|
+ v-model="searchModel.optional"
|
|
|
|
+ placeholder="不限"
|
|
|
|
+ clearable
|
|
|
|
+ style="width: 120px"
|
|
|
|
+ >
|
|
|
|
+ <el-option label="是" :value="true" />
|
|
|
|
+ <el-option label="否" :value="false" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="专业类型">
|
|
|
|
+ <el-select
|
|
|
|
+ v-model="searchModel.majorType"
|
|
|
|
+ placeholder="请选择"
|
|
|
|
+ clearable
|
|
|
|
+ style="width: 120px"
|
|
|
|
+ >
|
|
|
|
+ <el-option label="理科" value="理科" />
|
|
|
|
+ <el-option label="文科" value="文科" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="状态">
|
|
|
|
+ <el-select
|
|
|
|
+ v-model="searchModel.status"
|
|
|
|
+ placeholder="不限"
|
|
|
|
+ clearable
|
|
|
|
+ style="width: 120px"
|
|
|
|
+ >
|
|
|
|
+ <el-option label="正常" value="正常" />
|
|
|
|
+ <el-option label="异常" value="异常" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="总分">
|
|
|
|
+ <el-input-number
|
|
|
|
+ v-model.number="searchModel.totalScore"
|
|
|
|
+ :min="0"
|
|
|
|
+ :max="9999"
|
|
|
|
+ :step="0.1"
|
|
|
|
+ :precision="1"
|
|
|
|
+ :controls="false"
|
|
|
|
+ step-strictly
|
|
|
|
+ placeholder="请输入"
|
|
|
|
+ style="width: 120px"
|
|
|
|
+ />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item>
|
|
|
|
+ <el-space wrap>
|
|
|
|
+ <el-button type="primary" @click="toPage(1)">查询</el-button>
|
|
|
|
+ <el-dropdown @command="onImportCommand">
|
|
|
|
+ <el-button type="primary">
|
|
|
|
+ 导入
|
|
|
|
+ <el-icon class="el-icon--right"><ArrowDown /> </el-icon>
|
|
|
|
+ </el-button>
|
|
|
|
+ <template #dropdown>
|
|
|
|
+ <el-dropdown-menu>
|
|
|
|
+ <el-dropdown-item command="objective">客观题</el-dropdown-item>
|
|
|
|
+ <el-dropdown-item command="subjectivewStruct"
|
|
|
|
+ >主观题结构</el-dropdown-item
|
|
|
|
+ >
|
|
|
|
+ <el-dropdown-item command="subjectivewGroup"
|
|
|
|
+ >主观题分组</el-dropdown-item
|
|
|
|
+ >
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dropdown>
|
|
|
|
+
|
|
|
|
+ <el-dropdown @command="onExportCommand">
|
|
|
|
+ <el-button type="primary">
|
|
|
|
+ 导出
|
|
|
|
+ <el-icon class="el-icon--right"><ArrowDown /> </el-icon>
|
|
|
|
+ </el-button>
|
|
|
|
+ <template #dropdown>
|
|
|
|
+ <el-dropdown-menu>
|
|
|
|
+ <el-dropdown-item command="fhy">客观题</el-dropdown-item>
|
|
|
|
+ <el-dropdown-item command="fhy">主观题</el-dropdown-item>
|
|
|
|
+ </el-dropdown-menu>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dropdown>
|
|
|
|
+
|
|
|
|
+ <el-button type="success" @click="onAnalysis">客观题统计</el-button>
|
|
|
|
+ <el-button type="primary" @click="onAnalysis">分析统计</el-button>
|
|
|
|
+ <el-button type="primary" @click="onImportCommand('package')"
|
|
|
|
+ >导入数据包</el-button
|
|
|
|
+ >
|
|
|
|
+ </el-space>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="part-box">
|
|
|
|
+ <el-table v-loading="loadingTable" :data="dataList" class="page-table">
|
|
|
|
+ <el-table-column prop="name" label="科目名称">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <el-button type="primary" link @click="onEditPaperStruct(row)">
|
|
|
|
+ {{ row.code }}-{{ row.name }}
|
|
|
|
+ </el-button>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="level" label="层次" />
|
|
|
|
+ <el-table-column prop="majorType" label="专业类型" />
|
|
|
|
+ <el-table-column prop="optional" label="选做科目">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ {{ row.optional ? '是' : '否' }}
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="paperUrl" label="试卷">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <el-link
|
|
|
|
+ v-if="row.paperUrl"
|
|
|
|
+ type="primary"
|
|
|
|
+ :href="row.paperUrl"
|
|
|
|
+ target="_blank"
|
|
|
|
+ >
|
|
|
|
+ 已上传
|
|
|
|
+ </el-link>
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="answerUrl" label="答案">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <el-link
|
|
|
|
+ v-if="row.answerUrl"
|
|
|
|
+ type="primary"
|
|
|
|
+ :href="row.answerUrl"
|
|
|
|
+ target="_blank"
|
|
|
|
+ >
|
|
|
|
+ 已上传
|
|
|
|
+ </el-link>
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column prop="paperType" label="试卷类型" />
|
|
|
|
+ <el-table-column prop="cardFormatType" label="卡格式" />
|
|
|
|
+ <el-table-column prop="objectiveTotalScore" label="客观总分" />
|
|
|
|
+ <el-table-column prop="subjectiveTotalScore" label="主观总分" />
|
|
|
|
+ <el-table-column prop="paperTotalScore" label="试卷总分" />
|
|
|
|
+ <el-table-column prop="status" label="状态">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <el-tag :type="row.status === '正常' ? 'success' : 'danger'">
|
|
|
|
+ {{ row.status }}
|
|
|
|
+ </el-tag>
|
|
|
|
+ </template>
|
|
|
|
+ </el-table-column>
|
|
|
|
+ <el-table-column label="操作" width="200">
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
+ <el-button size="small" type="primary" link @click="onEdit(row)">
|
|
|
|
+ 编辑
|
|
|
|
+ </el-button>
|
|
|
|
+ <el-button size="small" type="primary" link @click="onAnalysis(row)">
|
|
|
|
+ 分析计算
|
|
|
|
+ </el-button>
|
|
|
|
+ <el-button
|
|
|
|
+ size="small"
|
|
|
|
+ type="primary"
|
|
|
|
+ link
|
|
|
|
+ @click="onSetOptional(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>
|
|
|
|
+
|
|
|
|
+ <!-- 科目设置弹窗 -->
|
|
|
|
+ <SubjectSettingDialog
|
|
|
|
+ ref="subjectSettingDialogRef"
|
|
|
|
+ :subject-data="currentSubject"
|
|
|
|
+ @modified="getList"
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <!-- 导入 -->
|
|
|
|
+ <ImportDialog
|
|
|
|
+ ref="importDialogRef"
|
|
|
|
+ :title="importConfig?.title"
|
|
|
|
+ :upload-url="importConfig?.url"
|
|
|
|
+ :format="importConfig?.format"
|
|
|
|
+ :download-handle="downloadTemplate"
|
|
|
|
+ :download-filename="importConfig?.donloadFilename"
|
|
|
|
+ />
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+ import { reactive, ref, onMounted, computed } from 'vue';
|
|
|
|
+ import { useRouter } from 'vue-router';
|
|
|
|
+ import { getSubjectList, getSubjectTotalScoreStatList } from '@/api/subject';
|
|
|
|
+ import type {
|
|
|
|
+ SubjectItem,
|
|
|
|
+ SubjectListFilter,
|
|
|
|
+ SubjectTotalScoreStatItem,
|
|
|
|
+ } from '@/api/types/subject';
|
|
|
|
+ import useTable from '@/hooks/table';
|
|
|
|
+
|
|
|
|
+ import SubjectSettingDialog from './components/SubjectSettingDialog.vue';
|
|
|
|
+
|
|
|
|
+ defineOptions({
|
|
|
|
+ name: 'SubjectManage',
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const router = useRouter();
|
|
|
|
+
|
|
|
|
+ const searchModel = reactive<SubjectListFilter>({
|
|
|
|
+ subject: null,
|
|
|
|
+ level: '',
|
|
|
|
+ majorType: '',
|
|
|
|
+ optional: undefined,
|
|
|
|
+ status: '',
|
|
|
|
+ totalScore: undefined,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const {
|
|
|
|
+ dataList,
|
|
|
|
+ pagination,
|
|
|
|
+ getList,
|
|
|
|
+ toPage,
|
|
|
|
+ pageSizeChange,
|
|
|
|
+ loading: loadingTable,
|
|
|
|
+ } = useTable<SubjectItem>(getSubjectList, searchModel, false);
|
|
|
|
+
|
|
|
|
+ const subjectSettingDialogRef = ref<InstanceType<
|
|
|
|
+ typeof SubjectSettingDialog
|
|
|
|
+ > | null>(null);
|
|
|
|
+ const currentSubject = ref<SubjectItem | undefined>(undefined);
|
|
|
|
+ const chartRef = ref();
|
|
|
|
+ const chartOption = ref({});
|
|
|
|
+
|
|
|
|
+ // 更新图表
|
|
|
|
+ const updateChart = (data: SubjectTotalScoreStatItem[]) => {
|
|
|
|
+ chartOption.value = {
|
|
|
|
+ title: {
|
|
|
|
+ text: '科目总分统计',
|
|
|
|
+ left: 'center',
|
|
|
|
+ },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'axis',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: data.map((item) => item.totalScore),
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ name: '科目数量',
|
|
|
|
+ },
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ name: '数量',
|
|
|
|
+ type: 'bar',
|
|
|
|
+ data: data.map((item) => item.count),
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#409EFF',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 获取科目总分统计数据
|
|
|
|
+ const getStatData = async () => {
|
|
|
|
+ try {
|
|
|
|
+ // 这里需要传入实际的examId,暂时使用1作为示例
|
|
|
|
+ const data = await getSubjectTotalScoreStatList(1);
|
|
|
|
+ updateChart(data);
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取统计数据失败:', error);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const onEdit = (row: SubjectItem) => {
|
|
|
|
+ currentSubject.value = row;
|
|
|
|
+ subjectSettingDialogRef.value?.open();
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const onEditPaperStruct = (row: SubjectItem) => {
|
|
|
|
+ router.push({
|
|
|
|
+ name: 'PaperStructEdit',
|
|
|
|
+ params: { subjectId: row.id },
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const onAnalysis = (row: SubjectItem | undefined) => {
|
|
|
|
+ currentSubject.value = row;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const onSetOptional = (row: SubjectItem) => {
|
|
|
|
+ currentSubject.value = row;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 导出
|
|
|
|
+ const onExportCommand = (command: string) => {
|
|
|
|
+ console.log('导出命令:', command);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 导入
|
|
|
|
+ const impoartConfigList = ref([
|
|
|
|
+ {
|
|
|
|
+ code: 'objective',
|
|
|
|
+ title: '客观题',
|
|
|
|
+ url: '/api/admin/subject/import/kgt',
|
|
|
|
+ format: ['xls', 'xlsx'],
|
|
|
|
+ donloadFilename: '客观题导入模板.xlsx',
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ code: 'zgtjg',
|
|
|
|
+ title: 'subjectivewStruct',
|
|
|
|
+ url: '/api/admin/subject/import/zgtjg',
|
|
|
|
+ format: ['xls', 'xlsx'],
|
|
|
|
+ donloadFilename: '主观题结构导入模板.xlsx',
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ code: 'subjectivewGroup',
|
|
|
|
+ title: '主观题分组',
|
|
|
|
+ url: '/api/admin/subject/import/zgtfhy',
|
|
|
|
+ format: ['xls', 'xlsx'],
|
|
|
|
+ donloadFilename: '主观题分组导入模板.xlsx',
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ code: 'package',
|
|
|
|
+ title: '数据包',
|
|
|
|
+ url: '/api/admin/subject/import/sjb',
|
|
|
|
+ format: ['zip'],
|
|
|
|
+ donloadFilename: undefined,
|
|
|
|
+ },
|
|
|
|
+ ]);
|
|
|
|
+ const importDialogRef = ref();
|
|
|
|
+ const importData = reactive({
|
|
|
|
+ importType: 'objective',
|
|
|
|
+ });
|
|
|
|
+ const importConfig = computed(() => {
|
|
|
|
+ return impoartConfigList.value.find(
|
|
|
|
+ (item) => item.code === importData.importType
|
|
|
|
+ );
|
|
|
|
+ });
|
|
|
|
+ const onImportCommand = (command: string) => {
|
|
|
|
+ console.log('导入命令:', command);
|
|
|
|
+ importData.importType = command;
|
|
|
|
+ importDialogRef.value?.open();
|
|
|
|
+ };
|
|
|
|
+ async function downloadTemplate() {
|
|
|
|
+ // const res = await downloadByApi(() => agentTemplate()).catch((e) => {
|
|
|
|
+ // Message.error(e || '下载失败,请重新尝试!');
|
|
|
|
+ // });
|
|
|
|
+ // if (!res) return;
|
|
|
|
+ // Message.success('下载成功!');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ onMounted(() => {
|
|
|
|
+ getList();
|
|
|
|
+ getStatData();
|
|
|
|
+ });
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped>
|
|
|
|
+ .chart-container {
|
|
|
|
+ padding: 20px;
|
|
|
|
+ background: #fff;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .chart-container h3 {
|
|
|
|
+ margin: 0 0 20px 0;
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ font-weight: 500;
|
|
|
|
+ color: #303133;
|
|
|
|
+ }
|
|
|
|
+</style>
|