Files
wxserver/pages/add-anniversary/add-anniversary.js
T
yuming 756d57d818
部署到群晖 / deploy (push) Successful in 44s
简化纪念日类型:去掉"农历生日"类型,公历/农历改由 isLunar 字段决定
- typeList 从 5 项简化到 4 项:生日 / 结婚纪念日 / 订婚纪念日 / 其他纪念日
- TYPE_NAMES / TYPE_ICONS 中 lunar_birthday 保留兼容映射(也映射到「生日」+ 🎂),
  让线上历史数据自然回显,无需数据库迁移(方案 A)
- getTypeIndex('lunar_birthday') = 0,老数据编辑时正确回显「生日」
- index.js 列表筛选和 wxml 图标判断本来已含 lunar_birthday 兼容,无需改

老数据自然淘汰:用户重新保存时新数据写 type='birthday',老数据 type 保留
直到下次编辑保存才升级。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 07:30:37 +08:00

402 lines
10 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')
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
}
// 如果开启了提醒,请求订阅消息授权
if (formData.remindEnabled) {
try {
await this.requestSubscribe()
} catch (err) {
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)
}
}
},
/**
* 请求订阅消息授权
*/
requestSubscribe() {
return new Promise((resolve, reject) => {
wx.requestSubscribeMessage({
tmplIds: ['6J7Stt-lu7DKU6jblJ0nZGq_D81z5glnksf7qWfy5Yw'],
success: (res) => {
console.log('订阅消息授权结果:', res)
resolve(res)
},
fail: (err) => {
console.error('订阅消息授权失败:', err)
reject(err)
}
})
})
},
/**
* 同步纪念日到后端(失败自动入队,启动时 flush)
*/
syncToCloud(id, data, action) {
const openid = wx.getStorageSync('openid')
if (!openid) {
console.log('未获取到openid,跳过云端同步')
return
}
sync.syncOrEnqueue({ kind: 'anniversary', action, data: { id, ...data } })
}
})