feat(mp, web, calendar): 新增微信收藏夹与日历批量生成功能

- 新增微信小程序版个人收藏夹全套功能,包含聚合首页、收藏列表、编辑、详情页面与配套后端API
- 新增web版时光助手门户与个人收藏夹工具,支持与小程序共享后端数据
- 优化日历提醒工具:替换本地bootstrap资源为公共CDN版本,新增批量生成循环提醒的前端界面与后端逻辑,新增数据库batch_id字段,更新鉴权配置文件
- 补充项目配置、站点地图、调试诊断脚本等配套文件
- 完善各模块的页面配置与样式文件
This commit is contained in:
yangxiangyuan
2026-09-02 22:20:37 +08:00
parent 132d132473
commit db7648eb4d
75 changed files with 8618 additions and 70 deletions
@@ -0,0 +1,173 @@
const api = require('../../api.js')
Page({
data: {
loading: true,
kits: [],
appNameZh: 'MilesYang时光助手',
subtitle: '个人工具集合,记录时光点滴。',
error: '',
clipVisible: false,
clipText: '',
clipCount: 0,
clipLines: 0,
clipPreview: '',
},
onLoad() {
this.loadMeta()
},
onShow() {
this.checkClipboard()
},
onPullDownRefresh() {
this.loadMeta().finally(() => {
wx.stopPullDownRefresh()
})
},
simpleHash(str) {
const s = String(str || '')
let h1 = 0x811c9dc5
let h2 = 0xdeadbeef
for (let i = 0; i < s.length; i++) {
const c = s.charCodeAt(i)
h1 = Math.imul(h1 ^ c, 16777619)
h2 = Math.imul(h2 ^ c, 2246822519)
}
h1 = (h1 ^ h1 >>> 16) >>> 0
h2 = (h2 ^ h2 >>> 15) >>> 0
return h1.toString(36) + h2.toString(36)
},
checkClipboard() {
try {
const enable = wx.getStorageSync('mp_ta_clip_enable')
if (enable === false || enable === 'false') return
} catch (e) {}
wx.getClipboardData({
success: (res) => {
const text = (res.data || '').trim()
if (text.length < 6 || text.length > 30000) return
const hash = this.simpleHash(text)
try {
const last = wx.getStorageSync('mp_ta_clip_hash')
if (last === hash) return
wx.setStorageSync('mp_ta_clip_hash', hash)
} catch (e) {}
const count = text.length
const lines = text.split('\n').length
const preview = text.length > 220 ? text.slice(0, 220) + '\n\n……' : text
this.setData({
clipVisible: true,
clipText: text,
clipCount: count,
clipLines: lines,
clipPreview: preview,
})
},
fail: () => {}
})
},
onClipCollect(e) {
const mode = (e && e.currentTarget && e.currentTarget.dataset.mode) || 'new'
const text = this.data.clipText || ''
if (!text) { this.setData({ clipVisible: false }); return }
try { wx.setStorageSync('mp_ta_clip_text', text) } catch (e) {}
try { wx.setStorageSync('mp_ta_clip_mode', mode) } catch (e) {}
this.setData({ clipVisible: false })
wx.navigateTo({
url: '/kits/personal_collect/pages/edit/edit',
fail: () => { wx.showToast({ title: '打开失败', icon: 'none' }) }
})
},
onClipCancel() {
this.setData({ clipVisible: false })
},
onClipDismissForever() {
wx.showModal({
title: '不再检测剪贴板?',
content: '关闭后可在未来任意新增收藏页面时重新开启。',
confirmText: '不再提示',
cancelText: '保留',
success: (r) => {
if (r.confirm) {
try { wx.setStorageSync('mp_ta_clip_enable', false) } catch (e) {}
this.setData({ clipVisible: false })
wx.showToast({ title: '已关闭检测', icon: 'none' })
}
}
})
},
async loadMeta() {
this.setData({ loading: true, error: '' })
try {
const res = await api.core.getMeta()
if (res && res.appNameZh) {
this.setData({ appNameZh: res.appNameZh })
}
if (res && Array.isArray(res.kits) && res.kits.length) {
this.setData({ kits: this.decorateKits(res.kits) })
try { getApp().setMeta(res) } catch (e) {}
} else {
this.setData({ kits: this.fallbackKits() })
}
} catch (e) {
this.setData({
error: e && e.message ? e.message : '加载失败,请检查网络',
kits: this.fallbackKits(),
})
} finally {
this.setData({ loading: false })
}
},
fallbackKits() {
return this.decorateKits([{
id: 'personal_collect',
name: '微信收藏夹',
description: '聊天图片、文件收藏管理,标签搜索,软删除可追溯。',
}])
},
decorateKits(list) {
const icons = {
personal_collect: '📁',
}
return list.map(item => Object.assign({}, item, {
icon: icons[item.id] || '🛠️',
path: this.getKitPath(item.id),
}))
},
getKitPath(id) {
const map = {
personal_collect: '/kits/personal_collect/pages/list/list',
}
return map[id] || ''
},
async onKitTap(e) {
const id = e.currentTarget.dataset.id
const path = e.currentTarget.dataset.path
if (!path) {
wx.showToast({ title: '功能开发中', icon: 'none' })
return
}
try {
await api.core.setActiveKit(id)
} catch (e) {}
wx.navigateTo({
url: path,
fail: () => {
wx.showToast({ title: '打开失败', icon: 'none' })
},
})
},
})