Files
wxserver/pages/add-anniversary/add-anniversary.js
T
yuming bc4fb7445d 修复保存纪念日时授权订阅消息未上报额度的缺口
requestSubscribe() 改为委托给 utils/subscribe.js 的
requestAndReport(),用户同意授权后会把新增额度同步上报后端,
避免最主要的授权入口被漏记导致余额系统性低估、定时任务拒发。
onSubmit 里原来依赖 reject 的 try/catch 改为直接判断布尔返回值,
消除死代码分支,"拒绝不阻塞保存"的行为保持不变。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 09:31:03 +08:00

399 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// add-anniversary.js
const storage = require('../../utils/storage')
const dateUtils = require('../../utils/date')
const sync = require('../../utils/sync')
const lunar = require('../../utils/lunar')
const subscribe = require('../../utils/subscribe')
Page({
data: {
anniversaryId: null,
personId: null,
personList: [],
inputName: '',
typeList: ['生日', '结婚纪念日', '订婚纪念日', '其他纪念日'],
typeIndex: 0,
showCustomType: false,
dateValue: '',
remindDaysList: ['提前3天', '提前7天', '提前14天', '提前30天', '自定义'],
remindDaysIndex: 0,
// UI 反馈:选了农历日期时展示对应农历文本 + 闰月警示
lunarText: '',
isLeapMonth: false,
formData: {
isLunar: false,
type: 'birthday',
customTypeName: '',
solarYear: '',
solarMonth: '',
solarDay: '',
lunarYear: '',
lunarMonth: '',
lunarDay: '',
isLeapMonth: false,
importance: 'low',
remindEnabled: true,
remindDays: 7,
remark: ''
}
},
onLoad(options) {
// 获取人员列表用于快捷选择
const persons = storage.getPersons()
this.setData({ personList: persons })
if (options.personId) {
// 从人员详情页进入,预选关联人员
const person = persons.find(p => p.id === options.personId)
if (person) {
this.setData({
personId: person.id,
inputName: person.name
})
}
}
if (options.id) {
// 编辑模式
this.setData({ anniversaryId: options.id })
this.loadAnniversary(options.id)
} else {
// 设置默认日期为今天
const today = new Date()
this.setData({
dateValue: dateUtils.formatDate(today, 'YYYY-MM-DD')
})
}
},
/**
* 加载纪念日信息(编辑模式)
*/
loadAnniversary(id) {
const anniversaries = storage.getAnniversaries()
const anniversary = anniversaries.find(a => a.id === id)
if (anniversary) {
const date = dateUtils.formatDate(
new Date(anniversary.solarYear, anniversary.solarMonth - 1, anniversary.solarDay),
'YYYY-MM-DD'
)
const typeIndex = this.getTypeIndex(anniversary.type)
// 编辑时不允许改关联人员,姓名展示用 personName 或从 personList 查
const person = this.data.personList.find(p => p.id === anniversary.personId)
this.setData({
formData: anniversary,
dateValue: date,
typeIndex,
showCustomType: anniversary.type === 'other',
personId: anniversary.personId,
inputName: person ? person.name : (anniversary.personName || '')
})
// 农历日期:编辑时也要回显
if (anniversary.isLunar) this._refreshLunar()
wx.setNavigationBarTitle({ title: '编辑纪念日' })
}
},
/**
* 获取类型索引
* 注:lunar_birthday 是老数据兼容,新 UI 没有这一项,统一映射到「生日」(0)
*/
getTypeIndex(type) {
const indexMap = {
birthday: 0,
lunar_birthday: 0,
wedding: 1,
engagement: 2,
other: 3
}
return indexMap[type] || 0
},
/**
* 姓名输入:用户打字时实时更新;点 chip 时也会触发
* 输入框值变了就清掉已绑定的 personId,提交时再按姓名查找/创建
*/
onNameInput(e) {
const name = e.detail.value
const matched = this.data.personList.find(p => p.name === name.trim())
this.setData({
inputName: name,
personId: matched ? matched.id : null
})
},
/**
* 点已有人员快捷 chip
*/
onPickPerson(e) {
const { id, name } = e.currentTarget.dataset
this.setData({ personId: id, inputName: name })
},
/**
* 选择类型
*/
onTypeChange(e) {
const index = parseInt(e.detail.value)
const types = ['birthday', 'wedding', 'engagement', 'other']
const isOther = index === 3
this.setData({
typeIndex: index,
showCustomType: isOther,
'formData.type': types[index]
})
},
/**
* 自定义类型输入
*/
onCustomTypeInput(e) {
this.setData({
'formData.customTypeName': e.detail.value
})
},
/**
* 日期类型改变
*/
onDateTypeChange(e) {
const isLunar = e.detail.value === 'lunar'
this.setData({ 'formData.isLunar': isLunar })
this._refreshLunar()
},
/**
* 日期改变
*/
onDateChange(e) {
const dateStr = e.detail.value
const parts = dateStr.split('-')
this.setData({
dateValue: dateStr,
'formData.solarYear': parseInt(parts[0]),
'formData.solarMonth': parseInt(parts[1]),
'formData.solarDay': parseInt(parts[2])
})
this._refreshLunar()
},
/**
* 选了"农历"时,把当前公历日期反算成农历,写入 formData + 提示文案
* Why:闰月生日必须在 UI 上让用户确认这是不是他想要的农历日期
*/
_refreshLunar() {
const { formData } = this.data
if (!formData.isLunar || !formData.solarYear || !formData.solarMonth || !formData.solarDay) {
this.setData({ lunarText: '', isLeapMonth: false, 'formData.isLeapMonth': false })
return
}
const solarDate = new Date(formData.solarYear, formData.solarMonth - 1, formData.solarDay)
const ld = lunar.solarToLunar(solarDate)
this.setData({
lunarText: ld.lunarText,
isLeapMonth: ld.isLeap,
'formData.lunarYear': ld.year,
'formData.lunarMonth': ld.month,
'formData.lunarDay': ld.day,
'formData.isLeapMonth': ld.isLeap
})
},
/**
* 重要程度改变
*/
onImportanceChange(e) {
this.setData({
'formData.importance': e.detail.value
})
},
/**
* 提醒开关改变
*/
onRemindEnabledChange(e) {
this.setData({
'formData.remindEnabled': e.detail.value
})
},
/**
* 提醒天数改变
*/
onRemindDaysChange(e) {
const index = parseInt(e.detail.value)
this.setData({ remindDaysIndex: index })
if (index === 4) {
// 自定义天数
wx.showModal({
title: '自定义提前天数',
editable: true,
placeholderText: '请输入天数(1-365',
success: (res) => {
if (res.confirm) {
const days = parseInt(res.content)
if (!days || days < 1 || days > 365) {
wx.showToast({ title: '请输入1-365之间的天数', icon: 'none' })
this.setData({ remindDaysIndex: 1, 'formData.remindDays': 7 })
return
}
this.setData({ 'formData.remindDays': days })
} else {
// 取消则回到默认7天
this.setData({ remindDaysIndex: 1, 'formData.remindDays': 7 })
}
}
})
} else {
const days = [3, 7, 14, 30][index]
this.setData({ 'formData.remindDays': days })
}
},
/**
* 备注改变
*/
onRemarkChange(e) {
this.setData({
'formData.remark': e.detail.value
})
},
/**
* 取消
*/
onCancel() {
wx.navigateBack()
},
/**
* 提交
*/
async onSubmit() {
const { formData, anniversaryId, inputName } = this.data
let { personId } = this.data
// 验证姓名
const name = (inputName || '').trim()
if (!name) {
wx.showToast({ title: '请输入姓名', icon: 'none' })
return
}
// 验证日期
if (!formData.solarYear || !formData.solarMonth || !formData.solarDay) {
wx.showToast({ title: '请选择日期', icon: 'none' })
return
}
// 验证自定义类型
if (formData.type === 'other' && !formData.customTypeName) {
wx.showToast({ title: '请输入自定义类型', icon: 'none' })
return
}
// 如果开启了提醒,请求订阅消息授权
// 注:requestSubscribe 委托给 utils/subscribe.js 的 requestAndReport
// 该函数只会 resolvetrue=同意/false=拒绝),不会 reject
// 无论用户是否同意,都不阻塞后续保存流程
if (formData.remindEnabled) {
const accepted = await this.requestSubscribe()
if (!accepted) {
console.log('用户未同意订阅消息,本次不上报额度')
}
}
// 新增模式且没绑定 personId → 按姓名查找或自动创建
if (!anniversaryId && !personId) {
const person = storage.ensurePerson(name)
if (!person) {
wx.showToast({ title: '保存失败', icon: 'none' })
return
}
personId = person.id
}
// 农历生日:兜底反算(onDateChange/_refreshLunar 通常已填好,这里防边界)
if (formData.isLunar) {
const solarDate = new Date(formData.solarYear, formData.solarMonth - 1, formData.solarDay)
const lunarDate = lunar.solarToLunar(solarDate)
formData.lunarYear = lunarDate.year
formData.lunarMonth = lunarDate.month
formData.lunarDay = lunarDate.day
formData.isLeapMonth = lunarDate.isLeap
}
const personName = name
if (anniversaryId) {
// 编辑模式
const success = storage.updateAnniversary(anniversaryId, {
personId,
personName,
...formData
})
if (success) {
// 同步到云端
this.syncToCloud(anniversaryId, {
personId,
personName,
...formData
}, 'update')
wx.showToast({ title: '保存成功', icon: 'success' })
setTimeout(() => wx.navigateBack(), 1500)
}
} else {
// 新增模式
const newAnniversary = storage.addAnniversary({
personId,
personName,
...formData
})
if (newAnniversary) {
// 同步到云端
this.syncToCloud(newAnniversary.id, newAnniversary, 'add')
wx.showToast({ title: '添加成功', icon: 'success' })
setTimeout(() => wx.navigateBack(), 1500)
}
}
},
/**
* 请求订阅消息授权
* 委托给 utils/subscribe.js 的 requestAndReport:同意后会自动把新增额度
* 上报给后端(走同步队列,失败自动入队)。
* 注意:本方法不能加 async,且内部不能在调用 requestAndReport 之前插入
* 任何 await —— wx.requestSubscribeMessage 必须由用户点击手势同步触发,
* 一旦中间出现异步跳跃,手势上下文丢失会导致调用必然失败。
* @returns {Promise<boolean>} 用户是否同意
*/
requestSubscribe() {
return subscribe.requestAndReport()
},
/**
* 同步纪念日到后端(失败自动入队,启动时 flush)
*/
syncToCloud(id, data, action) {
const openid = wx.getStorageSync('openid')
if (!openid) {
console.log('未获取到openid,跳过云端同步')
return
}
sync.syncOrEnqueue({ kind: 'anniversary', action, data: { id, ...data } })
}
})