赛事页面调整
This commit is contained in:
parent
d0a527dd9e
commit
ed6cb2b19e
@ -6,14 +6,16 @@ import { http } from '../../utils/request';
|
|||||||
/**
|
/**
|
||||||
* 分页查询参赛记录
|
* 分页查询参赛记录
|
||||||
* @param {Object} params - 查询参数
|
* @param {Object} params - 查询参数
|
||||||
* @param {number} [params.AppUserId] - 普通用户ID
|
* @param {number} [params.AppUserId] - 普通用户ID(AppUser表的Id)
|
||||||
* @param {number} [params.MerchantId] - 商户ID
|
* @param {number} [params.SysUserId] - 系统用户ID(SysUser表的Id,用于单位用户)
|
||||||
|
* @param {number} [params.MerchantId] - 商户ID(已弃用,使用SysUserId)
|
||||||
* @param {string} [params.EventName] - 赛事名称 (模糊查询)
|
* @param {string} [params.EventName] - 赛事名称 (模糊查询)
|
||||||
* @param {number} [params.RegistrationStatus] - 报名状态 (0:待支付, 1:已支付, 2:已取消, 3:已退款, 4:已完成)
|
* @param {number} [params.PaymentStatus] - 支付状态 (0:未支付/待审核, 1:已支付/审核通过, 2:支付失败/审核拒绝, 3:已退款, 4:已取消)
|
||||||
* @param {string} [params.StartTime] - 开始时间
|
* @param {string} [params.StartTime] - 开始时间
|
||||||
* @param {string} [params.EndTime] - 结束时间
|
* @param {string} [params.EndTime] - 结束时间
|
||||||
* @param {number} [params.PageNo] - 当前页码
|
* @param {number} [params.PageNo] - 当前页码
|
||||||
* @param {number} [params.PageSize] - 每页条数
|
* @param {number} [params.PageSize] - 每页条数
|
||||||
|
* @param {number} [params.refresh] - 刷新标记(用于强制刷新数据)
|
||||||
* @returns {Promise<{
|
* @returns {Promise<{
|
||||||
* pageNo: number,
|
* pageNo: number,
|
||||||
* pageSize: number,
|
* pageSize: number,
|
||||||
|
|||||||
@ -69,24 +69,44 @@
|
|||||||
</ProTable>
|
</ProTable>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="赛事编排" name="third">
|
<el-tab-pane label="赛事编排" name="third">
|
||||||
<el-descriptions :title="item.projectName" v-for="(item, index) in eventlist" :key="index">
|
<div class="arrangement-container">
|
||||||
<el-descriptions-item label="排序:">
|
<div class="sort-hint-top">
|
||||||
<el-input-number v-model="item.sortIndex" :min="1" :step="1" />
|
<el-icon><Rank /></el-icon>
|
||||||
</el-descriptions-item>
|
<span>拖动项目卡片可调整顺序,按最终顺序导出</span>
|
||||||
<el-descriptions-item label="设置场地数量:">
|
</div>
|
||||||
<el-input-number v-model="item.venueCount" :min="0" :step="1" />
|
<div ref="arrangementContainerRef" class="arrangement-cards-wrapper">
|
||||||
</el-descriptions-item>
|
<div class="arrangement-card" v-for="(item, index) in eventlist" :key="index">
|
||||||
<el-descriptions-item label="类型:">{{ item.projectType }}</el-descriptions-item>
|
<div class="card-left">
|
||||||
<el-descriptions-item label="时长:">{{ item.totalDurationMinutes }}:{{ item.durationSeconds
|
<div class="project-name">{{ item.projectName }}</div>
|
||||||
}}</el-descriptions-item>
|
<div class="project-type">类型: {{ item.projectType }}</div>
|
||||||
<el-descriptions-item :label="`组别${idx + 1}:`" v-for="(group, idx) in item.groups">
|
<div class="project-duration">时长: {{ item.totalDurationMinutes }}分{{ item.durationSeconds }}秒</div>
|
||||||
<el-tag>{{ group.groupName }}</el-tag>
|
</div>
|
||||||
<span style="margin: 0 70px;">参与人数: {{ group.participantCount }}</span>
|
|
||||||
</el-descriptions-item>
|
<div class="card-middle">
|
||||||
</el-descriptions>
|
<div class="group-tag" v-for="(group, idx) in item.groups" :key="idx">
|
||||||
<div style="text-align: right; margin-top: 20px;">
|
<span class="group-name">{{ group.groupName }}</span>
|
||||||
<el-button type="primary" @click="onSave"> 保存修改 </el-button>
|
<span class="group-count">{{ group.participantCount }}{{ getParticipantUnit(item.projectType) }}</span>
|
||||||
<el-button type="primary" @click="getArr"> 导出编排 </el-button>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-right">
|
||||||
|
<div class="venue-box">
|
||||||
|
<div class="box-label">设置场地数量</div>
|
||||||
|
<el-input-number v-model="item.venueCount" :min="0" :step="1" size="large" />
|
||||||
|
</div>
|
||||||
|
<div class="duration-box">
|
||||||
|
<div class="box-label">项目所需时长</div>
|
||||||
|
<div class="duration-value">{{ item.totalDurationMinutes }}'</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-bar">
|
||||||
|
<el-button type="default" @click="previewArrangement">预览</el-button>
|
||||||
|
<el-button type="primary" @click="onSave">保存修改</el-button>
|
||||||
|
<el-button type="primary" @click="getArr">导出</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="成绩与证书" name="fourth">
|
<el-tab-pane label="成绩与证书" name="fourth">
|
||||||
@ -153,15 +173,66 @@
|
|||||||
@change="onScoreFileChange"
|
@change="onScoreFileChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 赛事编排预览对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="previewDialogVisible"
|
||||||
|
title="赛事编排预览"
|
||||||
|
width="90%"
|
||||||
|
style="min-height: 70vh;"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div class="preview-container">
|
||||||
|
<div class="preview-title">{{ previewEventName }}</div>
|
||||||
|
<div class="preview-table-wrapper">
|
||||||
|
<table class="preview-table" border="1" cellpadding="6" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr class="header-row">
|
||||||
|
<th class="time-col">时间</th>
|
||||||
|
<th class="project-col">参赛项目</th>
|
||||||
|
<th class="group-col">参赛组别</th>
|
||||||
|
<th class="team-col">参赛队伍</th>
|
||||||
|
<th class="athlete-col">运动员姓名</th>
|
||||||
|
<th class="session-col">场次</th>
|
||||||
|
<th class="venue-col">场地</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="(item, index) in previewData" :key="index">
|
||||||
|
<tr :class="index % 2 === 0 ? 'data-row-even' : 'data-row-odd'">
|
||||||
|
<td
|
||||||
|
v-if="!isTimeMerged(index)"
|
||||||
|
:rowspan="getTimeRowspan(index)"
|
||||||
|
class="time-cell"
|
||||||
|
>
|
||||||
|
{{ formatTime(item.timeRange) }}
|
||||||
|
</td>
|
||||||
|
<td class="project-cell">{{ item.projectName }}</td>
|
||||||
|
<td class="group-cell">{{ item.groupName }}</td>
|
||||||
|
<td class="team-cell">{{ item.teamName || '-' }}</td>
|
||||||
|
<td class="athlete-cell">{{ item.athleteName }}</td>
|
||||||
|
<td class="session-cell">{{ item.sessionIndex }}</td>
|
||||||
|
<td class="venue-cell">{{ item.venueIndex }}</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="previewDialogVisible = false">关闭</el-button>
|
||||||
|
<el-button type="primary" @click="getArr">导出Excel</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, markRaw } from 'vue';
|
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { ArrowLeft, ArrowRight, Check, CircleCheckFilled, Refresh } from '@element-plus/icons-vue';
|
import { Check, View, Download, Rank } from '@element-plus/icons-vue';
|
||||||
import { eventPost, eventPut, eventDetailGet } from '/@/api/match/event';
|
import { eventDetailGet } from '/@/api/match/event';
|
||||||
import { eventarrangementListGet, eventarrangementPut, eventarrangementExportPost } from '/@/api/match/eventarrangement';
|
import { eventarrangementListGet, eventarrangementPut, eventarrangementExportPost, eventarrangementPreviewPost } from '/@/api/match/eventarrangement';
|
||||||
import { eventregistrationPagePost, eventregistrationBatchauditPost, eventregistrationMaterialurlsPost } from '/@/api/match/eventregistration';
|
import { eventregistrationPagePost, eventregistrationBatchauditPost, eventregistrationMaterialurlsPost } from '/@/api/match/eventregistration';
|
||||||
import { eventscorePageathletePost, eventscorePagecoachleaderPost,eventscoreImportscorePost, eventscoreDownloadtemplateGet, eventscoreExportcertificatePost } from '/@/api/match/eventscore';
|
import { eventscorePageathletePost, eventscorePagecoachleaderPost,eventscoreImportscorePost, eventscoreDownloadtemplateGet, eventscoreExportcertificatePost } from '/@/api/match/eventscore';
|
||||||
import Step1BasicInfo from './Steps/Step1BasicInfo.vue';
|
import Step1BasicInfo from './Steps/Step1BasicInfo.vue';
|
||||||
@ -170,12 +241,20 @@ import Step3ProjectConfig from './Steps/Step3ProjectConfig.vue';
|
|||||||
import Step4FeeConfig from './Steps/Step4FeeConfig.vue';
|
import Step4FeeConfig from './Steps/Step4FeeConfig.vue';
|
||||||
import ProTable from '/@/components/ProTable/index.vue';
|
import ProTable from '/@/components/ProTable/index.vue';
|
||||||
import PreviewImage from '/@/components/PreviewImage/index.vue';
|
import PreviewImage from '/@/components/PreviewImage/index.vue';
|
||||||
|
import Sortable from 'sortablejs';
|
||||||
|
|
||||||
|
|
||||||
const isShowDialog = ref(false);
|
const isShowDialog = ref(false);
|
||||||
const activeName = ref('first')
|
const activeName = ref('first')
|
||||||
const eventlist = ref([]);
|
const eventlist = ref([]);
|
||||||
const importingScore = ref(false);
|
const importingScore = ref(false);
|
||||||
|
const arrangementContainerRef = ref(null);
|
||||||
|
let sortableInstance = null;
|
||||||
|
|
||||||
|
// 预览相关
|
||||||
|
const previewDialogVisible = ref(false);
|
||||||
|
const previewData = ref([]);
|
||||||
|
const previewEventName = ref('赛事编排预览');
|
||||||
const scoreFileInputRef = ref(null);
|
const scoreFileInputRef = ref(null);
|
||||||
const tableuu = ref();
|
const tableuu = ref();
|
||||||
const tableCoach = ref();
|
const tableCoach = ref();
|
||||||
@ -230,9 +309,11 @@ const formData = ref({
|
|||||||
const openDialog = async (id) => {
|
const openDialog = async (id) => {
|
||||||
isShowDialog.value = true;
|
isShowDialog.value = true;
|
||||||
const res = await eventDetailGet(id);
|
const res = await eventDetailGet(id);
|
||||||
|
initParam.value.eventId = id;
|
||||||
initParam.value.id = id;
|
initParam.value.id = id;
|
||||||
const d = res?.data;
|
const d = res?.data;
|
||||||
if (d) {
|
if (d) {
|
||||||
|
previewEventName.value = d.eventName || '赛事编排预览';
|
||||||
formData.value.basicInfo = {
|
formData.value.basicInfo = {
|
||||||
...formData.value.basicInfo,
|
...formData.value.basicInfo,
|
||||||
id: d.id,
|
id: d.id,
|
||||||
@ -263,22 +344,48 @@ const openDialog = async (id) => {
|
|||||||
|
|
||||||
const res2 = await eventarrangementListGet(id);
|
const res2 = await eventarrangementListGet(id);
|
||||||
eventlist.value = res2.data
|
eventlist.value = res2.data
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
initSortable();
|
||||||
|
};
|
||||||
|
|
||||||
|
const initSortable = () => {
|
||||||
|
if (!arrangementContainerRef.value) return;
|
||||||
|
|
||||||
|
if (sortableInstance) {
|
||||||
|
sortableInstance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
sortableInstance = new Sortable(arrangementContainerRef.value, {
|
||||||
|
animation: 150,
|
||||||
|
ghostClass: 'sort-ghost',
|
||||||
|
dragClass: 'sort-drag',
|
||||||
|
handle: '.arrangement-card',
|
||||||
|
onEnd: (evt) => {
|
||||||
|
const { oldIndex, newIndex } = evt;
|
||||||
|
if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) return;
|
||||||
|
|
||||||
|
const item = eventlist.value.splice(oldIndex, 1)[0];
|
||||||
|
eventlist.value.splice(newIndex, 0, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const initParam = ref({
|
const initParam = ref({
|
||||||
refresh: false,
|
refresh: false,
|
||||||
id: '',
|
eventId: 0,
|
||||||
|
id: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const initParamCoach = computed(() => ({
|
const initParamCoach = computed(() => ({
|
||||||
refresh: initParam.value.refresh,
|
refresh: initParam.value.refresh,
|
||||||
id: initParam.value.id,
|
eventId: initParam.value.eventId,
|
||||||
personType: 'Coach'
|
personType: 'Coach'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const initParamLeader = computed(() => ({
|
const initParamLeader = computed(() => ({
|
||||||
refresh: initParam.value.refresh,
|
refresh: initParam.value.refresh,
|
||||||
id: initParam.value.id,
|
eventId: initParam.value.eventId,
|
||||||
personType: 'Leader'
|
personType: 'Leader'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -378,6 +485,52 @@ const selection = (e) => {
|
|||||||
selectListIds.value.push(element.id);
|
selectListIds.value.push(element.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//预览赛事编排
|
||||||
|
const previewArrangement = async () => {
|
||||||
|
try {
|
||||||
|
const res = await eventarrangementPreviewPost(initParam.value.id);
|
||||||
|
if (res.succeeded) {
|
||||||
|
previewData.value = res.data || [];
|
||||||
|
previewDialogVisible.value = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('预览编排失败:', error);
|
||||||
|
ElMessage.error('预览编排失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间格式化
|
||||||
|
const formatTime = (timeStr) => {
|
||||||
|
if (!timeStr) return '';
|
||||||
|
// 将 "30''" 格式转换为 "30''"
|
||||||
|
return timeStr.replace("''", "''");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否需要合并时间列
|
||||||
|
const isTimeMerged = (index) => {
|
||||||
|
if (index === 0) return false;
|
||||||
|
const current = previewData.value[index];
|
||||||
|
const prev = previewData.value[index - 1];
|
||||||
|
return current?.projectName === prev?.projectName && current?.timeRange === prev?.timeRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取时间列的合并行数
|
||||||
|
const getTimeRowspan = (index) => {
|
||||||
|
const current = previewData.value[index];
|
||||||
|
if (!current) return 1;
|
||||||
|
|
||||||
|
let count = 1;
|
||||||
|
for (let i = index + 1; i < previewData.value.length; i++) {
|
||||||
|
const next = previewData.value[i];
|
||||||
|
if (next?.projectName === current.projectName && next?.timeRange === current.timeRange) {
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
//更新赛事编排
|
//更新赛事编排
|
||||||
const onSave = async () => {
|
const onSave = async () => {
|
||||||
const res = await eventarrangementPut({
|
const res = await eventarrangementPut({
|
||||||
@ -577,7 +730,7 @@ const downloadTemplate = async () => {
|
|||||||
const exportCertificate = async () => {
|
const exportCertificate = async () => {
|
||||||
try {
|
try {
|
||||||
const { succeeded, data, errors } = await eventscoreExportcertificatePost({
|
const { succeeded, data, errors } = await eventscoreExportcertificatePost({
|
||||||
eventId: initParam.value.id
|
eventId: initParam.value.eventId
|
||||||
});
|
});
|
||||||
if (!succeeded) {
|
if (!succeeded) {
|
||||||
ElMessage.error(errors || '导出证书失败');
|
ElMessage.error(errors || '导出证书失败');
|
||||||
@ -604,8 +757,291 @@ const exportCertificate = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取参与人数的单位显示
|
||||||
|
const getParticipantUnit = (projectType) => {
|
||||||
|
return projectType === '团队' ? '队' : '人';
|
||||||
|
};
|
||||||
|
|
||||||
// 暴露变量
|
// 暴露变量
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openDialog,
|
openDialog,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.arrangement-container {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-hint-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #ecf5ff;
|
||||||
|
border: 1px solid #b3d8ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-hint-top .el-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrangement-cards-wrapper {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrangement-card {
|
||||||
|
display: flex;
|
||||||
|
border: 2px solid #409eff;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #ffffff;
|
||||||
|
cursor: move;
|
||||||
|
transition: box-shadow 0.2s, transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrangement-card:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-ghost {
|
||||||
|
opacity: 0.4;
|
||||||
|
background: #c6e2ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-drag {
|
||||||
|
opacity: 0.9;
|
||||||
|
box-shadow: 0 8px 20px rgba(64, 158, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left {
|
||||||
|
flex: 0 0 180px;
|
||||||
|
background: #409eff;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left .project-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left .project-type,
|
||||||
|
.card-left .project-duration {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-middle {
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-content: flex-start;
|
||||||
|
border-right: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border: 1px solid #409eff;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tag .group-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-tag .group-count {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-right {
|
||||||
|
flex: 0 0 280px;
|
||||||
|
display: flex;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box,
|
||||||
|
.duration-box {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box {
|
||||||
|
border-right: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box .el-input-number {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box :deep(.el-input__wrapper) {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box :deep(.el-input__inner) {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar .el-button {
|
||||||
|
min-width: 100px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.arrangement-card {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-middle {
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-right {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-box {
|
||||||
|
border-right: 2px solid #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 赛事编排预览样式 */
|
||||||
|
.preview-container {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: #ffd9b3;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 2px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table .header-row {
|
||||||
|
background: #ccffcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table th {
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table td {
|
||||||
|
padding: 10px 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-row-even {
|
||||||
|
background: #f0fff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-row-odd {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-col, .time-cell {
|
||||||
|
width: 10%;
|
||||||
|
background: #e8f4f8;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-col, .project-cell {
|
||||||
|
width: 16%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-col, .group-cell {
|
||||||
|
width: 13%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-col, .team-cell {
|
||||||
|
width: 22%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.athlete-col, .athlete-cell {
|
||||||
|
width: 14%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-col, .session-cell {
|
||||||
|
width: 7%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.venue-col, .venue-cell {
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@ -37,8 +37,8 @@
|
|||||||
</ProTable>
|
</ProTable>
|
||||||
|
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
<el-dialog title="参赛记录" v-model="isShowDialog" width="80%">
|
<el-dialog title="参赛记录" v-model="isShowDialog" width="80%" @close="onDialogClose">
|
||||||
<ProTable ref="table" :request-api="eventrecordPageGet" :columns="columnsUser" :pagination="true"
|
<ProTable v-if="isShowDialog" ref="table" :request-api="eventrecordPageGet" :columns="columnsUser" :pagination="true"
|
||||||
:tool-button="false" :initParams="initParamUser">
|
:tool-button="false" :initParams="initParamUser">
|
||||||
<template #index="scope">
|
<template #index="scope">
|
||||||
{{ scope.$index + 1 }}
|
{{ scope.$index + 1 }}
|
||||||
@ -89,7 +89,7 @@ const initParam = ref({
|
|||||||
});
|
});
|
||||||
const selectedListIds = ref([]);
|
const selectedListIds = ref([]);
|
||||||
const initParamUser = ref({
|
const initParamUser = ref({
|
||||||
appUserId: '',
|
appUserId: null,
|
||||||
});
|
});
|
||||||
const statusList = ['info', 'warning', 'success', 'danger'];
|
const statusList = ['info', 'warning', 'success', 'danger'];
|
||||||
|
|
||||||
@ -214,11 +214,28 @@ const columnsUser = ref([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const isShowDialog = ref(false);
|
const isShowDialog = ref(false);
|
||||||
|
const table = ref();
|
||||||
|
|
||||||
const onOpenMenu = (row) => {
|
const onOpenMenu = (row) => {
|
||||||
console.log(row);
|
console.log(row);
|
||||||
|
|
||||||
|
// 参数验证
|
||||||
|
if (!row || !row.id) {
|
||||||
|
ElMessage.warning('用户信息无效,无法查看参赛记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof row.id !== 'number' || row.id <= 0) {
|
||||||
|
ElMessage.warning('用户ID无效,请刷新页面后重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户ID参数
|
||||||
initParamUser.value.appUserId = row.id;
|
initParamUser.value.appUserId = row.id;
|
||||||
isShowDialog.value = true;
|
isShowDialog.value = true;
|
||||||
|
|
||||||
|
// 下次打开对话框时强制刷新数据
|
||||||
|
initParamUser.value.refresh = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBatchAudit = async () => {
|
const onBatchAudit = async () => {
|
||||||
@ -250,6 +267,15 @@ const lookBook = async (row) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 对话框关闭时清理
|
||||||
|
const onDialogClose = () => {
|
||||||
|
// 清理参数,避免下次查询时使用旧参数
|
||||||
|
initParamUser.value = {
|
||||||
|
appUserId: null,
|
||||||
|
refresh: Date.now()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 取消
|
// 取消
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
isShowDialog.value = false;
|
isShowDialog.value = false;
|
||||||
|
|||||||
@ -33,8 +33,8 @@
|
|||||||
</ProTable>
|
</ProTable>
|
||||||
|
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
<el-dialog title="参赛记录" v-model="isShowDialog" width="80%">
|
<el-dialog title="参赛记录" v-model="isShowDialog" width="80%" @close="onDialogClose">
|
||||||
<ProTable ref="table" :request-api="eventrecordPageGet" :columns="columnsUser" :pagination="true"
|
<ProTable v-if="isShowDialog" ref="table" :request-api="eventrecordPageGet" :columns="columnsUser" :pagination="true"
|
||||||
:tool-button="false" :initParams="initParamUser">
|
:tool-button="false" :initParams="initParamUser">
|
||||||
<template #index="scope">
|
<template #index="scope">
|
||||||
{{ scope.$index + 1 }}
|
{{ scope.$index + 1 }}
|
||||||
@ -105,7 +105,7 @@ const initParam = ref({
|
|||||||
});
|
});
|
||||||
const selectedListIds = ref([]);
|
const selectedListIds = ref([]);
|
||||||
const initParamUser = ref({
|
const initParamUser = ref({
|
||||||
appUserId: '',
|
sysUserId: null,
|
||||||
});
|
});
|
||||||
const statusList = ['info', 'warning', 'success', 'danger'];
|
const statusList = ['info', 'warning', 'success', 'danger'];
|
||||||
|
|
||||||
@ -219,11 +219,28 @@ const columnsUser = ref([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const isShowDialog = ref(false);
|
const isShowDialog = ref(false);
|
||||||
|
const table = ref();
|
||||||
|
|
||||||
const onOpenMenu = (row) => {
|
const onOpenMenu = (row) => {
|
||||||
console.log(row);
|
console.log(row);
|
||||||
initParamUser.value.appUserId = row.userId;
|
|
||||||
|
// 参数验证
|
||||||
|
if (!row || !row.userId) {
|
||||||
|
ElMessage.warning('用户信息无效,无法查看参赛记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof row.userId !== 'number' || row.userId <= 0) {
|
||||||
|
ElMessage.warning('用户ID无效,请刷新页面后重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户ID参数 - 单位用户使用 sysUserId
|
||||||
|
initParamUser.value.sysUserId = row.userId;
|
||||||
isShowDialog.value = true;
|
isShowDialog.value = true;
|
||||||
|
|
||||||
|
// 下次打开对话框时强制刷新数据
|
||||||
|
initParamUser.value.refresh = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCancelB = () => {
|
const onCancelB = () => {
|
||||||
@ -275,6 +292,15 @@ const onSubmitAudit = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 对话框关闭时清理
|
||||||
|
const onDialogClose = () => {
|
||||||
|
// 清理参数,避免下次查询时使用旧参数
|
||||||
|
initParamUser.value = {
|
||||||
|
sysUserId: null,
|
||||||
|
refresh: Date.now()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 取消
|
// 取消
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
isShowDialog.value = false;
|
isShowDialog.value = false;
|
||||||
|
|||||||
@ -174,7 +174,63 @@
|
|||||||
</span>
|
</span>
|
||||||
<el-icon class="assign-card-close" @click.stop="toggleGroupSelection(group.id)"><Close /></el-icon>
|
<el-icon class="assign-card-close" @click.stop="toggleGroupSelection(group.id)"><Close /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="assign-card-members">
|
|
||||||
|
<!-- 团队数量信息和添加团队按钮 -->
|
||||||
|
<div class="team-info-section" v-if="isTeamProject(group) && getGroupMaxTeams(group.id) > 1">
|
||||||
|
<div class="team-count-info">
|
||||||
|
已添加 {{ getGroupTeams(group.id).length }} / {{ getGroupMaxTeams(group.id) }} 队
|
||||||
|
</div>
|
||||||
|
<el-button
|
||||||
|
v-if="getGroupTeams(group.id).length < getGroupMaxTeams(group.id)"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
@click.stop="addTeam(group.id)"
|
||||||
|
>
|
||||||
|
<el-icon><Plus /></el-icon>
|
||||||
|
添加团队
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 团队列表 -->
|
||||||
|
<div v-if="isTeamProject(group) && getGroupMaxTeams(group.id) > 1" class="team-section">
|
||||||
|
<div
|
||||||
|
v-for="team in getGroupTeams(group.id)"
|
||||||
|
:key="team.teamIndex"
|
||||||
|
class="team-card"
|
||||||
|
:class="{ 'team-card-active': activeTeamKey === `${group.id}_${team.teamIndex}` }"
|
||||||
|
@click.stop="setActiveTeam(group.id, team.teamIndex)"
|
||||||
|
>
|
||||||
|
<div class="team-card-header">
|
||||||
|
<span class="team-name">团队{{ getTeamNumber(team.teamIndex) }}</span>
|
||||||
|
<el-button
|
||||||
|
v-if="getGroupTeams(group.id).length > 1"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
@click.stop="removeTeam(group.id, team.teamIndex)"
|
||||||
|
>
|
||||||
|
<el-icon><Delete /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="team-members">
|
||||||
|
<el-tag
|
||||||
|
v-for="m in team.members"
|
||||||
|
:key="m.userId ?? m.id"
|
||||||
|
closable
|
||||||
|
:type="getMemberValidationResult(m, group).isValid ? '' : 'danger'"
|
||||||
|
@close.stop="removeMemberFromTeam(group.id, team.teamIndex, m.userId ?? m.id)"
|
||||||
|
style="margin: 3px;"
|
||||||
|
>
|
||||||
|
{{ m.name }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-if="!team.members.length" class="team-placeholder">选择此团队后添加队员</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 单团队模式(向后兼容) -->
|
||||||
|
<div v-else class="assign-card-members">
|
||||||
<el-tag
|
<el-tag
|
||||||
v-for="m in (groupMemberMap[group.id] ?? [])"
|
v-for="m in (groupMemberMap[group.id] ?? [])"
|
||||||
:key="m.userId ?? m.id"
|
:key="m.userId ?? m.id"
|
||||||
@ -187,9 +243,10 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-if="!(groupMemberMap[group.id]?.length)" class="assign-card-placeholder">点击此卡片后,从右侧选择队员</span>
|
<span v-if="!(groupMemberMap[group.id]?.length)" class="assign-card-placeholder">点击此卡片后,从右侧选择队员</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 队员验证错误提示 -->
|
<!-- 队员验证错误提示 -->
|
||||||
<div v-if="hasValidationErrorsForGroup(group)" class="validation-error-section">
|
<div v-if="hasValidationErrorsForGroup(group)" class="validation-error-section">
|
||||||
<div class="validation-error-item" v-for="m in (groupMemberMap[group.id] ?? [])" :key="m.userId ?? m.id">
|
<div class="validation-error-item" v-for="m in (isTeamProject(group) && getGroupMaxTeams(group.id) > 1 ? getGroupTeams(group.id).flatMap(t => t.members) : (groupMemberMap[group.id] ?? []))" :key="m.userId ?? m.id">
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="!getMemberValidationResult(m, group).isValid"
|
v-if="!getMemberValidationResult(m, group).isValid"
|
||||||
:title="getMemberValidationResult(m, group).message"
|
:title="getMemberValidationResult(m, group).message"
|
||||||
@ -232,7 +289,7 @@
|
|||||||
:key="m.userId ?? m.id"
|
:key="m.userId ?? m.id"
|
||||||
class="team-member-tag"
|
class="team-member-tag"
|
||||||
:class="{ 'team-member-tag-assigned': isMemberAssignedToActive(m.userId ?? m.id) }"
|
:class="{ 'team-member-tag-assigned': isMemberAssignedToActive(m.userId ?? m.id) }"
|
||||||
@click="assignMemberToActiveGroup(m)"
|
@click="assignMemberToActiveTeam(m)"
|
||||||
>
|
>
|
||||||
{{ m.name }}
|
{{ m.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -292,9 +349,37 @@
|
|||||||
<div v-else-if="activeStep === 2">
|
<div v-else-if="activeStep === 2">
|
||||||
<el-skeleton :rows="10" animated v-if="previewLoading" />
|
<el-skeleton :rows="10" animated v-if="previewLoading" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- 支付二维码显示区域 -->
|
<!-- 银行转账信息显示区域 -->
|
||||||
<div v-if="paymentData" class="payment-qrcode-section">
|
<div v-if="paymentData && paymentMethod === 3" class="bank-transfer-section">
|
||||||
<div class="qrcode-title">请使用微信扫码支付</div>
|
<div class="bank-transfer-title">银行卡转账</div>
|
||||||
|
<div class="bank-transfer-content">
|
||||||
|
<div class="info-item">
|
||||||
|
<text class="info-label">收款商户:</text>
|
||||||
|
<text class="info-value">上海跃动文化科技有限公司</text>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<text class="info-label">收款账号:</text>
|
||||||
|
<text class="info-value">456498465198456261</text>
|
||||||
|
<el-button type="primary" size="small" @click="copyAccount">复制</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-amount">
|
||||||
|
<span class="amount-label">转账金额:</span>
|
||||||
|
<span class="amount-value">¥ {{ finalPaymentAmount }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="payment-tip">
|
||||||
|
<el-alert title="提示" type="info" :closable="false">
|
||||||
|
请使用银行APP或网银进行转账,转账完成后系统会自动审核。
|
||||||
|
</el-alert>
|
||||||
|
</div>
|
||||||
|
<div class="payment-actions">
|
||||||
|
<el-button @click="cancelPayment">取消支付</el-button>
|
||||||
|
<el-button type="primary" @click="confirmBankTransfer">确认已转账</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 支付二维码显示区域(微信/支付宝) -->
|
||||||
|
<div v-else-if="paymentData" class="payment-qrcode-section">
|
||||||
|
<div class="qrcode-title">请使用{{ paymentMethod === 2 ? '支付宝' : '微信'}}扫码支付</div>
|
||||||
<div class="qrcode-wrapper">
|
<div class="qrcode-wrapper">
|
||||||
<div v-if="paymentData.qrcodeUrl" class="qrcode-container">
|
<div v-if="paymentData.qrcodeUrl" class="qrcode-container">
|
||||||
<div ref="qrcodeRef"></div>
|
<div ref="qrcodeRef"></div>
|
||||||
@ -421,7 +506,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick, onUnmounted } from 'vue';
|
import { ref, computed, nextTick, onUnmounted } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { Close, Picture, Loading, CircleCheck, ArrowRight } from '@element-plus/icons-vue';
|
import { Close, Picture, Loading, CircleCheck, ArrowRight, Plus, Delete } from '@element-plus/icons-vue';
|
||||||
import ProTable from '/@/components/ProTable/index.vue';
|
import ProTable from '/@/components/ProTable/index.vue';
|
||||||
import { formatDate } from '/@/utils/formatTime';
|
import { formatDate } from '/@/utils/formatTime';
|
||||||
import {
|
import {
|
||||||
@ -758,6 +843,7 @@ const allGroups = computed(() => {
|
|||||||
projectCompletionCount: p.completionCount ?? 0,
|
projectCompletionCount: p.completionCount ?? 0,
|
||||||
parentProjectTypeName: p.parentProjectTypeName,
|
parentProjectTypeName: p.parentProjectTypeName,
|
||||||
projectTypeName: p.projectTypeName,
|
projectTypeName: p.projectTypeName,
|
||||||
|
projectType: p.projectType,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -860,6 +946,14 @@ const activeAssignGroupId = ref(null); // 当前正在分配队员的组别ID
|
|||||||
const groupMemberMap = ref({}); // { groupId: [member, ...] }
|
const groupMemberMap = ref({}); // { groupId: [member, ...] }
|
||||||
const usedTeamId = ref(null); // 本次报名已使用的团队ID
|
const usedTeamId = ref(null); // 本次报名已使用的团队ID
|
||||||
|
|
||||||
|
// ==================== 多团队管理相关 ====================
|
||||||
|
// 每个组别的团队列表: { groupId: [{ teamIndex, members: [...] }, ...] }
|
||||||
|
const groupTeamsMap = ref({});
|
||||||
|
// 当前正在编辑的团队(组别ID + 团队索引)
|
||||||
|
const activeTeamKey = ref(null); // 格式: "groupId_teamIndex"
|
||||||
|
// 组别限报团队数量: { groupId: maxTeams }
|
||||||
|
const groupMaxTeamsMap = ref({});
|
||||||
|
|
||||||
const previewLoading = ref(false);
|
const previewLoading = ref(false);
|
||||||
const previewData = ref(null);
|
const previewData = ref(null);
|
||||||
const paymentMethod = ref(1);
|
const paymentMethod = ref(1);
|
||||||
@ -913,6 +1007,10 @@ const clearRegisterState = () => {
|
|||||||
previewData.value = null;
|
previewData.value = null;
|
||||||
paymentMethod.value = 1;
|
paymentMethod.value = 1;
|
||||||
draftRegistrationId.value = null;
|
draftRegistrationId.value = null;
|
||||||
|
// 多团队管理相关
|
||||||
|
groupTeamsMap.value = {};
|
||||||
|
activeTeamKey.value = null;
|
||||||
|
groupMaxTeamsMap.value = {};
|
||||||
// 支付相关
|
// 支付相关
|
||||||
paymentData.value = null;
|
paymentData.value = null;
|
||||||
polling.value = false;
|
polling.value = false;
|
||||||
@ -938,6 +1036,10 @@ const resetStep2AndBeyond = () => {
|
|||||||
activeAssignGroupId.value = null;
|
activeAssignGroupId.value = null;
|
||||||
groupMemberMap.value = {};
|
groupMemberMap.value = {};
|
||||||
usedTeamId.value = null;
|
usedTeamId.value = null;
|
||||||
|
// 多团队管理相关
|
||||||
|
groupTeamsMap.value = {};
|
||||||
|
activeTeamKey.value = null;
|
||||||
|
groupMaxTeamsMap.value = {};
|
||||||
previewData.value = null;
|
previewData.value = null;
|
||||||
previewLoading.value = false;
|
previewLoading.value = false;
|
||||||
draftRegistrationId.value = null;
|
draftRegistrationId.value = null;
|
||||||
@ -1178,6 +1280,235 @@ const removeMemberFromGroup = (groupId, memberId) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 多团队管理函数 ====================
|
||||||
|
|
||||||
|
// 获取阿拉伯数字表示
|
||||||
|
const getTeamNumber = (num) => {
|
||||||
|
return num.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断项目是否为团队类型
|
||||||
|
const isTeamProject = (group) => {
|
||||||
|
// 优先使用 projectType 判断
|
||||||
|
if (group?.projectType) {
|
||||||
|
// 项目类型枚举值:
|
||||||
|
// 1003: 个人速度
|
||||||
|
// 1004: 个人花样
|
||||||
|
// 1005: 团队速度
|
||||||
|
// 1006: 团队花样
|
||||||
|
return group.projectType === 1005 || group.projectType === 1006;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备用判断:使用 projectTypeName 或 parentProjectTypeName
|
||||||
|
if (group?.projectTypeName || group?.parentProjectTypeName) {
|
||||||
|
const typeName = group.projectTypeName || group.parentProjectTypeName;
|
||||||
|
return typeName && typeName.includes('团队');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后的备用判断:使用 projectTeamSize
|
||||||
|
if (group?.projectTeamSize) {
|
||||||
|
return group.projectTeamSize > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化组别团队数据
|
||||||
|
const initGroupTeams = (groupId) => {
|
||||||
|
if (!groupTeamsMap.value[groupId]) {
|
||||||
|
groupTeamsMap.value[groupId] = [
|
||||||
|
{
|
||||||
|
teamIndex: 1,
|
||||||
|
members: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取某个组别的团队列表
|
||||||
|
const getGroupTeams = (groupId) => {
|
||||||
|
initGroupTeams(groupId);
|
||||||
|
return groupTeamsMap.value[groupId];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取某个组别的限报团队数量
|
||||||
|
const getGroupMaxTeams = (groupId) => {
|
||||||
|
return groupMaxTeamsMap.value[groupId] ?? 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加团队
|
||||||
|
const addTeam = (groupId) => {
|
||||||
|
initGroupTeams(groupId);
|
||||||
|
const teams = groupTeamsMap.value[groupId];
|
||||||
|
const maxTeams = getGroupMaxTeams(groupId);
|
||||||
|
|
||||||
|
if (teams.length >= maxTeams) {
|
||||||
|
ElMessage.warning(`已达到该项目组别限报${maxTeams}队的上限`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTeamIndex = teams.length + 1;
|
||||||
|
teams.push({
|
||||||
|
teamIndex: newTeamIndex,
|
||||||
|
members: []
|
||||||
|
});
|
||||||
|
|
||||||
|
ElMessage.success(`已添加团队${getTeamNumber(newTeamIndex)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除团队
|
||||||
|
const removeTeam = async (groupId, teamIndex) => {
|
||||||
|
initGroupTeams(groupId);
|
||||||
|
const teams = groupTeamsMap.value[groupId];
|
||||||
|
|
||||||
|
if (teams.length <= 1) {
|
||||||
|
ElMessage.warning('至少需要保留一个团队');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
`确定要删除团队${getTeamNumber(teamIndex)}吗?该团队的队员也会被移除。`,
|
||||||
|
'删除确认',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const teamIdx = teams.findIndex(t => t.teamIndex === teamIndex);
|
||||||
|
if (teamIdx > -1) {
|
||||||
|
teams.splice(teamIdx, 1);
|
||||||
|
|
||||||
|
// 重新编号团队索引
|
||||||
|
teams.forEach((team, idx) => {
|
||||||
|
team.teamIndex = idx + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
ElMessage.success('团队删除成功');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 用户取消
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取当前激活的团队(如果启用了多团队模式)
|
||||||
|
const getActiveTeamMembers = (groupId) => {
|
||||||
|
// 向后兼容:如果没有启用多团队,使用原来的 groupMemberMap
|
||||||
|
if (!groupTeamsMap.value[groupId] || groupTeamsMap.value[groupId].length === 0) {
|
||||||
|
return groupMemberMap.value[groupId] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多团队模式:找到当前激活的团队
|
||||||
|
if (!activeTeamKey.value) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [activeGroupId, activeTeamIdx] = activeTeamKey.value.split('_').map(Number);
|
||||||
|
if (activeGroupId !== groupId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = groupTeamsMap.value[groupId].find(t => t.teamIndex === activeTeamIdx);
|
||||||
|
return team?.members ?? [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置当前激活的团队
|
||||||
|
const setActiveTeam = (groupId, teamIndex) => {
|
||||||
|
activeTeamKey.value = `${groupId}_${teamIndex}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将成员分配到当前激活的团队
|
||||||
|
const assignMemberToActiveTeam = async (member) => {
|
||||||
|
const gId = activeAssignGroupId.value;
|
||||||
|
if (!gId) {
|
||||||
|
ElMessage.warning('请先点击左侧的组别卡片');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initGroupTeams(gId);
|
||||||
|
|
||||||
|
// 获取当前组别信息
|
||||||
|
const group = selectedGroupItems.value.find(g => g.id === gId);
|
||||||
|
// 检查是否为团队项目且启用了多团队模式
|
||||||
|
const isMultiTeamMode = group && isTeamProject(group) &&
|
||||||
|
(groupTeamsMap.value[gId].length > 1 || getGroupMaxTeams(gId) > 1);
|
||||||
|
|
||||||
|
if (!isMultiTeamMode) {
|
||||||
|
// 单团队模式,使用原来的逻辑
|
||||||
|
await assignMemberToActiveGroup(member);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多团队模式
|
||||||
|
if (!activeTeamKey.value) {
|
||||||
|
ElMessage.warning('请先选择要分配队员的团队');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [activeGroupId, activeTeamIdx] = activeTeamKey.value.split('_').map(Number);
|
||||||
|
if (activeGroupId !== gId) {
|
||||||
|
ElMessage.warning('请先选择对应组别的团队');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = groupTeamsMap.value[gId].find(t => t.teamIndex === activeTeamIdx);
|
||||||
|
if (!team) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberId = member.userId ?? member.id;
|
||||||
|
// 已存在则不重复添加
|
||||||
|
if (team.members.some((m) => (m.userId ?? m.id) === memberId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查队员是否来自已选择的团队
|
||||||
|
if (usedTeamId.value && member.teamId !== usedTeamId.value) {
|
||||||
|
ElMessage.warning('本次报名只能选择同一个团队的队员');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前组别信息进行验证(group变量已在函数开头声明)
|
||||||
|
if (group) {
|
||||||
|
const validation = await validateMemberForGroup(member, group);
|
||||||
|
if (!validation.isValid) {
|
||||||
|
ElMessage.warning(validation.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置已使用的团队
|
||||||
|
if (!usedTeamId.value) {
|
||||||
|
usedTeamId.value = member.teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
team.members.push({ ...member, teamId: member.teamId });
|
||||||
|
// 实时检查报名限制
|
||||||
|
await checkRegistrationLimit();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从团队中移除成员
|
||||||
|
const removeMemberFromTeam = (groupId, teamIndex, memberId) => {
|
||||||
|
initGroupTeams(groupId);
|
||||||
|
const team = groupTeamsMap.value[groupId].find(t => t.teamIndex === teamIndex);
|
||||||
|
if (!team) return;
|
||||||
|
|
||||||
|
team.members = team.members.filter((m) => (m.userId ?? m.id) !== memberId);
|
||||||
|
|
||||||
|
// 检查是否还有队员
|
||||||
|
let hasMembers = false;
|
||||||
|
groupTeamsMap.value[groupId].forEach(t => {
|
||||||
|
if (t.members.length > 0) {
|
||||||
|
hasMembers = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!hasMembers) {
|
||||||
|
usedTeamId.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 验证优惠券
|
// 验证优惠券
|
||||||
const validateCoupon = async () => {
|
const validateCoupon = async () => {
|
||||||
if (!couponCode.value?.trim()) {
|
if (!couponCode.value?.trim()) {
|
||||||
@ -1262,13 +1593,17 @@ const fetchGroupTeamLimit = async () => {
|
|||||||
const { succeeded, data } = await usereventregistrationGroupTeamLimitGet(currentEventId.value);
|
const { succeeded, data } = await usereventregistrationGroupTeamLimitGet(currentEventId.value);
|
||||||
if (succeeded && data) {
|
if (succeeded && data) {
|
||||||
const map = {};
|
const map = {};
|
||||||
|
const maxTeamsMap = {};
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
map[item.groupId] = {
|
map[item.groupId] = {
|
||||||
maxTeams: item.maxTeams,
|
maxTeams: item.maxTeams,
|
||||||
currentTeams: item.currentTeams
|
currentTeams: item.currentTeams
|
||||||
};
|
};
|
||||||
|
// 同时设置限报团队数量
|
||||||
|
maxTeamsMap[item.groupId] = item.maxTeams ?? 1;
|
||||||
});
|
});
|
||||||
groupTeamLimitMap.value = map;
|
groupTeamLimitMap.value = map;
|
||||||
|
groupMaxTeamsMap.value = maxTeamsMap;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
console.error('加载队伍限制配置失败');
|
console.error('加载队伍限制配置失败');
|
||||||
@ -1311,6 +1646,16 @@ const handleStep1Next = async () => {
|
|||||||
await fetchTeamList();
|
await fetchTeamList();
|
||||||
// 加载队伍限制配置
|
// 加载队伍限制配置
|
||||||
await fetchGroupTeamLimit();
|
await fetchGroupTeamLimit();
|
||||||
|
|
||||||
|
// 初始化每个组别的团队数据
|
||||||
|
await nextTick();
|
||||||
|
selectedGroupItems.value.forEach(group => {
|
||||||
|
initGroupTeams(group.id);
|
||||||
|
// 默认激活第一个团队
|
||||||
|
if (group.id === activeAssignGroupId.value) {
|
||||||
|
setActiveTeam(group.id, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStep2Next = async () => {
|
const handleStep2Next = async () => {
|
||||||
@ -1326,8 +1671,30 @@ const handleStep2Next = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查每个组别是否至少分配了一个队员
|
// 检查每个组别是否至少分配了一个队员(考虑多团队模式)
|
||||||
const emptyGroups = selectedGroupItems.value.filter((g) => !(groupMemberMap.value[g.id]?.length));
|
const emptyGroups = [];
|
||||||
|
for (const g of selectedGroupItems.value) {
|
||||||
|
const gId = g.id;
|
||||||
|
let hasMembers = false;
|
||||||
|
|
||||||
|
// 检查是否为团队项目且启用了多团队模式
|
||||||
|
if (isTeamProject(g) && groupTeamsMap.value[gId] && groupTeamsMap.value[gId].length > 0) {
|
||||||
|
for (const team of groupTeamsMap.value[gId]) {
|
||||||
|
if (team.members.length > 0) {
|
||||||
|
hasMembers = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单团队模式
|
||||||
|
hasMembers = !!(groupMemberMap.value[gId]?.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMembers) {
|
||||||
|
emptyGroups.push(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (emptyGroups.length > 0) {
|
if (emptyGroups.length > 0) {
|
||||||
ElMessage.warning(`请为所有组别分配队员,"${emptyGroups[0].groupName ?? emptyGroups[0].name}"尚未分配`);
|
ElMessage.warning(`请为所有组别分配队员,"${emptyGroups[0].groupName ?? emptyGroups[0].name}"尚未分配`);
|
||||||
return;
|
return;
|
||||||
@ -1340,14 +1707,56 @@ const handleStep2Next = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造按组别分配的数据
|
// 构造按组别分配的数据(支持多团队)
|
||||||
const groupMembers = selectedGroupIds.value.map((gId) => ({
|
const groupMembers = [];
|
||||||
groupId: gId,
|
const allTeams = []; // 用于收集所有团队数据
|
||||||
memberIds: (groupMemberMap.value[gId] ?? []).map((m) => m.userId ?? m.id),
|
const allMemberIdsSet = new Set();
|
||||||
}));
|
|
||||||
|
for (const gId of selectedGroupIds.value) {
|
||||||
// 收集所有不重复的 memberIds
|
// 找到当前组别信息
|
||||||
const allMemberIds = [...new Set(groupMembers.flatMap((g) => g.memberIds))];
|
const group = selectedGroupItems.value.find(g => g.id === gId);
|
||||||
|
// 检查是否为团队项目且启用了多团队模式
|
||||||
|
const isMultiTeamMode = group && isTeamProject(group) && groupTeamsMap.value[gId] &&
|
||||||
|
(groupTeamsMap.value[gId].length > 1 || getGroupMaxTeams(gId) > 1);
|
||||||
|
|
||||||
|
if (isMultiTeamMode) {
|
||||||
|
// 多团队模式
|
||||||
|
const teams = groupTeamsMap.value[gId];
|
||||||
|
const groupTeamsData = [];
|
||||||
|
|
||||||
|
for (const team of teams) {
|
||||||
|
const memberIds = team.members.map(m => m.userId ?? m.id);
|
||||||
|
memberIds.forEach(id => allMemberIdsSet.add(id));
|
||||||
|
|
||||||
|
groupTeamsData.push({
|
||||||
|
teamIndex: team.teamIndex,
|
||||||
|
memberIds: memberIds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMembers.push({
|
||||||
|
groupId: gId,
|
||||||
|
memberIds: groupTeamsData.flatMap(t => t.memberIds), // 保持向后兼容
|
||||||
|
teams: groupTeamsData
|
||||||
|
});
|
||||||
|
|
||||||
|
allTeams.push(...groupTeamsData.map(t => ({
|
||||||
|
groupId: gId,
|
||||||
|
...t
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
// 单团队模式(保持向后兼容)
|
||||||
|
const memberIds = (groupMemberMap.value[gId] ?? []).map((m) => m.userId ?? m.id);
|
||||||
|
memberIds.forEach(id => allMemberIdsSet.add(id));
|
||||||
|
|
||||||
|
groupMembers.push({
|
||||||
|
groupId: gId,
|
||||||
|
memberIds: memberIds
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allMemberIds = [...allMemberIdsSet];
|
||||||
|
|
||||||
submitDraftLoading.value = true;
|
submitDraftLoading.value = true;
|
||||||
try {
|
try {
|
||||||
@ -1484,6 +1893,47 @@ const refreshPayment = async () => {
|
|||||||
await handleCreateOrderAndPay();
|
await handleCreateOrderAndPay();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 复制银行账号
|
||||||
|
const copyAccount = () => {
|
||||||
|
navigator.clipboard.writeText('456498465198456261').then(() => {
|
||||||
|
ElMessage.success('账号已复制');
|
||||||
|
}).catch(() => {
|
||||||
|
// 如果clipboard API不可用,使用备用方法
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.value = '456498465198456261';
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ElMessage.success('账号已复制');
|
||||||
|
} catch (err) {
|
||||||
|
ElMessage.error('复制失败,请手动复制');
|
||||||
|
}
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认银行转账
|
||||||
|
const confirmBankTransfer = () => {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认已完成转账吗?转账完成后订单将进入待审核状态。',
|
||||||
|
'确认转账',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(async () => {
|
||||||
|
ElMessage.success('订单已提交,等待审核');
|
||||||
|
initParam.value.refresh = !initParam.value.refresh;
|
||||||
|
setTimeout(() => {
|
||||||
|
isShowRegisterDialog.value = false;
|
||||||
|
}, 1500);
|
||||||
|
}).catch(() => {
|
||||||
|
// 用户取消
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateOrderAndPay = async () => {
|
const handleCreateOrderAndPay = async () => {
|
||||||
if (!draftRegistrationId.value) return;
|
if (!draftRegistrationId.value) return;
|
||||||
createOrderLoading.value = true;
|
createOrderLoading.value = true;
|
||||||
@ -1508,7 +1958,13 @@ const handleCreateOrderAndPay = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 付费项目,显示二维码
|
// 如果是银行转账,直接显示银行信息,不需要二维码和轮询
|
||||||
|
if (paymentMethod.value === 3) {
|
||||||
|
paymentData.value = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 微信/支付宝支付,显示二维码
|
||||||
paymentData.value = data;
|
paymentData.value = data;
|
||||||
await generateQRCode(data.qrcodeUrl);
|
await generateQRCode(data.qrcodeUrl);
|
||||||
|
|
||||||
@ -1740,6 +2196,74 @@ const handleCreateOrderAndPay = async () => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 团队管理样式 ---- */
|
||||||
|
.team-info-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: #f0f7ff;
|
||||||
|
border-bottom: 1px solid #e6f4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-count-info {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-section {
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-card {
|
||||||
|
border: 1px solid #e6f4ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #56b4f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: #ecf5ff;
|
||||||
|
border-bottom: 1px solid #d9ecff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-members {
|
||||||
|
padding: 8px 10px;
|
||||||
|
min-height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-placeholder {
|
||||||
|
color: #c0c4cc;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.team-members-panel {
|
.team-members-panel {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid #d0e4f7;
|
border: 1px solid #d0e4f7;
|
||||||
@ -1870,6 +2394,55 @@ const handleCreateOrderAndPay = async () => {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 银行转账信息显示区域样式 */
|
||||||
|
.bank-transfer-section {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-content {
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-content .info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-content .info-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-content .info-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-transfer-content .info-value {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-tip {
|
||||||
|
margin: 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.free-project-hint {
|
.free-project-hint {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user