赛事报名和赛事记录页面
This commit is contained in:
parent
c4d302f0ca
commit
aee2f29e05
@ -292,4 +292,27 @@ export const usereventregistrationProjectCompletionCheckPost = (data) => {
|
||||
*/
|
||||
export const usereventregistrationOrderStatusGet = (orderNo) => {
|
||||
return http.get(`/usereventregistration/order/status/${orderNo}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 费用计算预览接口
|
||||
* @param {Object} data - 计算预览信息
|
||||
* @param {number} data.eventId - 赛事ID
|
||||
* @param {Array<{groupId: number, memberIds: Array<number>}>} [data.groupMembers] - 组别队员分配
|
||||
* @param {Array<number>} [data.memberIds] - 队员ID列表(当无组别时使用)
|
||||
* @returns {Promise<{
|
||||
* chargeMode: string,
|
||||
* totalAmount: number,
|
||||
* totalPersonCount: number,
|
||||
* feeDetails: Array<{
|
||||
* itemName: string,
|
||||
* unitPrice: number,
|
||||
* quantity: number,
|
||||
* subtotal: number,
|
||||
* remark: string
|
||||
* }>
|
||||
* }>}
|
||||
*/
|
||||
export const usereventregistrationFeePreviewPost = (data) => {
|
||||
return http.post('/usereventregistration/fee/preview', data);
|
||||
};
|
||||
@ -94,7 +94,9 @@
|
||||
preserve-expanded-content @selectionChange="selection">
|
||||
<template #tools>
|
||||
<el-button v-auth="'event:add'" type="primary" :loading="importingScore" @click="inScore"> 导入成绩 </el-button>
|
||||
<el-button v-auth="'event:add'" type="primary" @click="downloadTemplate"> 下载模板 </el-button>
|
||||
<el-button v-auth="'event:add'" type="primary" @click="allSure"> 发放证书 </el-button>
|
||||
<el-button v-auth="'event:add'" type="primary" @click="exportCertificate"> 导出证书 </el-button>
|
||||
</template>
|
||||
<template #index="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
@ -155,7 +157,7 @@ import { ArrowLeft, ArrowRight, Check, CircleCheckFilled, Refresh } from '@eleme
|
||||
import { eventPost, eventPut, eventDetailGet } from '/@/api/match/event';
|
||||
import { eventarrangementListGet, eventarrangementPut, eventarrangementExportPost } from '/@/api/match/eventarrangement';
|
||||
import { eventregistrationPagePost, eventregistrationBatchauditPost, eventregistrationMaterialurlsPost } from '/@/api/match/eventregistration';
|
||||
import { eventscorePageathletePost, eventscorePagecoachleaderPost,eventscoreImportscorePost } from '/@/api/match/eventscore';
|
||||
import { eventscorePageathletePost, eventscorePagecoachleaderPost,eventscoreImportscorePost, eventscoreDownloadtemplateGet, eventscoreExportcertificatePost } from '/@/api/match/eventscore';
|
||||
import Step1BasicInfo from './Steps/Step1BasicInfo.vue';
|
||||
import Step2Units from './Steps/Step2Units.vue';
|
||||
import Step3ProjectConfig from './Steps/Step3ProjectConfig.vue';
|
||||
@ -549,6 +551,50 @@ const onScoreFileChange = async (event) => {
|
||||
}
|
||||
};
|
||||
|
||||
const downloadTemplate = async () => {
|
||||
try {
|
||||
const data = await eventscoreDownloadtemplateGet();
|
||||
const blob = new Blob([data], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
downloadByBlob(blob, '成绩导入模板.xlsx');
|
||||
ElMessage.success('模板下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error);
|
||||
ElMessage.error('下载模板失败');
|
||||
}
|
||||
};
|
||||
|
||||
const exportCertificate = async () => {
|
||||
try {
|
||||
const { succeeded, data, errors } = await eventscoreExportcertificatePost({
|
||||
eventId: initParam.value.id
|
||||
});
|
||||
if (!succeeded) {
|
||||
ElMessage.error(errors || '导出证书失败');
|
||||
return;
|
||||
}
|
||||
if (!data) {
|
||||
ElMessage.warning('未获取到导出文件');
|
||||
return;
|
||||
}
|
||||
const byteCharacters = atob(data);
|
||||
const byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
const blob = new Blob([byteArray], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
downloadByBlob(blob, '证书导出.xlsx');
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error) {
|
||||
console.error('导出证书失败:', error);
|
||||
ElMessage.error('导出证书失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<div class="system-menu-container layout-padding">
|
||||
<ProTable ref="tableRef" :request-api="usereventrecordListGet" :columns="columns" :pagination="true"
|
||||
:tool-button="false" :initParam="initParam" row-key="registrationId">
|
||||
<template #index="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
|
||||
:tool-button="false" :init-param="initParam" row-key="registrationId">
|
||||
<template #status="scope">
|
||||
<el-tag :type="tagTypeForStatus(scope.row.status)">{{ tagTextForStatus(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
@ -21,6 +17,38 @@
|
||||
<template #hasVoucher="scope">
|
||||
<el-tag :type="scope.row.hasVoucher ? 'success' : 'info'">{{ scope.row.hasVoucher ? '已上传' : '未上传' }}</el-tag>
|
||||
</template>
|
||||
<template #coverImage="scope">
|
||||
<el-image v-if="scope.row.coverImage" :src="scope.row.coverImage" style="height: 60px; width: 80px" fit="cover" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #eventStatus="scope">
|
||||
<el-tag :type="scope.row.eventStatus === 0 ? 'success' : scope.row.eventStatus === 1 ? 'warning' : 'danger'">
|
||||
{{ scope.row.eventStatus === 0 ? '报名中' : scope.row.eventStatus === 1 ? '比赛中' : '已结束' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #registrationStartTime="scope">
|
||||
<div v-if="scope.row.registrationStartTime && scope.row.registrationEndTime">
|
||||
<div>开始:{{ scope.row.registrationStartTime }}</div>
|
||||
<div>截止:{{ scope.row.registrationEndTime }}</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #materialDeadline="scope">
|
||||
<span>{{ scope.row.materialDeadline ?? '-' }}</span>
|
||||
</template>
|
||||
<template #matchStartTime="scope">
|
||||
<div v-if="scope.row.matchStartTime && scope.row.matchEndTime">
|
||||
<div>开始:{{ scope.row.matchStartTime }}</div>
|
||||
<div>截止:{{ scope.row.matchEndTime }}</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #eventType="scope">
|
||||
<span>{{ getEventTypeText(scope.row.eventType) }}</span>
|
||||
</template>
|
||||
<template #chargeMode="scope">
|
||||
<span>{{ getChargeModeText(scope.row.chargeMode) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="scope">
|
||||
<el-space wrap>
|
||||
@ -345,32 +373,42 @@ const tableRef = ref();
|
||||
const initParam = ref({ refresh: true });
|
||||
|
||||
const columns = ref([
|
||||
{ prop: 'index', label: '序号', align: 'center' },
|
||||
{ prop: 'eventName', label: '赛事名称', search: { el: 'input', key: 'Keyword' } },
|
||||
{ prop: 'teamName', label: '参赛队伍' },
|
||||
{ type: 'index', label: '序号', align: 'center', width: 70, fixed: 'left' },
|
||||
{
|
||||
prop: 'status',
|
||||
label: '报名状态',
|
||||
search: {
|
||||
el: 'select',
|
||||
key: 'Status',
|
||||
},
|
||||
prop: 'eventName',
|
||||
label: '赛事名称',
|
||||
search: { el: 'input', key: 'Keyword' },
|
||||
fixed: 'left',
|
||||
minWidth: 170,
|
||||
},
|
||||
{ prop: 'coverImage', label: '封面', align: 'center', width: 110 },
|
||||
{
|
||||
prop: 'eventType',
|
||||
label: '赛事类型',
|
||||
align: 'center',
|
||||
width: 120,
|
||||
enum: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '待支付', value: 0 },
|
||||
{ label: '已支付', value: 1 },
|
||||
{ label: '已取消', value: 2 },
|
||||
{ label: '已退款', value: 3 },
|
||||
{ label: '已完成', value: 4 },
|
||||
{ label: '线上', value: 0 },
|
||||
{ label: '线下', value: 1 },
|
||||
],
|
||||
},
|
||||
{ prop: 'payStatus', label: '支付状态' },
|
||||
{ prop: 'certStatus', label: '证书状态' },
|
||||
{ prop: 'hasMaterial', label: '素材', align: 'center' },
|
||||
{ prop: 'hasVoucher', label: '凭证', align: 'center' },
|
||||
{ prop: 'memberCount', label: '参赛人数', align: 'center' },
|
||||
{ prop: 'expireTime', label: '截止时间', align: 'center' },
|
||||
{ prop: 'action', label: '操作', align: 'center', fixed: 'right', width: 150 },
|
||||
{
|
||||
prop: 'chargeMode',
|
||||
label: '收费模式',
|
||||
align: 'center',
|
||||
width: 120,
|
||||
enum: [
|
||||
{ label: '免费', value: 0 },
|
||||
{ label: '定价(赛事)', value: 1 },
|
||||
{ label: '单价(赛事)', value: 2 },
|
||||
{ label: '单价(项目)', value: 3 },
|
||||
],
|
||||
},
|
||||
{ prop: 'eventStatus', label: '赛事状态', align: 'center', width: 120 },
|
||||
{ prop: 'registrationStartTime', label: '报名起止时间', align: 'center', minWidth: 320 },
|
||||
{ prop: 'materialDeadline', label: '上传素材截止时间', align: 'center', minWidth: 180 },
|
||||
{ prop: 'matchStartTime', label: '比赛起止时间', align: 'center', minWidth: 320 },
|
||||
{ prop: 'action', label: '操作', align: 'center', fixed: 'right', width: 210 },
|
||||
]);
|
||||
|
||||
const isShowDialog = ref(false);
|
||||
@ -411,6 +449,21 @@ const tagTextForPayStatus = (v) => payTagMap[normalizeToNumber(v)]?.text ?? (v ?
|
||||
const tagTypeForCertStatus = (v) => certTagMap[normalizeToNumber(v)]?.type ?? 'info';
|
||||
const tagTextForCertStatus = (v) => certTagMap[normalizeToNumber(v)]?.text ?? (v ?? '-');
|
||||
|
||||
const eventTypeMap = {
|
||||
0: '线上',
|
||||
1: '线下',
|
||||
};
|
||||
|
||||
const chargeModeMap = {
|
||||
0: '免费',
|
||||
1: '定价(赛事)',
|
||||
2: '单价(赛事)',
|
||||
3: '单价(项目)',
|
||||
};
|
||||
|
||||
const getEventTypeText = (v) => eventTypeMap[normalizeToNumber(v)] ?? (v ?? '-');
|
||||
const getChargeModeText = (v) => chargeModeMap[normalizeToNumber(v)] ?? (v ?? '-');
|
||||
|
||||
const disableCancel = computed(() => {
|
||||
if (!currentRow.value) return true;
|
||||
const status = normalizeToNumber(currentRow.value.status);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<ProTable ref="tableRef" :request-api="usereventregistrationListGet" :columns="columns" :pagination="true"
|
||||
:tool-button="false" :init-param="initParam" row-key="id">
|
||||
<template #coverImage="scope">
|
||||
<el-image v-if="scope.row?.coverImage" style="width: 70px; height: 46px;"
|
||||
<el-image v-if="scope.row?.coverImage" style="height: 60px; width: 80px"
|
||||
:src="scope.row.coverImage" fit="cover" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
@ -21,39 +21,25 @@
|
||||
</template>
|
||||
|
||||
<template #status="scope">
|
||||
<el-tag :type="eventStatusTagType(scope.row.status)">{{ eventStatusText(scope.row.status) }}</el-tag>
|
||||
<el-tag :type="scope.row.status === 0 || scope.row.status === 'Enrolling' || scope.row.status === 'enrolling' ? 'success' : scope.row.status === 1 || scope.row.status === 'Ongoing' || scope.row.status === 'ongoing' ? 'warning' : 'danger'">
|
||||
{{ scope.row.status === 0 || scope.row.status === 'Enrolling' || scope.row.status === 'enrolling' ? '报名中' : scope.row.status === 1 || scope.row.status === 'Ongoing' || scope.row.status === 'ongoing' ? '比赛中' : '已结束' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<template #registrationStartTime="scope">
|
||||
<div class="time-range-display">
|
||||
<div class="time-item">
|
||||
<span class="time-label">开始</span>
|
||||
<span class="time-value">{{ formatDateTime(scope.row.registrationStartTime) }}</span>
|
||||
</div>
|
||||
<div class="time-separator">
|
||||
<el-icon><ArrowRight /></el-icon>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<span class="time-label">结束</span>
|
||||
<span class="time-value">{{ formatDateTime(scope.row.registrationEndTime) }}</span>
|
||||
</div>
|
||||
<div v-if="scope.row.registrationStartTime && scope.row.registrationEndTime">
|
||||
<div>开始:{{ formatDateTime(scope.row.registrationStartTime) }}</div>
|
||||
<div>截止:{{ formatDateTime(scope.row.registrationEndTime) }}</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
|
||||
<template #matchStartTime="scope">
|
||||
<div class="time-range-display">
|
||||
<div class="time-item">
|
||||
<span class="time-label">开始</span>
|
||||
<span class="time-value">{{ formatDateTime(scope.row.matchStartTime) }}</span>
|
||||
</div>
|
||||
<div class="time-separator">
|
||||
<el-icon><ArrowRight /></el-icon>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<span class="time-label">结束</span>
|
||||
<span class="time-value">{{ formatDateTime(scope.row.matchEndTime) }}</span>
|
||||
</div>
|
||||
<div v-if="scope.row.matchStartTime && scope.row.matchEndTime">
|
||||
<div>开始:{{ formatDateTime(scope.row.matchStartTime) }}</div>
|
||||
<div>截止:{{ formatDateTime(scope.row.matchEndTime) }}</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
|
||||
<template #action="scope">
|
||||
@ -283,7 +269,7 @@
|
||||
<!-- 队伍数量限制提示 -->
|
||||
<el-alert
|
||||
v-if="showTeamLimitHint"
|
||||
title="若管理后台配置一个组别只能限报2队,添加2队后,"添加"按钮隐藏"
|
||||
title="若管理后台配置一个组别只能限报2队,添加2队后,'添加'按钮隐藏"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 12px;"
|
||||
@ -499,6 +485,12 @@ const formatDateTime = (v) => {
|
||||
}
|
||||
};
|
||||
|
||||
const formatTimeRange = (start, end) => {
|
||||
if (!start && !end) return '-';
|
||||
if (start && end) return `${formatDateTime(start)} - ${formatDateTime(end)}`;
|
||||
return formatDateTime(start || end);
|
||||
};
|
||||
|
||||
const formatRange = (start, end) => {
|
||||
if (!start && !end) return '-';
|
||||
if (start && end) return `${formatDateTime(start)} 至 ${formatDateTime(end)}`;
|
||||
@ -518,14 +510,14 @@ const registrationTypeText = (row) => {
|
||||
if (!v) return '-';
|
||||
switch (v) {
|
||||
case 'OnlyIndividual':
|
||||
case 1:
|
||||
return '仅个人报名';
|
||||
case 0:
|
||||
return '只允许个人报名';
|
||||
case 'OnlyTeam':
|
||||
case 2:
|
||||
return '仅团队报名';
|
||||
case 1:
|
||||
return '只允许团队报名';
|
||||
case 'Both':
|
||||
case 3:
|
||||
return '个人&团队';
|
||||
case 2:
|
||||
return '两者都有';
|
||||
default:
|
||||
return v ?? '-';
|
||||
}
|
||||
@ -534,10 +526,24 @@ const registrationTypeText = (row) => {
|
||||
const chargeModeText = (row) => {
|
||||
const v = row?.chargeMode ?? row?.feeConfig?.chargeMode ?? row?.feeMode;
|
||||
if (!v) return '-';
|
||||
// 后端可能返回 Free/Paid 等枚举,这里做一个常见值映射
|
||||
if (v === 'Free' || v === 0 || v === 'free') return '免费';
|
||||
if (v === 'Paid' || v === 1 || v === 'paid') return '收费';
|
||||
return v ?? '-';
|
||||
// 后端枚举值映射,确保与后端完全匹配
|
||||
switch (v) {
|
||||
case 'Free':
|
||||
case 0:
|
||||
case 'free':
|
||||
return '免费';
|
||||
case 'FixedPrice':
|
||||
case 1:
|
||||
return '定价(赛事)';
|
||||
case 'UnitPriceEvent':
|
||||
case 2:
|
||||
return '单价(赛事)';
|
||||
case 'UnitPriceProject':
|
||||
case 3:
|
||||
return '单价(项目)';
|
||||
default:
|
||||
return v ?? '-';
|
||||
}
|
||||
};
|
||||
|
||||
const eventStatusText = (status) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user