2026-02-12 21:38:29 +08:00
|
|
|
<template>
|
2026-03-18 18:03:12 +08:00
|
|
|
<div class="step-config">
|
|
|
|
|
<el-form ref="formRef" :model="localFormData" :rules="rules" label-position="top">
|
|
|
|
|
<el-card shadow="never" style="margin-bottom: 16px">
|
|
|
|
|
<template #header>限报配置</template>
|
|
|
|
|
<el-row :gutter="12">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="个人项目限报数" prop="registrationLimit.individualProjectLimit">
|
|
|
|
|
<el-input-number v-model="localFormData.registrationLimit.individualProjectLimit" :min="0" :step="1" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="团队项目限报数" prop="registrationLimit.teamProjectLimit">
|
|
|
|
|
<el-input-number v-model="localFormData.registrationLimit.teamProjectLimit" :min="0" :step="1" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
|
|
<span>项目配置</span>
|
|
|
|
|
<el-button type="primary" plain icon="ele-Plus" @click="addProject">新增项目</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<el-table :data="localFormData.projects" border stripe>
|
|
|
|
|
<el-table-column label="项目名称" min-width="180">
|
|
|
|
|
<template #default="{ row, $index }">
|
|
|
|
|
<el-form-item :prop="`projects.${$index}.name`" :rules="[{ required: true, message: '请输入项目名称', trigger: 'blur' }]" style="margin: 0">
|
|
|
|
|
<el-input v-model="row.name" placeholder="请输入项目名称" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="项目类型ID" width="120">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.projectTypeId" :min="0" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="项目类型名" width="140">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input v-model="row.projectTypeName" placeholder="可不填" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="完成次数" width="120">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.completionCount" :min="0" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="时长(分)" width="110">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.durationMinutes" :min="0" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="时长(秒)" width="110">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.durationSeconds" :min="0" :max="59" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="费用" width="110">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.fee" :min="0" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="限报人数" width="110">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.limitCount" :min="0" :step="1" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="组别(简)" min-width="220">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input v-model="row.groupsText" placeholder="格式:组名|起始生日|结束生日|性别;多条用换行" type="textarea" :rows="2" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="120" align="center">
|
|
|
|
|
<template #default="{ $index }">
|
|
|
|
|
<el-button text type="danger" icon="ele-Delete" @click="removeProject($index)">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-form>
|
|
|
|
|
</div>
|
2026-02-12 21:38:29 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-03-18 18:03:12 +08:00
|
|
|
import { ref, watch } from 'vue';
|
|
|
|
|
|
|
|
|
|
const props = defineProps({ modelValue: { type: Object, required: true } });
|
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
|
|
|
|
|
|
const formRef = ref(null);
|
|
|
|
|
const localFormData = ref({
|
|
|
|
|
projects: [],
|
|
|
|
|
registrationLimit: { individualProjectLimit: 1, teamProjectLimit: 1 },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const normalizeFromParent = (v) => {
|
|
|
|
|
localFormData.value.registrationLimit = v?.registrationLimit ?? { individualProjectLimit: 1, teamProjectLimit: 1 };
|
|
|
|
|
localFormData.value.projects = (v?.projects ?? []).map((p) => ({
|
|
|
|
|
...p,
|
|
|
|
|
// 用文本编辑组别,提交时再解析成 groups[]
|
|
|
|
|
groupsText: Array.isArray(p.groups)
|
|
|
|
|
? p.groups.map((g) => `${g.groupName}|${g.birthDateStart ?? ''}|${g.birthDateEnd ?? ''}|${g.gender ?? ''}`).join('\n')
|
|
|
|
|
: '',
|
|
|
|
|
}));
|
|
|
|
|
if (!localFormData.value.projects.length) addProject();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
(v) => normalizeFromParent(v),
|
|
|
|
|
{ deep: true, immediate: true }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
localFormData,
|
|
|
|
|
(v) => {
|
|
|
|
|
const projects = v.projects.map((p) => ({
|
|
|
|
|
id: p.id,
|
|
|
|
|
name: p.name,
|
|
|
|
|
projectTypeId: p.projectTypeId,
|
|
|
|
|
projectTypeName: p.projectTypeName,
|
|
|
|
|
completionCount: p.completionCount,
|
|
|
|
|
durationMinutes: p.durationMinutes,
|
|
|
|
|
durationSeconds: p.durationSeconds,
|
|
|
|
|
fee: p.fee,
|
|
|
|
|
limitCount: p.limitCount,
|
|
|
|
|
groups: (p.groupsText || '')
|
|
|
|
|
.split('\n')
|
|
|
|
|
.map((line) => line.trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.map((line) => {
|
|
|
|
|
const [groupName, birthDateStart, birthDateEnd, gender] = line.split('|').map((x) => (x ?? '').trim());
|
|
|
|
|
return { groupName, birthDateStart, birthDateEnd, gender };
|
|
|
|
|
}),
|
|
|
|
|
}));
|
|
|
|
|
emit('update:modelValue', { ...props.modelValue, registrationLimit: v.registrationLimit, projects });
|
|
|
|
|
},
|
|
|
|
|
{ deep: true }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const addProject = () => {
|
|
|
|
|
localFormData.value.projects.push({
|
|
|
|
|
name: '',
|
|
|
|
|
projectTypeId: 0,
|
|
|
|
|
projectTypeName: '',
|
|
|
|
|
completionCount: 0,
|
|
|
|
|
durationMinutes: 0,
|
|
|
|
|
durationSeconds: 0,
|
|
|
|
|
fee: 0,
|
|
|
|
|
limitCount: 0,
|
|
|
|
|
groupsText: '',
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const removeProject = (idx) => localFormData.value.projects.splice(idx, 1);
|
|
|
|
|
|
|
|
|
|
const rules = {
|
|
|
|
|
'registrationLimit.individualProjectLimit': [{ required: true, message: '请填写个人项目限报数', trigger: 'change' }],
|
|
|
|
|
'registrationLimit.teamProjectLimit': [{ required: true, message: '请填写团队项目限报数', trigger: 'change' }],
|
|
|
|
|
};
|
2026-02-12 21:38:29 +08:00
|
|
|
|
|
|
|
|
const validate = async () => {
|
2026-03-18 18:03:12 +08:00
|
|
|
if (!formRef.value) return false;
|
|
|
|
|
return await formRef.value.validate();
|
|
|
|
|
};
|
2026-02-12 21:38:29 +08:00
|
|
|
|
2026-03-18 18:03:12 +08:00
|
|
|
defineExpose({ validate });
|
2026-02-12 21:38:29 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.step-config {
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.step-config .el-form-item {
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.input-hint {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #909399;
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.switch-hint {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #909399;
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-info-card {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
padding: 10px 15px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-name {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #409eff;
|
|
|
|
|
min-width: 120px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-desc {
|
|
|
|
|
color: #606266;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|