laobinghu 30f2115877 feat: dark mode adaptation for main pages
Add dark mode CSS variable overrides to index, events, teams, results, and scoreboard pages for consistent theme switching
2026-03-22 16:03:39 +08:00

306 lines
8.7 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.

<template>
<div class="events-container">
<el-card>
<template #header>
<div class="card-header">
<span>比赛项目管理</span>
<el-button type="primary" @click="showAddDialog = true">添加项目</el-button>
</div>
</template>
<el-form :inline="true" class="filter-form">
<!-- 类别筛选 -->
<el-form-item label="类别">
<el-select v-model="filters.category" placeholder="全部" clearable @change="loadEvents">
<el-option label="田赛" value="田赛" />
<el-option label="径赛" value="径赛" />
<el-option label="团体赛" value="团体赛" />
</el-select>
</el-form-item>
<!-- 年级筛选 -->
<el-form-item label="年级">
<el-select v-model="filters.grade" placeholder="全部" clearable @change="onFilterChange">
<el-option v-for="g in config.grades" :key="g" :label="g" :value="g" />
</el-select>
</el-form-item>
<!-- 班级类型筛选 -->
<el-form-item label="班级类型">
<el-select v-model="filters.classType" placeholder="全部" clearable :disabled="!filters.grade" @change="onFilterChange">
<el-option v-for="c in config.classTypes" :key="c" :label="c" :value="c" />
</el-select>
</el-form-item>
<!-- 性别筛选 -->
<el-form-item label="性别">
<el-select v-model="filters.gender" placeholder="全部" clearable :disabled="!filters.grade" @change="onFilterChange">
<el-option v-for="g in config.genders" :key="g" :label="g" :value="g" />
</el-select>
</el-form-item>
</el-form>
<el-table :data="events" border stripe class="events-table transition-base">
<el-table-column prop="id" label="ID" width="80" class-name="col-id" />
<el-table-column prop="name" label="项目名称" />
<el-table-column prop="category" label="类别" width="120" class-name="col-category" />
<el-table-column prop="event_group" label="组别" width="150" class-name="col-group" />
<el-table-column prop="unit" label="单位" width="100" class-name="col-unit" />
<el-table-column prop="status" label="状态" width="100" class-name="col-status">
<template #default="{ row }">
<el-tag :type="row.status === 'completed' ? 'success' : 'info'">
{{ row.status === 'completed' ? '已完成' : '进行中' }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="showAddDialog" title="添加比赛项目" width="500px">
<el-form :model="form" label-width="100px">
<el-form-item label="类别">
<el-select v-model="form.category" placeholder="请选择" @change="onCategoryChange">
<el-option label="田赛" value="田赛" />
<el-option label="径赛" value="径赛" />
<el-option label="团体赛" value="团体赛" />
</el-select>
</el-form-item>
<el-form-item label="项目名称">
<el-select v-model="form.name" placeholder="请选择">
<el-option
v-for="event in availableEvents"
:key="event.name"
:label="event.name"
:value="event.name"
/>
</el-select>
</el-form-item>
<el-form-item label="组别">
<el-select v-model="form.event_group" placeholder="请选择">
<el-option
v-for="g in allGroupOptions"
:key="g.value"
:label="g.label"
:value="g.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showAddDialog = false">取消</el-button>
<el-button type="primary" @click="addEvent">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { computed } from 'vue'
const events = ref([])
const showAddDialog = ref(false)
const config = ref({
grades: [] as string[],
classTypes: [] as string[],
genders: [] as string[],
all: [] as string[]
})
const filters = ref({
category: '',
grade: '',
classType: '',
gender: ''
})
const eventTypes = ref({})
const form = ref({
name: '',
category: '',
event_group: '',
unit: ''
})
const availableEvents = computed(() => {
if (!form.value.category) return []
return eventTypes.value[form.value.category] || []
})
// 为添加项目对话框生成所有可用的组别选项从config API获取
const allGroupOptions = computed(() => {
return (config.value.all || []).map(g => ({ value: g, label: g }))
})
const loadConfig = async () => {
try {
const res = await $fetch('/api/config')
config.value = res.data.groups
eventTypes.value = res.data.eventTypes
} catch (error) {
ElMessage.error('加载配置失败')
}
}
const loadEvents = async () => {
try {
const params = new URLSearchParams()
if (filters.value.category) params.append('category', filters.value.category)
if (filters.value.grade) params.append('grade', filters.value.grade)
if (filters.value.classType) params.append('classType', filters.value.classType)
if (filters.value.gender) params.append('gender', filters.value.gender)
const res = await $fetch(`/api/events?${params}`)
events.value = res.data
} catch (error) {
ElMessage.error('加载项目失败')
}
}
const onFilterChange = () => {
loadEvents()
}
const onCategoryChange = () => {
form.value.name = ''
form.value.unit = ''
}
const addEvent = async () => {
try {
const selectedEvent = availableEvents.value.find(e => e.name === form.value.name)
if (!selectedEvent) {
ElMessage.error('请选择项目')
return
}
await $fetch('/api/events', {
method: 'POST',
body: {
name: form.value.name,
category: form.value.category,
event_group: form.value.event_group,
unit: selectedEvent.unit
}
})
ElMessage.success('添加成功')
showAddDialog.value = false
form.value = { name: '', category: '', event_group: '', unit: '' }
loadEvents()
} catch (error) {
ElMessage.error('添加失败')
}
}
onMounted(() => {
loadConfig()
loadEvents()
})
</script>
<style scoped>
.events-container {
max-width: 1200px;
margin: 0 auto;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header span {
color: var(--header-text);
font-size: 18px;
font-weight: 600;
}
.filter-form {
margin-bottom: 20px;
}
/* 确保表格和表单在暗色模式下文字清晰 */
:deep(.el-card) {
background-color: var(--header-bg);
color: var(--header-text);
}
:deep(.el-card .el-card__header) {
border-bottom-color: var(--header-border);
}
:deep(.el-form-item__label) {
color: var(--header-text) !important;
}
:deep(.el-table) {
color: var(--header-text);
}
:deep(.el-table th) {
background-color: var(--header-bg);
color: var(--header-text);
}
:deep(.el-table tr) {
background-color: var(--header-bg);
}
:deep(.el-table td) {
color: var(--header-text);
}
/* Table row staggered animations */
.events-table :deep(.el-table__body tr) {
animation: slide-in-up 0.3s ease-out;
animation-fill-mode: both;
}
.events-table :deep(.el-table__body tr:nth-child(1)) { animation-delay: 0.05s; }
.events-table :deep(.el-table__body tr:nth-child(2)) { animation-delay: 0.1s; }
.events-table :deep(.el-table__body tr:nth-child(3)) { animation-delay: 0.15s; }
.events-table :deep(.el-table__body tr:nth-child(4)) { animation-delay: 0.2s; }
.events-table :deep(.el-table__body tr:nth-child(5)) { animation-delay: 0.25s; }
.events-table :deep(.el-table__body tr:nth-child(6)) { animation-delay: 0.3s; }
.events-table :deep(.el-table__body tr:nth-child(7)) { animation-delay: 0.35s; }
.events-table :deep(.el-table__body tr:nth-child(8)) { animation-delay: 0.4s; }
.events-table :deep(.el-table__body tr:nth-child(9)) { animation-delay: 0.45s; }
.events-table :deep(.el-table__body tr:nth-child(10)) { animation-delay: 0.5s; }
@keyframes slide-in-up {
0% {
opacity: 0;
transform: translateY(15px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 900px) {
.card-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.filter-form :deep(.el-form-item) {
margin-right: 0;
width: 100%;
}
.filter-form :deep(.el-select) {
width: 100%;
}
.events-table :deep(.col-id),
.events-table :deep(.col-category),
.events-table :deep(.col-unit),
.events-table :deep(.col-status) {
display: none;
}
}
</style>