赛事管理bug

This commit is contained in:
马梓彭 2026-04-07 09:05:06 +08:00
parent 1c15442978
commit 762483d07d
5 changed files with 161 additions and 54 deletions

View File

@ -124,7 +124,7 @@ const localFormData = ref({
// init from parent // init from parent
watch( watch(
() => props.modelValue, () => props.modelValue,
(v) => { async (v) => {
console.log('init from parent', v); console.log('init from parent', v);
if (!v) return; if (!v) return;
Object.keys(localFormData.value).forEach((k) => { Object.keys(localFormData.value).forEach((k) => {
@ -137,6 +137,21 @@ watch(
} }
}); });
console.log('after init', localFormData.value); console.log('after init', localFormData.value);
// 如果有省份数据,重新加载对应的城市和区县数据
if(localFormData.value.regionIds[0]) {
try {
const cityRes = await regionCitiesbyprovinceGet({ provincecode: localFormData.value.regionIds[0] });
cities.value = cityRes?.data ?? [];
if(localFormData.value.regionIds[1]) {
const districtRes = await regionDistrictsbycityGet({ citycode: localFormData.value.regionIds[1] });
districts.value = districtRes?.data ?? [];
}
} catch {
cities.value = [];
districts.value = [];
}
}
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );

View File

@ -47,7 +47,7 @@
</div> </div>
<div class="account-item"> <div class="account-item">
<div class="account-item__label">营业执照</div> <div class="account-item__label">营业执照</div>
<img :src="businessLicenseUrl" style="width: 100px; height: 100px;" /> <el-image :src="localFormData.businessLicenseUrl" style="width: 100px; height: 100px;" fit="cover" />
</div> </div>
</div> </div>
</div> </div>
@ -149,15 +149,67 @@ const localFormData = ref({
creditCode: '', creditCode: '',
bankName: '', bankName: '',
accountNumber: '', accountNumber: '',
businessLicenseUrl: '',
}); });
const merchantOptions = ref([]);
const isInitialized = ref(false);
const syncMerchantInfo = () => {
const id = localFormData.value.merchantId;
// 如果已经有商户信息(merchantName 不为空),即使没有 merchantId 也正常显示
if (localFormData.value.merchantName) {
return;
}
// 如果没有 id,不强制清空,保留已有数据
if (!id) {
return;
}
// 尝试从 merchantOptions 中查找商户
const m = (merchantOptions.value ?? []).find((x) => x?.value == id);
if (m) {
// 只在 localFormData 中的值为空时才从 merchantOptions 中获取
if (!localFormData.value.merchantName) {
localFormData.value.merchantName = m.merchantName ?? '';
}
if (!localFormData.value.creditCode) {
localFormData.value.creditCode = m.creditCode ?? '';
}
if (!localFormData.value.bankName) {
localFormData.value.bankName = m.bankName ?? '';
}
if (!localFormData.value.accountNumber) {
localFormData.value.accountNumber = m.accountNumber ?? '';
}
// 只在营业执照 URL 为空时才从 merchantOptions 中获取
if (!localFormData.value.businessLicenseUrl) {
localFormData.value.businessLicenseUrl = m.businessLicenseUrl ?? '';
}
}
};
watch( watch(
() => props.modelValue, () => props.modelValue,
(v) => { (v) => {
if (!v) return; if (!v) return;
Object.keys(localFormData.value).forEach((k) => {
if (v[k] !== undefined) localFormData.value[k] = v[k]; // 直接复制所有属性,强制覆盖
}); localFormData.value.chargeMode = v.chargeMode ?? 0;
localFormData.value.price = v.price ?? 0;
localFormData.value.merchantId = v.merchantId;
localFormData.value.merchantName = v.merchantName ?? '';
localFormData.value.creditCode = v.creditCode ?? '';
localFormData.value.bankName = v.bankName ?? '';
localFormData.value.accountNumber = v.accountNumber ?? '';
localFormData.value.businessLicenseUrl = v.businessLicenseUrl ?? '';
// 如果已经初始化完成,调用 syncMerchantInfo
if (isInitialized.value) {
syncMerchantInfo();
}
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
@ -167,8 +219,7 @@ watch(
(v) => emit('update:modelValue', { ...props.modelValue, ...v }), (v) => emit('update:modelValue', { ...props.modelValue, ...v }),
{ deep: true } { deep: true }
); );
const businessLicenseUrl = ref('');
const merchantOptions = ref([]);
onMounted(async () => { onMounted(async () => {
try { try {
const res = await merchantPageGet({ PageNo: 1, PageSize: 200 }); const res = await merchantPageGet({ PageNo: 1, PageSize: 200 });
@ -185,27 +236,11 @@ onMounted(async () => {
} catch { } catch {
merchantOptions.value = []; merchantOptions.value = [];
} }
isInitialized.value = true;
syncMerchantInfo(); syncMerchantInfo();
}); });
const syncMerchantInfo = () => {
const id = localFormData.value.merchantId;
if (!id) {
localFormData.value.merchantName = '';
localFormData.value.creditCode = '';
localFormData.value.bankName = '';
localFormData.value.accountNumber = '';
businessLicenseUrl.value = '';
return;
}
const m = (merchantOptions.value ?? []).find((x) => x?.value == id);
localFormData.value.merchantName = m?.merchantName ?? '';
localFormData.value.creditCode = m?.creditCode ?? '';
localFormData.value.bankName = m?.bankName ?? '';
localFormData.value.accountNumber = m?.accountNumber ?? '';
businessLicenseUrl.value = m?.businessLicenseUrl ?? '';
};
// 商户选择弹窗相关 // 商户选择弹窗相关
const merchantDialogVisible = ref(false); const merchantDialogVisible = ref(false);
const merchantList = ref([]); const merchantList = ref([]);
@ -256,16 +291,47 @@ const selectMerchant = (row) => {
localFormData.value.creditCode = row.creditCode ?? ''; localFormData.value.creditCode = row.creditCode ?? '';
localFormData.value.bankName = row.bankName ?? ''; localFormData.value.bankName = row.bankName ?? '';
localFormData.value.accountNumber = row.accountNumber ?? ''; localFormData.value.accountNumber = row.accountNumber ?? '';
businessLicenseUrl.value = row.businessLicenseUrl ?? ''; localFormData.value.businessLicenseUrl = row.businessLicenseUrl ?? '';
merchantDialogVisible.value = false; merchantDialogVisible.value = false;
}; };
watch(merchantOptions, syncMerchantInfo, { deep: true }); watch(merchantOptions, () => {
if (isInitialized.value) {
syncMerchantInfo();
}
}, { deep: true });
const validatePrice = (rule, value, callback) => {
// 当收费模式为定价(赛事)或单价(赛事)时,价格必填
if (localFormData.value.chargeMode === 1 || localFormData.value.chargeMode === 2) {
if (value === '' || value === null || value === undefined || value <= 0) {
callback(new Error('请输入价格,且价格必须大于0'));
return;
}
}
callback();
};
const validateMerchantId = (rule, value, callback) => {
// 当收费模式为非免费时,收款商户必填
if (localFormData.value.chargeMode !== 0) {
// 如果有 merchantName,说明数据已经从后端回填了,即使没有 merchantId 也通过
if (!value && !localFormData.value.merchantName) {
callback(new Error('请选择收款商户'));
return;
}
}
callback();
};
const rules = { const rules = {
chargeMode: [{ required: true, message: '请选择收费模式', trigger: 'change' }], chargeMode: [{ required: true, message: '请选择收费模式', trigger: 'change' }],
price: [{ required: false, message: '请输入价格', trigger: 'change' }], price: [
merchantId: [{ required: true, message: '请选择收款商户', trigger: 'change' }], { validator: validatePrice, trigger: 'change' }
],
merchantId: [
{ validator: validateMerchantId, trigger: 'change' }
],
bankName: [{ required: false, message: '请输入开户银行', trigger: 'blur' }], bankName: [{ required: false, message: '请输入开户银行', trigger: 'blur' }],
accountNumber: [{ required: false, message: '请输入银行账号', trigger: 'blur' }], accountNumber: [{ required: false, message: '请输入银行账号', trigger: 'blur' }],
}; };

View File

@ -12,9 +12,7 @@
<!-- 步骤内容区域 --> <!-- 步骤内容区域 -->
<div class="step-content"> <div class="step-content">
<keep-alive> <component :is="currentStepComponent" :key="activeStep" ref="stepRef" :model-value="currentStepData" @update:model-value="handleStepDataUpdate" />
<component :is="currentStepComponent" ref="stepRef" :model-value="currentStepData" @update:model-value="handleStepDataUpdate" />
</keep-alive>
</div> </div>
<!-- 底部操作按钮 --> <!-- 底部操作按钮 -->
@ -160,13 +158,14 @@ const formData = ref({
}, },
// 步骤4:费用与账户配置 // 步骤4:费用与账户配置
feeConfig: { feeConfig: {
chargeMode: 'Free', chargeMode: 0, // 0=免费,1=定价(赛事),2=单价(赛事),3=单价(项目)
price: 0, price: 0,
merchantId: undefined, merchantId: undefined,
merchantName: '', merchantName: '',
creditCode: '', creditCode: '',
bankName: '', bankName: '',
accountNumber: '', accountNumber: '',
businessLicenseUrl: '',
}, },
}); });
@ -191,6 +190,10 @@ const openDialog = async (id) => {
console.log('openDialog called with id:', id); console.log('openDialog called with id:', id);
dialog.isShowDialog = true; dialog.isShowDialog = true;
dialog.loading = true; dialog.loading = true;
// 先重置表单,清除前一个赛事的数据残留
handleReset();
if (id) { if (id) {
dialog.title = '编辑赛事'; dialog.title = '编辑赛事';
dialog.submitTxt = '保 存'; dialog.submitTxt = '保 存';
@ -223,14 +226,24 @@ const openDialog = async (id) => {
projects: d.projects ?? [], projects: d.projects ?? [],
registrationLimit: d.registrationLimit ?? { individualProjectLimit: 1, teamProjectLimit: 1 }, registrationLimit: d.registrationLimit ?? { individualProjectLimit: 1, teamProjectLimit: 1 },
}; };
formData.value.feeConfig = { ...formData.value.feeConfig, ...(d.feeConfig ?? {}) }; // 完全覆盖 feeConfig,不要保留旧数据
console.log('formData.value', formData.value); if (d.feeConfig) {
formData.value.feeConfig = {
chargeMode: d.feeConfig.chargeMode ?? 0,
price: d.feeConfig.price ?? 0,
merchantId: d.feeConfig.merchantId,
merchantName: d.feeConfig.merchantName ?? '',
creditCode: d.feeConfig.creditCode ?? '',
bankName: d.feeConfig.bankName ?? '',
accountNumber: d.feeConfig.accountNumber ?? '',
businessLicenseUrl: d.feeConfig.businessLicenseUrl ?? '',
};
}
} }
} catch (e) { } catch (e) {
ElMessage.error('加载赛事详情失败'); ElMessage.error('加载赛事详情失败');
} }
} else { } else {
handleReset();
dialog.title = '新增赛事'; dialog.title = '新增赛事';
dialog.submitTxt = '新 增'; dialog.submitTxt = '新 增';
} }
@ -360,6 +373,7 @@ const handleReset = () => {
creditCode: '', creditCode: '',
bankName: '', bankName: '',
accountNumber: '', accountNumber: '',
businessLicenseUrl: '',
}; };
// 重置步骤 // 重置步骤

