Learn.Archives.Web/src/views/exam/classExam.vue

367 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import ahTable from "@/components/hTable/index.vue";
import {
ConditionalType,
intTableData,
TableColumnSearch,
TableConfig,
} from "@/components/hTable/hTable";
import { onMounted, ref } from "vue";
import { fa } from "element-plus/es/locales.mjs";
import { hTableAPI } from "@/api/hTable";
import { getenum } from "@/api/enum";
import { ruleRequired, ruleRequiredNumber } from "@/utils/rules";
import { ClassRanking } from "@/api/exam";
import { ElMessage } from "element-plus";
const ControllerName = "ExamClassInfo";
defineOptions({
name: "ClassExam",
});
const props = defineProps<{
data: any;
}>();
function searchCallback(data) {}
const table = ref<{ initTable: (config: TableConfig) => void }>();
const tableData: TableConfig = intTableData({
apiUrl: ControllerName,
expandColumn: true,
selectColumn: false, // 列表选择
border: false, // 是否显示表格边框
searchCallback: searchCallback,
expandChange: expandChange,
search: {
// 查询条件
show: true,
PageSize: 999,
},
operationColumn: true, // 显示操作按钮
operationColumnData: [
{
topBtn: false, // 头部按钮
show: true,
label: "详情",
btnType: "custom",
btnStyle: "primary",
custom: {
title: "考试班级详情", // 弹出框title
src: "exam/classExamRecord", // 组件路径
width: "1600px", // 弹框宽度
height: "800px", // 弹框高度
},
},
],
column: {
// 行数据
schoolName: {
label: "学校",
width: "180px",
search: new TableColumnSearch(true, ConditionalType.Like),
},
grade: {
label: "年级",
width: "120px",
custom: (row) => row.gradeLevel + row.gradeYear + "届",
search: new TableColumnSearch(true),
},
className: {
label: "班级",
width: "80px",
search: new TableColumnSearch(true, ConditionalType.Like),
},
examName: {
label: "最近考试",
width: "200px",
},
peopleCount: {
label: "参考人数",
},
},
data: [],
pageData: {
total: 0,
},
selectRows: [],
});
const showTable = ref(false);
onMounted(async () => {
//初始化数据原
showTable.value = true;
});
// types/exam.ts 或直接在组件中定义
export interface ExamClassTag {
Name: string; // 标签名称
SubjectStr?: string; // 学科枚举,可空(总分则为 null
OnLineRanking: number; // 上线考试排名
OnLineRate: number; // 上线率注意C#中是 decimalTS里用 number
OnLineCount: number; // 上线人数
}
async function expandChange(row: any, expandedRows: any[]) {
if (row.rankingList != null) return;
let res = await ClassRanking(row.examId, row.classId);
if (res.code != 200) return ElMessage.error(res.message || "获取数据失败");
row.rankingList = res.data;
}
// 根据学科获取CSS类名
const getSubjectClass = (subjectStr) => {
if (!subjectStr) return "";
const subjectMap = {
总分: "total",
数学: "math",
英语: "english",
物理: "physics",
化学: "chemistry",
生物: "biology",
};
return subjectMap[subjectStr] || "";
};
</script>
<template>
<div>
<ahTable v-if="showTable" ref="table" :tableConfig="tableData">
<template #expandSlot="{ props }">
<!-- 拓展内容 -->
<div class="expanded-content expandSlot">
<div
v-if="props.row.rankingList && props.row.rankingList.length > 0"
class="ranking-list"
>
<div
v-for="(tag, tagIndex) in props.row.rankingList"
:key="tagIndex"
class="tag-card"
:class="getSubjectClass(tag.subjectStr)"
>
<div class="tag-name">
<span>{{ tag.name }}</span>
<span class="subject-badge">{{ tag.subjectStr || "总分" }}</span>
</div>
<div class="tag-details">
<div class="detail-item">
<span class="detail-label">上线排名:</span>
<span class="detail-value">第 {{ tag.onLineRanking }} 名</span>
</div>
<div class="detail-item">
<span class="detail-label">上线人数:</span>
<span class="detail-value">{{ tag.onLineCount }} 人</span>
</div>
<div class="detail-item">
<span class="detail-label">上线率:</span>
<span class="detail-value"
>{{ (tag.onLineRate * 100).toFixed(0) }}%</span
>
</div>
<div class="progress-container">
<div
class="progress-bar"
:style="{ width: tag.onLineRate * 100 + '%' }"
></div>
</div>
</div>
</div>
</div>
<div v-else class="empty-state">
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 15px"></i>
<p>暂无标签数据</p>
</div>
</div>
</template>
</ahTable>
</div>
</template>
<style>
.expandSlot {
padding: 10px 20px;
background: #f5f7fa;
}
.container {
width: 100%;
max-width: 1200px;
background: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(90deg, #3498db, #2c3e50);
color: white;
padding: 20px 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-weight: 600;
font-size: 1.8rem;
}
.header .subtitle {
opacity: 0.9;
font-size: 1rem;
}
.table-container {
padding: 20px;
}
.main-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.main-table th {
background-color: #f8f9fa;
padding: 15px;
text-align: left;
font-weight: 600;
color: #2c3e50;
border-bottom: 2px solid #e9ecef;
}
.main-table td {
padding: 15px;
border-bottom: 1px solid #e9ecef;
vertical-align: top;
}
.main-table tr:hover {
background-color: #f8f9fa;
}
.expand-btn {
background: #3498db;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.3s;
}
.expand-btn:hover {
background: #2980b9;
transform: translateY(-2px);
}
.ranking-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 15px;
margin-top: 10px;
}
.tag-card {
background: white;
border-radius: 8px;
padding: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
border-left: 4px solid #3498db;
transition: transform 0.3s, box-shadow 0.3s;
}
.tag-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.tag-card.total {
border-left-color: #777f8a;
}
.tag-card.math {
border-left-color: #e74c3c;
}
.tag-card.english {
border-left-color: #f39c12;
}
.tag-card.physics {
border-left-color: #9b59b6;
}
.tag-card.chemistry {
border-left-color: #1abc9c;
}
.tag-card.biology {
border-left-color: #2ecc71;
}
.tag-name {
font-weight: 600;
font-size: 1.1rem;
color: #2c3e50;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.subject-badge {
padding: 3px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
background: #ecf0f1;
}
.tag-details {
margin-top: 10px;
}
.detail-item {
display: flex;
justify-content: space-between;
margin-bottom: 0px;
font-size: 0.9rem;
}
.detail-label {
color: #7f8c8d;
}
.detail-value {
font-weight: 500;
color: #2c3e50;
}
.progress-container {
height: 8px;
background: #ecf0f1;
border-radius: 4px;
margin-top: 5px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #3498db, #2ecc71);
border-radius: 4px;
transition: width 0.5s ease;
}
.empty-state {
text-align: center;
padding: 30px;
color: #7f8c8d;
font-style: italic;
}
</style>