feat: 查看已提交数据

This commit is contained in:
2026-04-13 15:23:32 +08:00
parent 9e20d709b0
commit 18c5ad9521
10 changed files with 470 additions and 18 deletions

View File

@@ -448,6 +448,8 @@ const handleTestComplete = () => {
}
}
const contestantId = computed(() => route.query.id || route.params.id)
const readOnly = computed(() => {
if (route.query.id && !hasValidEmail.value) {
return true

310
src/views/Preview/index.vue Normal file
View File

@@ -0,0 +1,310 @@
<template>
<div class="page-container">
<div class="card">
<section class="stats-section">
<h1 class="title">{{ $t('Preview.title') }}</h1>
<div class="count-badge">
<span class="label">{{ $t('Preview.total') }}</span>
<span class="number">{{ submittedCount }}</span>
</div>
<button v-loading="excelLoading" @click="handleDownloadAll" class="download-all-btn flex">
<SvgIcon name="CDownload" size="22" color="#fff" />
{{ $t('Preview.downloadExcel') }}
</button>
</section>
<div class="divider"></div>
<section class="download-section">
<h2 class="subtitle">{{ $t('Preview.range') }}</h2>
<div class="input-group">
<div class="field">
<label>{{ $t('Preview.startIndex') }}</label>
<input v-model.number="range.start" type="number" placeholder="10000" min="1" />
</div>
<div class="field">
<label>{{ $t('Preview.endIndex') }}</label>
<input v-model.number="range.end" type="number" :placeholder="maxIndex" min="1" />
</div>
</div>
<button v-loading="fileDownloading" @click="handleDownload" :disabled="!isValid" class="download-btn">
<span class="btn-text">{{ $t('Preview.download') }}</span>
</button>
<p v-if="errorMsg" class="status-text error">{{ errorMsg }}</p>
</section>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { Https } from '@/utils/request'
import { debounce } from 'lodash-es'
// 模拟已提交人数
const submittedCount = ref(0)
// 输入框绑定的数据模型
const range = ref({
start: null,
end: null
})
const maxIndex = ref(10000)
const handleFetchSubmittedCount = async () => {
Https.axiosGet(Https.httpUrls.getContestCount).then((res) => {
submittedCount.value = res.count
maxIndex.value = res.maxContestantNumber
})
}
// 逻辑判断:区间是否合法
const isValid = computed(() => {
const { start, end } = range.value
// 必须是数字,且起始>0结束>=起始,结束不超过当前总数
return start !== null && end !== null && start > 0 && end >= start && end <= maxIndex.value
})
// 错误提示文案
const errorMsg = computed(() => {
const { start, end } = range.value
if (start === null || end === null) return ''
if (start < 10000) return '起始序号必须大于 10000'
if (end < start) return '结束序号不能小于起始序号'
if (end > maxIndex.value) return `结束序号不能超过最大值 ${maxIndex.value}`
return ''
})
const excelLoading = ref(false)
const handleDownloadAll = debounce(() => {
excelLoading.value = true
Https.axiosGet(Https.httpUrls.getExcel, {
responseType: 'blob',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then((res) => {
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `Global_Awards_Applications_${Date.now()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
.finally(() => {
excelLoading.value = false
})
}, 500)
const fileDownloading = ref(false)
// 下载执行函数
const handleDownload = debounce(() => {
if (!isValid.value) return
fileDownloading.value = true
Https.axiosPost(
Https.httpUrls.postExportFile,
{
minContestantNumber: range.value.start,
maxContestantNumber: range.value.end
},
{ responseType: 'blob' }
)
.then((res) => {
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute(
'download',
`Global_Awards_Applications_${range.value.start}_to_${range.value.end}.zip`
)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
.finally(() => {
fileDownloading.value = false
})
}, 500)
onMounted(() => {
handleFetchSubmittedCount()
})
</script>
<style lang="less" scoped>
/* 全局背景容器 */
.page-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
/* 渐变背景:深邃蓝紫色调 */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* 核心卡片样式 */
.card {
background: #ffffff;
width: 80rem;
padding: 4rem;
border-radius: 2.4rem;
box-shadow: 0 2rem 4rem rgba(0, 0, 0, 0.2);
}
/* 统计区域 */
.title {
margin: 0 0 2.4rem 0;
font-size: 2.4rem;
text-align: center;
color: #2d3436;
font-weight: 800;
}
.count-badge {
background: #f8f9fa;
border: 0.2rem solid #edf2f7;
border-radius: 1.6rem;
padding: 2rem;
text-align: center;
}
.count-badge .label {
display: block;
font-size: 1.4rem;
color: #636e72;
margin-bottom: 0.8rem;
}
.count-badge .number {
font-size: 4rem;
font-weight: 900;
color: #4834d4;
}
.download-all-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 1.2rem;
background-color: #00b894;
color: white;
border: none;
border-radius: 1rem;
font-size: 2rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
column-gap: 1rem;
margin-top: 2rem;
.c-svg {
width: initial;
height: initial;
}
}
.divider {
height: 0.1rem;
background: #eee;
margin: 3.2rem 0;
}
/* 表单区域 */
.subtitle {
font-size: 1.6rem;
color: #2d3436;
margin-bottom: 2rem;
font-weight: 600;
}
.input-group {
display: flex;
gap: 1.6rem;
margin-bottom: 2.4rem;
}
.field {
flex: 1;
}
.field label {
display: block;
font-size: 1.2rem;
color: #b2bec3;
margin-bottom: 0.6rem;
font-weight: bold;
text-transform: uppercase;
}
.field input {
width: 100%;
padding: 1.2rem 1.6rem;
border: 0.2rem solid #dfe6e9;
border-radius: 1.2rem;
font-size: 1.6rem;
color: #2d3436;
transition: all 0.3s ease;
box-sizing: border-box;
}
.field input:focus {
outline: none;
border-color: #6c5ce7;
box-shadow: 0 0 0 4px rgba(108, 92, 231, 0.1);
}
/* 按钮逻辑 */
.download-btn {
width: 100%;
padding: 1.6rem;
border: none;
border-radius: 1.2rem;
background: #6c5ce7;
color: white;
font-size: 1.6rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
}
.download-btn:hover:not(:disabled) {
background: #5849be;
transform: translateY(-2px);
box-shadow: 0 0.8rem 1.5rem rgba(108, 92, 231, 0.3);
}
.download-btn:active:not(:disabled) {
transform: translateY(0);
}
.download-btn:disabled {
background: #dfe6e9;
color: #b2bec3;
cursor: not-allowed;
}
/* 状态文本 */
.status-text {
text-align: center;
font-size: 1.3rem;
margin-top: 1.6rem;
font-weight: 500;
}
.status-text.error {
color: #ff7675;
}
.status-text.success {
color: #00b894;
}
</style>