laobinghu 4df5c13976 refactor: simplify dark mode with VueUse + Element Plus
Remove custom CSS variable overrides, use Element Plus built-in dark mode with VueUse useDark
2026-03-22 16:22:35 +08:00

279 lines
7.7 KiB
Vue

<template>
<div class="results-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.event_id" placeholder="全部" clearable @change="loadResults">
<el-option
v-for="event in events"
:key="event.id"
:label="`${event.name} (${event.event_group})`"
:value="event.id"
/>
</el-select>
</el-form-item>
</el-form>
<el-table :data="results" border stripe class="results-table transition-base">
<el-table-column prop="event_name" label="项目名称" />
<el-table-column prop="team_name" label="队伍" />
<el-table-column prop="team_group" label="组别" width="150" class-name="col-group" />
<el-table-column prop="score" label="成绩" width="120" />
<el-table-column prop="unit" label="单位" width="80" class-name="col-unit" />
<el-table-column prop="rank" label="名次" width="100" class-name="col-rank">
<template #default="{ row }">
<el-tag v-if="row.rank === 1" type="warning">第1名</el-tag>
<el-tag v-else-if="row.rank === 2" type="success">第2名</el-tag>
<el-tag v-else-if="row.rank === 3" type="info">第3名</el-tag>
<span v-else-if="row.rank">{{ row.rank }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="created_at" label="录入时间" width="180" class-name="col-time">
<template #default="{ row }">
{{ new Date(row.created_at).toLocaleString('zh-CN') }}
</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.event_id" placeholder="请选择" @change="onEventChange">
<el-option
v-for="event in events"
:key="event.id"
:label="`${event.name} (${event.event_group})`"
:value="event.id"
/>
</el-select>
</el-form-item>
<el-form-item label="参赛队伍">
<el-select v-model="form.team_id" placeholder="请选择">
<el-option
v-for="team in filteredTeams"
:key="team.id"
:label="team.name"
:value="team.id"
/>
</el-select>
</el-form-item>
<el-form-item label="成绩">
<el-input v-model="form.score" placeholder="请输入成绩">
<template #append>{{ currentUnit }}</template>
</el-input>
</el-form-item>
<el-form-item label="名次">
<el-input-number v-model="form.rank" :min="1" placeholder="请输入名次" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showAddDialog = false">取消</el-button>
<el-button type="primary" @click="addResult">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
const results = ref([])
const events = ref([])
const teams = ref([])
const showAddDialog = ref(false)
const filters = ref({ event_id: '' })
const form = ref({
event_id: null,
team_id: null,
score: '',
rank: null
})
const currentUnit = computed(() => {
const event = events.value.find(e => e.id === form.value.event_id)
return event?.unit || ''
})
const filteredTeams = computed(() => {
const event = events.value.find(e => e.id === form.value.event_id)
if (!event) return []
return teams.value.filter(t => t.team_group === event.event_group)
})
const onEventChange = () => {
form.value.team_id = null
}
const loadEvents = async () => {
try {
const res = await $fetch('/api/events')
events.value = res.data
} catch (error) {
ElMessage.error('加载项目失败')
}
}
const loadTeams = async () => {
try {
const res = await $fetch('/api/teams')
teams.value = res.data
} catch (error) {
ElMessage.error('加载队伍失败')
}
}
const loadResults = async () => {
try {
const params = new URLSearchParams()
if (filters.value.event_id) params.append('event_id', filters.value.event_id)
const res = await $fetch(`/api/results?${params}`)
results.value = res.data
} catch (error) {
ElMessage.error('加载成绩失败')
}
}
const addResult = async () => {
if (!form.value.event_id || !form.value.team_id || !form.value.score) {
ElMessage.error('请填写完整信息')
return
}
try {
await $fetch('/api/results', {
method: 'POST',
body: form.value
})
ElMessage.success('录入成功')
showAddDialog.value = false
form.value = { event_id: null, team_id: null, score: '', rank: null }
loadResults()
} catch (error) {
ElMessage.error('录入失败')
}
}
onMounted(() => {
loadEvents()
loadTeams()
loadResults()
})
</script>
<style scoped>
.results-container {
max-width: 1200px;
margin: 0 auto;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header span {
color: var(--el-text-color-primary);
font-size: 18px;
font-weight: 600;
}
.filter-form {
margin-bottom: 20px;
}
/* 确保表格和表单在暗色模式下文字清晰 */
:deep(.el-card) {
background-color: var(--header-bg);
color: var(--el-text-color-primary);
}
:deep(.el-card .el-card__header) {
border-bottom-color: var(--el-border-color);
}
:deep(.el-form-item__label) {
color: var(--el-text-color-primary) !important;
}
:deep(.el-table) {
color: var(--el-text-color-primary);
}
:deep(.el-table th) {
background-color: var(--header-bg);
color: var(--el-text-color-primary);
}
:deep(.el-table tr) {
background-color: var(--header-bg);
}
:deep(.el-table td) {
color: var(--el-text-color-primary);
}
/* Table row staggered animations */
.results-table :deep(.el-table__body tr) {
animation: slide-in-up 0.3s ease-out;
animation-fill-mode: both;
}
.results-table :deep(.el-table__body tr:nth-child(1)) { animation-delay: 0.05s; }
.results-table :deep(.el-table__body tr:nth-child(2)) { animation-delay: 0.1s; }
.results-table :deep(.el-table__body tr:nth-child(3)) { animation-delay: 0.15s; }
.results-table :deep(.el-table__body tr:nth-child(4)) { animation-delay: 0.2s; }
.results-table :deep(.el-table__body tr:nth-child(5)) { animation-delay: 0.25s; }
.results-table :deep(.el-table__body tr:nth-child(6)) { animation-delay: 0.3s; }
.results-table :deep(.el-table__body tr:nth-child(7)) { animation-delay: 0.35s; }
.results-table :deep(.el-table__body tr:nth-child(8)) { animation-delay: 0.4s; }
.results-table :deep(.el-table__body tr:nth-child(9)) { animation-delay: 0.45s; }
.results-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%;
}
.results-table :deep(.col-group),
.results-table :deep(.col-unit),
.results-table :deep(.col-time) {
display: none;
}
}
</style>