feat(bazi): 八字页重构——空态示例排盘+字段术语表+文化声明,生辰档案绑定
- 八字页:未绑定生辰时展示虚构示例排盘(1990-08-08 10:30男)与15条字段说明, 附传统文化声明文案;已绑定时支持多档案切换,排盘增强(十神/藏干/纳音/五行配色/命盘概览) - 个人中心:新增生辰管理卡片,每用户免费绑定3个出生年月;登录改为真实微信登录 - 服务端:新增 BirthProfile 模型与 /api/user/birth-profiles 增删查接口,AutoMigrate 建表 - 前端:新增 birth-profile.js 本地存储优先+登录后云端双向同步 - 修复:个人中心 tabBar 页面入口误用 navigateTo 改为 switchTab
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
// 生辰档案管理工具
|
||||
// 本地存储优先;已登录(存在 token)时与服务端双向同步
|
||||
const { request } = require('./request.js');
|
||||
|
||||
const STORAGE_KEY = 'lunar-birth-profiles';
|
||||
// 每个用户可免费绑定的生辰数量上限
|
||||
const MAX_PROFILES = 3;
|
||||
|
||||
/** 读取本地生辰档案列表 */
|
||||
function getProfiles() {
|
||||
return wx.getStorageSync(STORAGE_KEY) || [];
|
||||
}
|
||||
|
||||
/** 保存本地生辰档案列表 */
|
||||
function saveProfiles(profiles) {
|
||||
wx.setStorageSync(STORAGE_KEY, profiles);
|
||||
}
|
||||
|
||||
/** 获取当前激活的档案(第一个为默认) */
|
||||
function getActiveProfile() {
|
||||
const profiles = getProfiles();
|
||||
if (profiles.length === 0) return null;
|
||||
const activeId = wx.getStorageSync('lunar-birth-active-id');
|
||||
return profiles.find(p => p.id === activeId) || profiles[0];
|
||||
}
|
||||
|
||||
/** 设置激活档案 */
|
||||
function setActiveProfile(id) {
|
||||
wx.setStorageSync('lunar-birth-active-id', id);
|
||||
}
|
||||
|
||||
/** 新增生辰档案(本地),超出上限或重复时返回错误信息 */
|
||||
function addProfile(profile) {
|
||||
const profiles = getProfiles();
|
||||
if (profiles.length >= MAX_PROFILES) {
|
||||
return { ok: false, msg: `最多可免费绑定 ${MAX_PROFILES} 个生辰` };
|
||||
}
|
||||
const dup = profiles.find(p => p.birthday === profile.birthday && p.birthTime === profile.birthTime);
|
||||
if (dup) {
|
||||
return { ok: false, msg: '该生辰已绑定,请勿重复添加' };
|
||||
}
|
||||
const item = {
|
||||
id: `local-${Date.now()}`,
|
||||
name: profile.name || '未命名',
|
||||
gender: profile.gender || 'male',
|
||||
birthday: profile.birthday,
|
||||
birthTime: profile.birthTime,
|
||||
serverId: 0,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
profiles.push(item);
|
||||
saveProfiles(profiles);
|
||||
if (!getActiveProfile()) setActiveProfile(item.id);
|
||||
return { ok: true, profile: item };
|
||||
}
|
||||
|
||||
/** 删除生辰档案(本地) */
|
||||
function removeProfile(id) {
|
||||
const profiles = getProfiles().filter(p => p.id !== id);
|
||||
saveProfiles(profiles);
|
||||
if (wx.getStorageSync('lunar-birth-active-id') === id) {
|
||||
setActiveProfile(profiles.length > 0 ? profiles[0].id : '');
|
||||
}
|
||||
}
|
||||
|
||||
/** 是否已登录(存在有效 token) */
|
||||
function isLoggedIn() {
|
||||
return !!wx.getStorageSync('token');
|
||||
}
|
||||
|
||||
/** 推送单个档案到服务端(静默失败,不影响本地) */
|
||||
function pushProfileToServer(profile) {
|
||||
if (!isLoggedIn()) return Promise.resolve(null);
|
||||
return request({
|
||||
url: '/api/user/birth-profiles',
|
||||
method: 'POST',
|
||||
data: {
|
||||
name: profile.name,
|
||||
gender: profile.gender === 'female' ? 2 : 1,
|
||||
birthday: profile.birthday,
|
||||
birthTime: profile.birthTime
|
||||
}
|
||||
}).then(res => {
|
||||
if (res && res.code === 0 && res.data && res.data.profile) {
|
||||
// 回填 serverId
|
||||
const profiles = getProfiles();
|
||||
const target = profiles.find(p => p.id === profile.id);
|
||||
if (target) {
|
||||
target.serverId = res.data.profile.id;
|
||||
saveProfiles(profiles);
|
||||
}
|
||||
return res.data.profile;
|
||||
}
|
||||
return null;
|
||||
}).catch(() => null);
|
||||
}
|
||||
|
||||
/** 删除服务端档案(静默失败) */
|
||||
function removeProfileFromServer(profile) {
|
||||
if (!isLoggedIn() || !profile.serverId) return Promise.resolve();
|
||||
return request({
|
||||
url: `/api/user/birth-profiles/${profile.serverId}`,
|
||||
method: 'DELETE'
|
||||
}).catch(() => null);
|
||||
}
|
||||
|
||||
/** 从服务端拉取档案并与本地合并(以服务端为准) */
|
||||
function syncFromServer() {
|
||||
if (!isLoggedIn()) return Promise.resolve(getProfiles());
|
||||
return request({ url: '/api/user/birth-profiles' })
|
||||
.then(res => {
|
||||
if (!res || res.code !== 0 || !Array.isArray(res.data && res.data.profiles)) {
|
||||
return getProfiles();
|
||||
}
|
||||
const serverList = res.data.profiles.map(sp => ({
|
||||
id: `server-${sp.id}`,
|
||||
serverId: sp.id,
|
||||
name: sp.name || '未命名',
|
||||
gender: sp.gender === 2 ? 'female' : 'male',
|
||||
birthday: sp.birthday,
|
||||
birthTime: sp.birthTime,
|
||||
createdAt: sp.createdAt ? new Date(sp.createdAt).getTime() : 0
|
||||
}));
|
||||
// 本地尚未成功推送的档案保留,按 (birthday+birthTime) 去重
|
||||
const merged = [...serverList];
|
||||
getProfiles().forEach(lp => {
|
||||
const exists = merged.some(m => m.birthday === lp.birthday && m.birthTime === lp.birthTime);
|
||||
if (!exists) merged.push(lp);
|
||||
});
|
||||
saveProfiles(merged.slice(0, MAX_PROFILES));
|
||||
return getProfiles();
|
||||
})
|
||||
.catch(() => getProfiles());
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MAX_PROFILES,
|
||||
getProfiles,
|
||||
getActiveProfile,
|
||||
setActiveProfile,
|
||||
addProfile,
|
||||
removeProfile,
|
||||
isLoggedIn,
|
||||
pushProfileToServer,
|
||||
removeProfileFromServer,
|
||||
syncFromServer
|
||||
};
|
||||
Reference in New Issue
Block a user