refactor(mini): 个人中心改为登录+二级菜单结构
Publish Mini Program Dev Version / publish (push) Successful in 1m8s

- 个人中心仅保留登录管理、我的数据/功能菜单与关于信息
- 新增偏好设置二级页 preferences(原偏好设置统一收敛至此)
- 新增我的收藏二级页 bookmarks,支持删除与跳转日详情
- 生辰管理拆分为二级页 birth-profiles,未登录时提示引导登录
- 八字页入口改为直达生辰管理页;修复 fortune 非 tabBar 页误用 switchTab
This commit is contained in:
gouki
2026-08-10 11:52:15 +00:00
parent f7d88622e0
commit 87f5fa2478
17 changed files with 508 additions and 347 deletions
+108
View File
@@ -0,0 +1,108 @@
const birthProfile = require('../../utils/birth-profile.js');
Page({
data: {
loggedIn: false,
profiles: [],
activeProfileId: '',
maxFree: birthProfile.MAX_PROFILES,
canAdd: true,
showAddForm: false,
formName: '',
formGender: 'male',
formDate: '1990-01-01',
formTime: '12:00'
},
onShow() {
this.setData({ loggedIn: birthProfile.isLoggedIn() });
this.loadProfiles();
},
loadProfiles() {
const render = () => {
const profiles = birthProfile.getProfiles();
const active = birthProfile.getActiveProfile();
this.setData({
profiles,
activeProfileId: active ? active.id : '',
canAdd: profiles.length < this.data.maxFree
});
};
// 已登录时先从服务端同步,失败静默回退本地
if (birthProfile.isLoggedIn()) {
birthProfile.syncFromServer().then(render).catch(render);
} else {
render();
}
},
toggleAddForm() {
if (!this.data.showAddForm && !this.data.canAdd) {
wx.showToast({ title: `最多可免费绑定 ${this.data.maxFree} 个生辰`, icon: 'none' });
return;
}
this.setData({ showAddForm: !this.data.showAddForm });
},
onFormName(e) {
this.setData({ formName: e.detail.value });
},
setFormGender(e) {
this.setData({ formGender: e.currentTarget.dataset.g });
},
onFormDate(e) {
this.setData({ formDate: e.detail.value });
},
onFormTime(e) {
this.setData({ formTime: e.detail.value });
},
submitProfile() {
const { formName, formGender, formDate, formTime } = this.data;
if (!formDate || !formTime) {
wx.showToast({ title: '请选择出生日期和时间', icon: 'none' });
return;
}
const result = birthProfile.addProfile({
name: formName.trim() || '未命名',
gender: formGender,
birthday: formDate,
birthTime: formTime
});
if (!result.ok) {
wx.showToast({ title: result.msg, icon: 'none' });
return;
}
// 已登录时推送到服务端(静默失败,不影响本地)
birthProfile.pushProfileToServer(result.profile);
this.setData({ showAddForm: false, formName: '' });
wx.showToast({ title: '绑定成功', icon: 'success' });
this.loadProfiles();
},
removeProfile(e) {
const id = e.currentTarget.dataset.id;
const profile = this.data.profiles.find(p => p.id === id);
if (!profile) return;
wx.showModal({
title: '删除生辰',
content: `确定删除「${profile.name}」的生辰信息吗?`,
success: (res) => {
if (!res.confirm) return;
birthProfile.removeProfile(id);
birthProfile.removeProfileFromServer(profile);
wx.showToast({ title: '已删除', icon: 'success' });
this.loadProfiles();
}
});
},
// 未登录时引导回个人中心登录
goLogin() {
wx.navigateBack();
}
});
@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "生辰管理",
"enablePullDownRefresh": false
}
@@ -0,0 +1,70 @@
<view class="container">
<!-- 未登录提示 -->
<view class="card login-tip" wx:if="{{!loggedIn}}">
<text class="text-sm">当前未登录,生辰仅保存在本机</text>
<text class="text-xs text-muted block mt-1">登录后可云端同步,换设备不丢失</text>
<view class="btn-outline text-center mt-2" bindtap="goLogin">去登录</view>
</view>
<!-- 生辰管理 -->
<view class="card">
<view class="section-title flex justify-between items-center">
<text>生辰管理</text>
<text class="text-xs text-muted">{{profiles.length}}/{{maxFree}}</text>
</view>
<view class="text-xs text-muted mb-2">八字排盘基于生辰信息,每位用户可免费绑定 {{maxFree}} 个出生年月(如本人、家人)。</view>
<view class="profile-list" wx:if="{{profiles.length > 0}}">
<view class="profile-item" wx:for="{{profiles}}" wx:key="id">
<view class="flex justify-between items-center">
<view>
<view class="flex items-center gap-1">
<text class="text-base font-medium">{{item.name}}</text>
<text class="badge badge-lucky" wx:if="{{item.id === activeProfileId}}">使用中</text>
</view>
<text class="text-xs text-muted block">{{item.gender === 'female' ? '女' : '男'}} · {{item.birthday}} {{item.birthTime}}</text>
</view>
<view class="btn-ghost text-unlucky" bindtap="removeProfile" data-id="{{item.id}}">删除</view>
</view>
</view>
</view>
<view class="text-center text-muted p-2" wx:else>
<text>暂未绑定生辰,请到下方添加</text>
</view>
<view class="btn-outline text-center mt-2" bindtap="toggleAddForm" wx:if="{{canAdd}}">
{{showAddForm ? '收起' : '+ 添加生辰'}}
</view>
<view class="text-center text-xs text-muted mt-2" wx:else>
免费名额已用完,可删除旧档案后添加
</view>
<!-- 添加表单 -->
<block wx:if="{{showAddForm}}">
<view class="form-item mt-2">
<text class="form-label">备注名(如:本人、爸爸)</text>
<input class="form-input" placeholder="未命名" value="{{formName}}" bindinput="onFormName"/>
</view>
<view class="form-item">
<text class="form-label">性别</text>
<view class="flex gap-2">
<view class="btn-outline {{formGender === 'male' ? 'active' : ''}}" bindtap="setFormGender" data-g="male">男</view>
<view class="btn-outline {{formGender === 'female' ? 'active' : ''}}" bindtap="setFormGender" data-g="female">女</view>
</view>
</view>
<view class="form-item">
<text class="form-label">出生日期(公历)</text>
<picker mode="date" value="{{formDate}}" start="1900-01-01" end="2100-12-31" bindchange="onFormDate">
<view class="picker-input">{{formDate}}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">出生时间</text>
<picker mode="time" value="{{formTime}}" bindchange="onFormTime">
<view class="picker-input">{{formTime}}</view>
</picker>
</view>
<view class="btn-primary text-center" bindtap="submitProfile">保存生辰</view>
</block>
</view>
</view>
@@ -0,0 +1,56 @@
.container {
padding: 20rpx;
}
.section-title {
font-size: 28rpx;
font-weight: 600;
margin-bottom: 16rpx;
color: #C41E3A;
}
.login-tip {
border: 1rpx dashed #C41E3A;
}
/* 生辰档案 */
.profile-item {
padding: 16rpx 0;
border-bottom: 1rpx solid #E8D5C4;
}
.profile-item:last-child {
border-bottom: none;
}
/* 添加生辰表单 */
.form-item {
margin-bottom: 24rpx;
}
.form-label {
font-size: 24rpx;
color: #6B5E53;
margin-bottom: 8rpx;
display: block;
}
.form-input {
padding: 20rpx;
background: #FFFBF5;
border-radius: 12rpx;
border: 1rpx solid #E8D5C4;
}
.picker-input {
padding: 20rpx;
background: #FFFBF5;
border-radius: 12rpx;
border: 1rpx solid #E8D5C4;
}
.btn-outline.active {
background: #C41E3A;
color: #FFFFFF;
border-color: #C41E3A;
}