体育活动创建可以沿用,承办单位选填;导出出现重复人员修复;优化增加外援功能,也可以导出;报名界面的设置领导以及教练不能删除修复

This commit is contained in:
2026-09-28 17:43:46 +08:00
parent 39bdcbfc14
commit b2fc1979fe
18 changed files with 1062 additions and 430 deletions
@@ -11,6 +11,21 @@
label-suffix=":">
<vi-title title="填写活动信息"></vi-title>
<!-- 沿用入口只用于创建活动,独占首行,宽度与下方单个表单字段一致。 -->
<el-row gutter="40" v-if="!form.id">
<el-col :span="8">
<el-form-item label="沿用之前活动">
<el-select v-model="reuseActivityId" clearable filterable
:loading="reuseLoading" :disabled="formLoading"
placeholder="请选择要沿用的活动" @change="reuseActivityChange"
style="width: 100%">
<el-option v-for="item in reuseActivityOptions" :key="item.id"
:label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row gutter="40">
<el-col :span="8">
<el-form-item prop="activityLevel" label="活动级别">
@@ -620,6 +635,12 @@ module.exports = {
planList: [],
events: [],
projectType: "",
reuseActivityId: null,
reuseActivityOptions: [],
reuseLoading: false,
reuseReady: false,
formLoading: false,
reuseRequestId: 0,
formRules: {
name: [{required: true, message: '必填', trigger: ['blur', 'change']}],
address: [{required: true, message: '必填', trigger: ['blur', 'change']}],
@@ -627,7 +648,6 @@ module.exports = {
eventsIds: [{required: true, message: '必填', trigger: ['blur', 'change']}],
applyTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
unitSponsor: [{required: true, message: '必填', trigger: ['blur', 'change']}],
undertakeUnit: [{required: true, message: '必填', trigger: ['blur', 'change']}],
// unitJointly: [{required: true, message: '必填', trigger: ['blur', 'change']}],
time: [{required: true, message: '必填', trigger: ['blur', 'change']}],
activityGroupId: [{required: true, message: '必填', trigger: ['blur', 'change']}],
@@ -652,6 +672,208 @@ module.exports = {
},
methods: {
// 下拉接口无参数;data 为当前管理范围内的 {id, name} 数组。
getReuseActivityOptions() {
return $.post('/platform/activity/school/info/mange/getReuseActivityOptions').then((resp) => {
if (resp.code === 0) {
this.$set(this, 'reuseActivityOptions', resp.data || [])
} else {
this.$message.error(resp.msg)
}
}).fail(() => {
this.$message.error('历史活动列表加载失败,请重新打开创建活动')
})
},
// JSON 列可能返回字符串、数组或空值,统一转成独立数组,避免修改来源数据。
reuseArray(value) {
let data = value
try {
data = typeof value === 'string' ? JSON.parse(value || '[]') : value
} catch (error) {
this.$message.warning('历史活动存在无法读取的配置,请检查并重新填写')
return []
}
return Array.isArray(data) ? clone(data) : []
},
// id 为来源活动ID;响应包含活动字段和 schoolEvents,不携带旧主键进入新增保存。
reuseActivityChange(id) {
const requestId = ++this.reuseRequestId
this.$set(this, 'reuseReady', false)
if (!id) {
this.$set(this, 'reuseLoading', false)
this.openAdd()
return
}
this.$set(this, 'reuseLoading', true)
return $.post('/platform/activity/school/info/mange/findReuseActivity', {id}).then((resp) => {
if (requestId !== this.reuseRequestId) return
if (resp.code !== 0) {
this.$message.error(resp.msg)
return
}
const form = clone(resp.data)
// 项目列表及编号按沿用活动的实际类型加载,旧日期要求重新填写。
return $.when(
$.post('/platform/activity/common/getEvents', {type: form.applyType}),
$.post('/platform/activity/apply/generateActivityCode', {activity_type: form.activityLevel}),
$.post('/platform/vi/common/getClubsByRole')
).then((eventResponse, codeResponse, clubResponse) => {
if (requestId !== this.reuseRequestId) return
// 当前项目使用 jQuery 1.x;when 返回每个请求的 [data, status, xhr]。
const responses = [eventResponse[0], codeResponse[0], clubResponse[0]]
const failed = responses.find((response) => response.code !== 0)
if (failed) {
this.$message.error(failed.msg)
return
}
const events = responses[0].data || []
const selectedIds = this.reuseArray(form.eventsIds)
const availableIds = selectedIds.filter((eventId) => events.some((event) => event.id === eventId))
if (availableIds.length !== selectedIds.length) {
this.$message.warning('历史活动中已停用或不存在的项目未沿用,请检查活动项目')
}
;['applyWay', 'unitSponsor', 'undertakeUnit', 'unitJointly', 'image'].forEach((key) => {
this.$set(form, key, this.reuseArray(form[key]))
})
const configs = (form.schoolEvents || []).filter((event) => availableIds.includes(event.eventId)).map((event) => {
const config = clone(event)
this.$set(config, 'id', null)
this.$set(config, 'activityId', null)
this.$set(config, 'unionLimit', this.reuseArray(config.unionLimit))
this.$set(config, 'ageDate', config.startAgeDate ? [config.startAgeDate, config.endAgeDate] : [])
return config
})
;['id', 'foundDate', 'applyStartTime', 'applyEndTime', 'startTime', 'endTime'].forEach((key) => this.$set(form, key, null))
this.$set(form, 'schoolEvents', [])
this.$set(form, 'close', false)
this.$set(form, 'activityCode', responses[1].data)
this.$set(form, 'activityLevel', Number(form.activityLevel))
this.$set(form, 'activityGroupId', form.activityGroupId ? Number(form.activityGroupId) : null)
this.$set(form, 'applyTime', [])
this.$set(form, 'time', [])
this.$set(form, 'eventsIds', availableIds)
this.$set(this, 'events', events)
this.$set(this, 'clubOptions', responses[2].data || [])
this.$set(this, 'form', form)
this.$set(this, 'eventsIds', [])
this.$set(this, 'checkedEventInfo', configs)
this.$set(this, 'checkedEventInfo2', clone(configs))
this.$set(this, 'eventForm', {})
this.$set(this, 'checkedEvents', [])
this.$set(this, 'rightChecked', [])
this.$set(this, 'reuseReady', true)
this.$nextTick(() => {
this.initEditor()
if (this.$refs.activityForm) this.$refs.activityForm.clearValidate()
})
})
}).fail(() => {
if (requestId === this.reuseRequestId) {
this.$message.error('沿用活动加载失败,请重新选择活动')
}
}).always(() => {
if (requestId === this.reuseRequestId) this.$set(this, 'reuseLoading', false)
})
},
// 合并当前所选项目与已沿用配置;新增选择使用原有默认值,已移除项目不再提交。
buildReuseEvents() {
return this.events.filter((event) => this.form.eventsIds.includes(event.id)).map((event) => {
const existing = this.checkedEventInfo.find((config) => config.eventId === event.id)
const config = existing ? clone(existing) : {
eventId: event.id, projectType: event.projectType, leanderNum: 0, coachNum: 0,
athletesMaxNum: event.projectType === '1' ? 3 : null,
restrictRegNumber: event.projectType === '1' ? 2 : null,
restrictTotalTeam: 1, substituteNum: 0, applyUserModel: 1
}
this.$set(config, 'id', null)
this.$set(config, 'activityId', null)
this.$set(config, 'startAgeDate', config.ageDate && config.ageDate.length ? config.ageDate[0] : null)
this.$set(config, 'endAgeDate', config.ageDate && config.ageDate.length ? config.ageDate[1] : null)
return config
})
},
// isSave 区分保存和提交;新接口返回新活动ID,保存成功后转入该新活动的编辑状态。
doReuseAdd(isSave) {
if (this.formLoading || this.reuseLoading) return
if (!this.reuseReady) {
this.$message.warning('请重新选择并加载沿用活动')
return
}
const save = () => {
const form = clone(this.form)
if (!form.applyTime || form.applyTime.length !== 2 || !form.time || form.time.length !== 2) {
this.$message.warning('请重新填写报名日期和活动时间')
return
}
if (moment(form.time[0]).valueOf() <= moment(form.applyTime[0]).valueOf()) {
this.$message.error('活动时间必须晚于报名时间')
return
}
if (!form.eventsIds.length) {
this.$message.warning('请选择活动项目')
return
}
if (this.getArrDifference(form.unitJointly, form.unitSponsor).length === 0) {
this.$message.error('协办单位里存在主办单位')
return
}
this.$set(form, 'id', null)
this.$set(form, 'isSave', isSave)
this.$set(form, 'applyStartTime', form.applyTime[0])
this.$set(form, 'applyEndTime', form.applyTime[1])
this.$set(form, 'startTime', form.time[0])
this.$set(form, 'endTime', form.time[1])
this.$set(this, 'formLoading', true)
const loading = this.$loading({lock: true, text: '正在提交...', background: COVER_LAYER_COLOR})
return $.post('/platform/activity/school/info/mange/doReuseAdd', {
sourceId: this.reuseActivityId, data: JSON.stringify(form), events: JSON.stringify(this.buildReuseEvents())
}).then((resp) => {
if (resp.code !== 0) {
this.$message.error(resp.msg)
return
}
this.$set(this.form, 'id', resp.data)
this.$set(this, 'reuseActivityId', null)
if (isSave) {
this.$emit('flush')
this.$message.success('保存成功')
// 保存后读取新项目主键,后续编辑必须更新新活动的项目,不能继续使用空主键。
return $.post('/platform/activity/school/info/mange/findOne', {id: resp.data}).then((saved) => {
if (saved.code !== 0) {
this.$message.error('活动已保存,回显失败,请从活动列表重新打开')
return
}
const configs = (saved.data.schoolEvents || []).map((event) => {
this.$set(event, 'unionLimit', this.reuseArray(event.unionLimit))
this.$set(event, 'ageDate', event.startAgeDate ? [event.startAgeDate, event.endAgeDate] : [])
return event
})
this.$set(this, 'checkedEventInfo', configs)
this.$set(this, 'checkedEventInfo2', clone(configs))
this.$set(this, 'eventsIds', clone(this.form.eventsIds))
const selected = configs.find((event) => event.eventId === this.eventForm.checkedEventId)
if (selected) {
this.$set(this, 'eventForm', Object.assign({}, selected, {checkedEventId: selected.eventId}))
}
})
}
this.$emit('flip')
}).fail(() => {
this.$message.error('请求失败,请检查活动列表后重试')
}).always(() => {
this.$set(this, 'formLoading', false)
loading.close()
})
}
if (this.$refs.activityForm) {
this.$refs.activityForm.validate((valid) => {
if (valid) save()
})
} else {
return save()
}
},
async applyUserModelInput(val) {
if (val === 2) {
if (!this.eventForm.unionLimit || this.eventForm.unionLimit.length === 0) {
@@ -871,6 +1093,11 @@ module.exports = {
});
},
next() {
if (this.reuseLoading || this.formLoading) return
if (this.reuseActivityId && !this.reuseReady) {
this.$message.warning('请重新选择并加载沿用活动')
return
}
this.$refs["activityForm"].validate(async (valid) => {
const form = clone(this.form)
if (!valid) {
@@ -917,7 +1144,13 @@ module.exports = {
this.eventForm.checkedEventId = this.checkedEvents[0].id
this.projectType = this.checkedEvents[0].projectType
if (!form.id || this.checkedEventInfo2.length <= 0) {
if (this.reuseActivityId && !form.id) {
// 新活动也保留沿用配置,不能因尚无活动ID而恢复为默认人数。
const allItems = this.checkedEventInfo.find((event) => event.eventId === 'allItems')
const configs = this.buildReuseEvents()
if (allItems) configs.unshift(clone(allItems))
this.$set(this, 'checkedEventInfo', configs)
} else if (!form.id || this.checkedEventInfo2.length <= 0) {
this.eventForm = {
checkedEventId: this.checkedEvents[0].id,
leanderNum: 0,
@@ -959,6 +1192,14 @@ module.exports = {
}
,
async openAdd() {
this.reuseRequestId++
this.$set(this, 'reuseLoading', false)
this.$set(this, 'reuseReady', false)
this.$set(this, 'reuseActivityId', null)
this.$set(this, 'checkedEventInfo', [])
this.$set(this, 'checkedEventInfo2', [])
this.$set(this, 'eventsIds', [])
this.getReuseActivityOptions()
this.events = await activity.getEvents(2)
this.planList = await activity.getSchoolActivityPlan(this.form.activityLevel)
await this.getActivityGroup()
@@ -977,6 +1218,7 @@ module.exports = {
applyWay: [2],
eventsIds: [],
unitJointly: [],
undertakeUnit: [],
applyType: 2,
activityCode: data,
activityLevel: 40001,
@@ -989,6 +1231,9 @@ module.exports = {
return data;
},
async openEdit(row, flag) {
this.reuseRequestId++
this.$set(this, 'reuseLoading', false)
this.$set(this, 'reuseActivityId', null)
this.active = flag ? 0 : this.active
await this.applyTypeChange(row.applyType)
this.planList = await activity.getSchoolActivityPlan(1)
@@ -1006,7 +1251,7 @@ module.exports = {
v.eventsIds = JSON.parse(v.eventsIds)
v.applyWay = JSON.parse(v.applyWay)
v.unitSponsor = JSON.parse(v.unitSponsor)
v.undertakeUnit = JSON.parse(v.undertakeUnit)
v.undertakeUnit = this.reuseArray(v.undertakeUnit)
v.unitJointly = JSON.parse(v.unitJointly)
v.activityGroupId = parseInt(v.activityGroupId)
v.activityLevel = parseInt(v.activityLevel)
@@ -1075,6 +1320,8 @@ module.exports = {
}
,
async operate() {
if (this.reuseLoading || this.formLoading) return
if (this.reuseActivityId && !this.form.id) return this.doReuseAdd(false)
const deleteEventIds = this.eventsIds.filter(v => !this.form.eventsIds.includes(v))
const {data} = await $.get(loc() + "/getEventApply", {
@@ -1094,6 +1341,8 @@ module.exports = {
}
},
async doSave() {
if (this.reuseLoading || this.formLoading) return
if (this.reuseActivityId && !this.form.id) return this.doReuseAdd(true)
let url = this.form.id ? "/doEdit" : "/doAdd"
this.checkedEventInfo.forEach(v => {
@@ -1295,4 +1544,4 @@ module.exports = {
.restrictTotalTeam .el-form-item__label {
color: red;
}
</style>
</style>