You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
708 lines
18 KiB
708 lines
18 KiB
const app = getApp<IAppOption>() |
|
|
|
const DRAFT_KEY = 'actAdd:draft' |
|
|
|
interface ILevelItem { |
|
id: number |
|
name: string |
|
code: string |
|
sort: number |
|
} |
|
|
|
interface ICategoryItem { |
|
id: number |
|
name: string |
|
code: string |
|
icon: string |
|
sort: number |
|
isEnabled: number |
|
} |
|
|
|
interface ITagItem { |
|
id: number |
|
name: string |
|
sort: number |
|
activityCount: number |
|
} |
|
|
|
interface AgendaItem { |
|
agendaDate: string |
|
agendaTime: string |
|
title: string |
|
description: string |
|
sort: number |
|
} |
|
|
|
Page({ |
|
data: { |
|
currentStep: 1, |
|
steps: [ |
|
{ label: '基本信息', field: 'basic' }, |
|
{ label: '报名签到设置', field: 'signup' }, |
|
{ label: '活动议程', field: 'agenda' }, |
|
], |
|
|
|
// 步骤1 基本信息 |
|
coverImageList: [] as Array<{ uid: string; url: string; type: string; name: string; size: number }>, |
|
title: '', |
|
type: 1, |
|
typeOptions: [ |
|
{ id: 1, name: '讲座' }, |
|
{ id: 2, name: '比赛' }, |
|
{ id: 3, name: '社团' }, |
|
{ id: 4, name: '志愿' }, |
|
{ id: 5, name: '文体' }, |
|
{ id: 6, name: '其他' }, |
|
], |
|
typeOther: '', |
|
summary: '', |
|
startTime: '', |
|
endTime: '', |
|
detail: '', |
|
detailImages: [] as string[], |
|
location: '', |
|
organizer: '', |
|
contactName: '', |
|
contactPhone: '', |
|
|
|
// 活动等级 |
|
levelList: [] as ILevelItem[], |
|
levelId: 0, |
|
|
|
// 活动分类 |
|
categoryList: [] as ICategoryItem[], |
|
selectedCategoryIds: [] as number[], |
|
categoryTags: [] as Array<{ id: number; name: string; isSelected: boolean }>, // 用于显示选中状态 |
|
|
|
// 活动标签 |
|
tagList: [] as ITagItem[], |
|
selectedTagIds: [] as number[], |
|
tagTags: [] as Array<{ id: number; name: string; isSelected: boolean }>, // 用于显示选中状态 |
|
|
|
// 步骤2 报名签到设置 |
|
needRegister: true, |
|
registerStartTime: '', |
|
registerEndTime: '', |
|
registerLimit: 'unlimited', |
|
registerLimitCount: '', |
|
registerCondition: '', |
|
|
|
checkinWay: 'dynamic', |
|
checkinStartTime: '', |
|
checkinEndTime: '', |
|
|
|
// 步骤3 活动议程 |
|
agendas: [{ agendaDate: '', agendaTime: '', title: '', description: '', sort: 0 }] as AgendaItem[], |
|
nextAgendaId: 2, |
|
|
|
// 提交状态 |
|
submitting: false, |
|
}, |
|
|
|
async onLoad() { |
|
await app.waitLogin({ type: 1 }) |
|
try { |
|
await Promise.all([this.fetchLevelList(), this.fetchCategoryList(), this.fetchTagList()]) |
|
} catch (err) { |
|
console.error('初始化数据失败:', err) |
|
} |
|
|
|
// 检查是否有本地草稿 |
|
try { |
|
const draft = wx.getStorageSync(DRAFT_KEY) as any |
|
if (draft && Object.keys(draft).length) { |
|
wx.showModal({ |
|
title: '检测到未完成的活动', |
|
content: '上次未完成的表单已保存,是否继续编辑?', |
|
confirmText: '继续编辑', |
|
cancelText: '重新开始', |
|
success: (res) => { |
|
if (res.confirm) { |
|
this.restoreDraft(draft) |
|
} else { |
|
// 用户选择重新开始,清除草稿 |
|
this.clearDraft() |
|
} |
|
}, |
|
}) |
|
} |
|
} catch (err) { |
|
console.error('读取本地草稿失败:', err) |
|
} |
|
}, |
|
|
|
// ========== 草稿管理 ========== |
|
buildDraft(partial: Record<string, any> = {}) { |
|
const keys = [ |
|
'currentStep', |
|
'coverImageList', |
|
'title', |
|
'type', |
|
'typeOther', |
|
'summary', |
|
'startTime', |
|
'endTime', |
|
'detail', |
|
'detailImages', |
|
'location', |
|
'organizer', |
|
'contactName', |
|
'contactPhone', |
|
'levelId', |
|
'selectedCategoryIds', |
|
'selectedTagIds', |
|
'needRegister', |
|
'registerStartTime', |
|
'registerEndTime', |
|
'registerLimit', |
|
'registerLimitCount', |
|
'registerCondition', |
|
'checkinWay', |
|
'checkinStartTime', |
|
'checkinEndTime', |
|
'agendas', |
|
] |
|
|
|
const draft: Record<string, any> = {} |
|
keys.forEach((k) => { |
|
if (Object.prototype.hasOwnProperty.call(partial, k)) { |
|
draft[k] = partial[k] |
|
} else { |
|
draft[k] = (this.data as any)[k] |
|
} |
|
}) |
|
return draft |
|
}, |
|
|
|
saveDraft(partial: Record<string, any> = {}) { |
|
try { |
|
const draft = this.buildDraft(partial) |
|
wx.setStorageSync(DRAFT_KEY, draft) |
|
} catch (err) { |
|
console.error('保存草稿失败:', err) |
|
} |
|
}, |
|
|
|
clearDraft() { |
|
try { |
|
wx.removeStorageSync(DRAFT_KEY) |
|
} catch { |
|
/* ignore */ |
|
} |
|
}, |
|
|
|
restoreDraft(draft: Record<string, any>) { |
|
if (!draft) return |
|
const safeDraft = { ...draft } |
|
safeDraft.coverImageList = safeDraft.coverImageList || [] |
|
safeDraft.detailImages = safeDraft.detailImages || [] |
|
safeDraft.agendas = safeDraft.agendas || [] |
|
safeDraft.selectedCategoryIds = safeDraft.selectedCategoryIds || [] |
|
safeDraft.selectedTagIds = safeDraft.selectedTagIds || [] |
|
|
|
this.setData(safeDraft) |
|
|
|
// 重建标签选中状态显示 |
|
const categoryTags = (this.data.categoryList || []).map((item: ICategoryItem) => ({ |
|
id: item.id, |
|
name: item.name, |
|
isSelected: safeDraft.selectedCategoryIds.includes(item.id), |
|
})) |
|
const tagTags = (this.data.tagList || []).map((item: ITagItem) => ({ |
|
id: item.id, |
|
name: item.name, |
|
isSelected: safeDraft.selectedTagIds.includes(item.id), |
|
})) |
|
this.setData({ categoryTags, tagTags }) |
|
}, |
|
|
|
setAndSave(patch: Record<string, any>) { |
|
this.setData(patch) |
|
this.saveDraft(patch) |
|
}, |
|
|
|
// 获取活动等级列表 |
|
async fetchLevelList() { |
|
try { |
|
const res = await wx.ajax({ |
|
url: '/activity-level/list', |
|
method: 'GET', |
|
data: {}, |
|
}) |
|
if (res && res.list) { |
|
this.setData({ levelList: res.list }) |
|
} |
|
} catch (err) { |
|
console.error('获取活动等级列表失败:', err) |
|
} |
|
}, |
|
|
|
// 获取活动分类列表 |
|
async fetchCategoryList() { |
|
try { |
|
const res = await wx.ajax({ |
|
url: '/activity-category/list', |
|
method: 'GET', |
|
data: {}, |
|
}) |
|
if (res && res.list) { |
|
const { selectedCategoryIds } = this.data |
|
const categoryTags = res.list.map((item: ICategoryItem) => ({ |
|
id: item.id, |
|
name: item.name, |
|
isSelected: selectedCategoryIds.includes(item.id), |
|
})) |
|
this.setData({ |
|
categoryList: res.list, |
|
categoryTags, |
|
}) |
|
} |
|
} catch (err) { |
|
console.error('获取活动分类列表失败:', err) |
|
} |
|
}, |
|
|
|
// 获取活动标签列表 |
|
async fetchTagList() { |
|
try { |
|
const res = await wx.ajax({ |
|
url: '/activity-tag/list', |
|
method: 'GET', |
|
data: {}, |
|
}) |
|
if (res && res.list) { |
|
const { selectedTagIds } = this.data |
|
const tagTags = res.list.map((item: ITagItem) => ({ |
|
id: item.id, |
|
name: item.name, |
|
isSelected: selectedTagIds.includes(item.id), |
|
})) |
|
this.setData({ |
|
tagList: res.list, |
|
tagTags, |
|
}) |
|
} |
|
} catch (err) { |
|
console.error('获取活动标签列表失败:', err) |
|
} |
|
}, |
|
|
|
// ========== 步骤切换 ========== |
|
goStep(step: number) { |
|
if (step < 1 || step > 4) return |
|
this.setAndSave({ currentStep: step }) |
|
}, |
|
|
|
// 验证当前步骤的必填项 |
|
validateCurrentStep(): boolean { |
|
const { |
|
currentStep, |
|
coverImageList, |
|
title, |
|
startTime, |
|
endTime, |
|
location, |
|
needRegister, |
|
registerStartTime, |
|
registerEndTime, |
|
checkinWay, |
|
checkinStartTime, |
|
checkinEndTime, |
|
agendas, |
|
} = this.data |
|
|
|
// 步骤1:基本信息 |
|
if (currentStep === 1) { |
|
if (!coverImageList.length) { |
|
wx.showToast({ title: '请上传活动头图', icon: 'none' }) |
|
return false |
|
} |
|
if (!title.trim()) { |
|
wx.showToast({ title: '请输入活动标题', icon: 'none' }) |
|
return false |
|
} |
|
if (!startTime) { |
|
wx.showToast({ title: '请选择活动开始时间', icon: 'none' }) |
|
return false |
|
} |
|
if (!endTime) { |
|
wx.showToast({ title: '请选择活动结束时间', icon: 'none' }) |
|
return false |
|
} |
|
if (!location.trim()) { |
|
wx.showToast({ title: '请输入活动地点', icon: 'none' }) |
|
return false |
|
} |
|
} |
|
|
|
// 步骤2:报名签到设置 |
|
if (currentStep === 2) { |
|
if (needRegister) { |
|
if (!registerStartTime) { |
|
wx.showToast({ title: '请选择报名开始时间', icon: 'none' }) |
|
return false |
|
} |
|
if (!registerEndTime) { |
|
wx.showToast({ title: '请选择报名截止时间', icon: 'none' }) |
|
return false |
|
} |
|
} |
|
|
|
// 签到时间校验(动态二维码或固定二维码时必填) |
|
if (checkinWay !== 'none') { |
|
if (!checkinStartTime) { |
|
wx.showToast({ title: '请选择签到开始时间', icon: 'none' }) |
|
return false |
|
} |
|
if (!checkinEndTime) { |
|
wx.showToast({ title: '请选择签到结束时间', icon: 'none' }) |
|
return false |
|
} |
|
} |
|
} |
|
|
|
// 步骤3:活动议程 |
|
if (currentStep === 3) { |
|
if (!agendas.length || !agendas[0].title.trim()) { |
|
wx.showToast({ title: '请添加活动议程', icon: 'none' }) |
|
return false |
|
} |
|
} |
|
|
|
return true |
|
}, |
|
|
|
onNextStep() { |
|
// 先验证当前步骤 |
|
if (!this.validateCurrentStep()) { |
|
return |
|
} |
|
|
|
const next = this.data.currentStep + 1 |
|
if (next <= 4) this.setAndSave({ currentStep: next }) |
|
}, |
|
|
|
onPrevStep() { |
|
const prev = this.data.currentStep - 1 |
|
if (prev >= 1) this.setAndSave({ currentStep: prev }) |
|
}, |
|
|
|
// ========== 图片上传 ========== |
|
// 上传成功后,直接添加到列表(maxCount=1,只保留一个) |
|
onCoverSuccess(e: WechatMiniprogram.CustomEvent) { |
|
const { file } = e.detail |
|
this.setAndSave({ coverImageList: [file] }) |
|
}, |
|
|
|
// 上传失败后,显示错误 |
|
onCoverError(_e: WechatMiniprogram.CustomEvent) { |
|
wx.showToast({ title: '上传失败,请重试', icon: 'none' }) |
|
}, |
|
|
|
// 删除封面图片 |
|
handleDelCover() { |
|
this.setAndSave({ coverImageList: [] }) |
|
}, |
|
|
|
onDetailImageSuccess(e: WechatMiniprogram.CustomEvent) { |
|
const { urls } = e.detail |
|
this.setAndSave({ detailImages: urls }) |
|
}, |
|
|
|
// ========== 输入绑定 ========== |
|
onInputChange(e: WechatMiniprogram.Input) { |
|
const { field } = e.currentTarget.dataset |
|
this.setAndSave({ [field]: e.detail.value }) |
|
}, |
|
|
|
onTextareaChange(e: WechatMiniprogram.TextareaInput) { |
|
const { field } = e.currentTarget.dataset |
|
this.setAndSave({ [field]: e.detail.value }) |
|
}, |
|
|
|
// ========== 时间选择 ========== |
|
onPickTime(e: WechatMiniprogram.PickerChange) { |
|
const { field } = e.currentTarget.dataset |
|
this.setAndSave({ [field]: e.detail.value }) |
|
}, |
|
|
|
// ========== 活动类型选择 ========== |
|
onSelectType(e: WechatMiniprogram.TouchEvent) { |
|
const { value } = e.currentTarget.dataset |
|
this.setAndSave({ type: value }) |
|
}, |
|
|
|
// ========== 活动等级选择 ========== |
|
onSelectLevel(e: WechatMiniprogram.TouchEvent) { |
|
const { id } = e.currentTarget.dataset |
|
this.setAndSave({ levelId: id }) |
|
}, |
|
|
|
// ========== 活动分类选择(多选) ========== |
|
onSelectCategory(e: WechatMiniprogram.TouchEvent) { |
|
const { id } = e.currentTarget.dataset |
|
let { selectedCategoryIds, categoryTags } = this.data |
|
|
|
const index = selectedCategoryIds.indexOf(id) |
|
if (index > -1) { |
|
selectedCategoryIds = selectedCategoryIds.filter((item) => item !== id) |
|
} else { |
|
selectedCategoryIds = [...selectedCategoryIds, id] |
|
} |
|
|
|
categoryTags = categoryTags.map((item) => ({ |
|
...item, |
|
isSelected: selectedCategoryIds.includes(item.id), |
|
})) |
|
|
|
this.setAndSave({ selectedCategoryIds, categoryTags }) |
|
}, |
|
|
|
// ========== 活动标签选择(多选) ========== |
|
onSelectTag(e: WechatMiniprogram.TouchEvent) { |
|
const { id } = e.currentTarget.dataset |
|
let { selectedTagIds, tagTags } = this.data |
|
|
|
const index = selectedTagIds.indexOf(id) |
|
if (index > -1) { |
|
selectedTagIds = selectedTagIds.filter((item) => item !== id) |
|
} else { |
|
selectedTagIds = [...selectedTagIds, id] |
|
} |
|
|
|
tagTags = tagTags.map((item) => ({ |
|
...item, |
|
isSelected: selectedTagIds.includes(item.id), |
|
})) |
|
|
|
this.setAndSave({ selectedTagIds, tagTags }) |
|
}, |
|
|
|
// ========== 议程管理 ========== |
|
onAddAgenda() { |
|
const agendas = this.data.agendas |
|
agendas.push({ |
|
agendaDate: '', |
|
agendaTime: '', |
|
title: '', |
|
description: '', |
|
sort: agendas.length, |
|
}) |
|
this.setAndSave({ agendas }) |
|
}, |
|
|
|
onRemoveAgenda(e: WechatMiniprogram.TouchEvent) { |
|
const { index } = e.currentTarget.dataset |
|
const agendas = this.data.agendas.filter((_, i) => i !== index) |
|
this.setAndSave({ agendas }) |
|
}, |
|
|
|
onAgendaInput(e: WechatMiniprogram.Input | WechatMiniprogram.TextareaInput) { |
|
const { index, field } = e.currentTarget.dataset |
|
const agendas = this.data.agendas |
|
agendas[index][field] = e.detail.value |
|
this.setAndSave({ agendas }) |
|
}, |
|
|
|
onAgendaTime(e: WechatMiniprogram.PickerChange) { |
|
const { index, field } = e.currentTarget.dataset |
|
const agendas = this.data.agendas |
|
agendas[index][field] = e.detail.value as string |
|
this.setAndSave({ agendas }) |
|
}, |
|
|
|
// ========== 报名签到设置 ========== |
|
onToggleRegister(e: WechatMiniprogram.TouchEvent) { |
|
const { value } = e.currentTarget.dataset |
|
this.setAndSave({ needRegister: value === 'yes' }) |
|
}, |
|
|
|
onToggleRegisterLimit(e: WechatMiniprogram.TouchEvent) { |
|
const { value } = e.currentTarget.dataset |
|
this.setAndSave({ registerLimit: value }) |
|
}, |
|
|
|
onSelectCheckinWay(e: WechatMiniprogram.TouchEvent) { |
|
const { value } = e.currentTarget.dataset |
|
this.setAndSave({ checkinWay: value }) |
|
}, |
|
|
|
// ========== 底部操作 ========== |
|
onSaveDraft() { |
|
this.submitActivity(1) // activityStatus = 1 (草稿) |
|
}, |
|
|
|
onSubmit() { |
|
wx.showModal({ |
|
title: '确认提交', |
|
content: '提交后将进入审核流程,是否继续?', |
|
success: (res) => { |
|
if (res.confirm) { |
|
this.submitActivity(2) // activityStatus = 2 (待审核) |
|
} |
|
}, |
|
}) |
|
}, |
|
|
|
// 提交活动申请 |
|
async submitActivity(activityStatus: number) { |
|
const { |
|
coverImageList, |
|
title, |
|
type, |
|
typeOther, |
|
summary, |
|
startTime, |
|
endTime, |
|
location, |
|
organizer, |
|
contactName, |
|
contactPhone, |
|
levelId, |
|
selectedCategoryIds, |
|
selectedTagIds, |
|
needRegister, |
|
registerStartTime, |
|
registerEndTime, |
|
registerLimit, |
|
registerLimitCount, |
|
registerCondition, |
|
checkinWay, |
|
checkinStartTime, |
|
checkinEndTime, |
|
agendas, |
|
detailImages, |
|
submitting, |
|
} = this.data |
|
|
|
if (submitting) return |
|
|
|
// 校验必填字段 |
|
if (!coverImageList.length) { |
|
wx.showToast({ title: '请上传活动头图', icon: 'error' }) |
|
return |
|
} |
|
if (!title.trim()) { |
|
wx.showToast({ title: '请输入活动标题', icon: 'error' }) |
|
return |
|
} |
|
if (!startTime) { |
|
wx.showToast({ title: '请选择活动开始时间', icon: 'error' }) |
|
return |
|
} |
|
if (!endTime) { |
|
wx.showToast({ title: '请选择活动结束时间', icon: 'error' }) |
|
return |
|
} |
|
if (!location.trim()) { |
|
wx.showToast({ title: '请输入活动地点', icon: 'error' }) |
|
return |
|
} |
|
|
|
// 校验报名设置 |
|
if (needRegister) { |
|
if (!registerStartTime) { |
|
wx.showToast({ title: '请选择报名开始时间', icon: 'error' }) |
|
return |
|
} |
|
if (!registerEndTime) { |
|
wx.showToast({ title: '请选择报名截止时间', icon: 'error' }) |
|
return |
|
} |
|
} |
|
|
|
// 校验签到设置 |
|
if (checkinWay !== 'none') { |
|
if (!checkinStartTime) { |
|
wx.showToast({ title: '请选择签到开始时间', icon: 'error' }) |
|
return |
|
} |
|
if (!checkinEndTime) { |
|
wx.showToast({ title: '请选择签到结束时间', icon: 'error' }) |
|
return |
|
} |
|
} |
|
|
|
// 校验议程 |
|
if (!agendas.length || !agendas[0].title.trim()) { |
|
wx.showToast({ title: '请添加活动议程', icon: 'error' }) |
|
return |
|
} |
|
|
|
this.setData({ submitting: true }) |
|
wx.showLoading({ title: activityStatus === 1 ? '保存中...' : '提交中...' }) |
|
|
|
try { |
|
const checkinTypeMap: Record<string, number> = { |
|
dynamic: 1, |
|
fixed: 2, |
|
none: 3, |
|
} |
|
|
|
const params: Record<string, any> = { |
|
mainImages: coverImageList.map((item) => item.url), |
|
name: title, |
|
type, |
|
typeOther: type === 6 ? typeOther : '', |
|
summary, |
|
detailImages, |
|
checkinType: checkinTypeMap[checkinWay], |
|
regType: needRegister ? 1 : 2, |
|
regCondition: registerCondition, |
|
quota: registerLimit === 'limited' ? Number(registerLimitCount) : 0, |
|
regStartAt: needRegister ? registerStartTime : '', |
|
regEndAt: needRegister ? registerEndTime : '', |
|
startAt: startTime, |
|
endAt: endTime, |
|
location, |
|
organizer, |
|
contactName, |
|
contactPhone, |
|
tagIds: selectedTagIds, |
|
categoryIds: selectedCategoryIds, |
|
levelId, |
|
agendas: agendas.map((item, index) => ({ |
|
...item, |
|
sort: index, |
|
})), |
|
activityStatus, |
|
} |
|
|
|
const res = await wx.ajax({ |
|
url: '/activity/apply', |
|
method: 'POST', |
|
data: params, |
|
}) |
|
|
|
wx.hideLoading() |
|
|
|
if (res) { |
|
wx.showToast({ |
|
title: activityStatus === 1 ? '已保存草稿' : '提交成功', |
|
icon: 'success', |
|
}) |
|
|
|
// 清理本地草稿 |
|
this.clearDraft() |
|
|
|
// 跳转到结果页面 |
|
wx.redirectTo({ |
|
url: `/pages/actAddResult/index?id=${res.activityId}&status=${res.status}`, |
|
}) |
|
} |
|
} catch (err: any) { |
|
wx.hideLoading() |
|
const message = err?.message || '提交失败' |
|
wx.showToast({ title: message, icon: 'error' }) |
|
} finally { |
|
this.setData({ submitting: false }) |
|
} |
|
}, |
|
|
|
onGoHome() { |
|
this.clearDraft() |
|
wx.switchTab({ url: '/pages/index/index' }) |
|
}, |
|
}) |
|
|
|
export {}
|
|
|