View File

@ -80,13 +80,13 @@ const refreshTable = () =>{
} }
const columns = reactive([ const columns = reactive([
{ {
width: '120px', width: '80px',
prop: 'index', prop: 'index',
label: '序号', label: '序号',
align: 'center', align: 'center',
}, },
{ {
width: '120px', width: '200px',
prop: 'eventName', prop: 'eventName',
label: '赛事名称', label: '赛事名称',
align: 'center', align: 'center',
@ -99,10 +99,9 @@ const columns = reactive([
prop: 'coverImage', prop: 'coverImage',
label: '封面', label: '封面',
align: 'center', align: 'center',
width: '120px',
}, },
{ {
width: '120px', width: '100px',
prop: 'eventType', prop: 'eventType',
label: '赛事类型', label: '赛事类型',
search: { search: {
@ -115,7 +114,7 @@ const columns = reactive([
], ],
}, },
{ {
width: '120px', width: '130px',
prop: 'registrationType', prop: 'registrationType',
label: '报名方式', label: '报名方式',
enum: [ enum: [
@ -125,7 +124,7 @@ const columns = reactive([
], ],
}, },
{ {
width: '120px', width: '130px',
prop: 'chargeMode', prop: 'chargeMode',
label: '收费模式', label: '收费模式',
search: { search: {
@ -139,10 +138,9 @@ const columns = reactive([
{ label: '单价(项目)', value: 3 }, { label: '单价(项目)', value: 3 },
], ],
}, },
{ prop: 'merchantName', label: '收款账户', width: '150px', },
{ prop: 'merchantName', label: '收款账户',width: '120px', },
{ {
width: '120px', width: '100px',
prop: 'eventStatus', prop: 'eventStatus',
label: '赛事状态', label: '赛事状态',
search: { search: {
@ -157,7 +155,7 @@ const columns = reactive([
], ],
}, },
{ {
width: '120px', width: '160px',
prop: 'registrationStartTime', prop: 'registrationStartTime',
label: '报名开始时间', label: '报名开始时间',
search: { search: {
@ -168,7 +166,7 @@ const columns = reactive([
}, },
}, },
{ {
width: '120px', width: '160px',
prop: 'registrationEndTime', prop: 'registrationEndTime',
label: '报名结束时间', label: '报名结束时间',
search: { search: {
@ -179,7 +177,7 @@ const columns = reactive([
}, },
}, },
{ {
width: '120px', width: '160px',
prop: 'matchStartTime', prop: 'matchStartTime',
label: '比赛开始时间', label: '比赛开始时间',
search: { search: {
@ -190,7 +188,7 @@ const columns = reactive([
}, },
}, },
{ {
width: '120px', width: '160px',
prop: 'matchEndTime', prop: 'matchEndTime',
label: '比赛结束时间', label: '比赛结束时间',
search: { search: {
@ -200,7 +198,7 @@ const columns = reactive([
}, },
}, },
}, },
{ prop: 'action', label: '操作', align: 'center', width: 160, fixed: 'right', isShow: auths(['event:edit', 'event:delete']) }, { prop: 'action', label: '操作', align: 'center', width: 200, fixed: 'right', isShow: auths(['event:edit', 'event:delete']) },
]); ]);
const exportableColumns = computed(() => const exportableColumns = computed(() =>

View File

@ -420,7 +420,7 @@
<script setup> <script setup>
import { ref, computed, nextTick, onUnmounted } from 'vue'; import { ref, computed, nextTick, onUnmounted } from 'vue';
import { ElMessage } 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 } 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';
@ -954,9 +954,6 @@ const openRegistrationDialog = async (row) => {
clearRegisterState(); clearRegisterState();
currentEventId.value = eventId; currentEventId.value = eventId;
isShowRegisterDialog.value = true;
activeStep.value = 0;
registerLoading.value = true; registerLoading.value = true;
try { try {
@ -965,9 +962,26 @@ const openRegistrationDialog = async (row) => {
ElMessage.error(data ?? '项目加载失败'); ElMessage.error(data ?? '项目加载失败');
return; return;
} }
// 检查报名类型,如果是只允许个人报名,则显示提示并阻止继续
const registrationType = data?.registrationType;
if (registrationType === 'OnlyIndividual') {
ElMessageBox.alert(
'当前赛事只允许个人报名,请到移动端进行个人报名',
'提示',
{
confirmButtonText: '确定',
type: 'warning'
}
);
return;
}
// 保存完整的详情数据,用于项目类型显示等 // 保存完整的详情数据,用于项目类型显示等
detailData.value = data; detailData.value = data;
projects.value = Array.isArray(data?.projects) ? data.projects : []; projects.value = Array.isArray(data?.projects) ? data.projects : [];
isShowRegisterDialog.value = true;
activeStep.value = 0;
} catch { } catch {
ElMessage.error('项目加载失败'); ElMessage.error('项目加载失败');
} finally { } finally {