feat(vip): 限制底部栏自定义功能为VIP专享
非VIP用户尝试配置底部栏时将显示会员购买弹窗, 只有VIP会员才能自由开启或关闭导航标签。 包含会员权益说明的国际化支持和存储结构重构。
This commit is contained in:
@@ -9,10 +9,17 @@ export interface TabConfig {
|
||||
icon: string; // SF Symbol 图标名
|
||||
titleKey: string; // i18n 翻译 key
|
||||
enabled: boolean; // 是否启用
|
||||
canBeDisabled: boolean; // 是否可以被禁用
|
||||
canBeDisabled: boolean; // 是否可以被禁用(系统级配置,不持久化)
|
||||
order: number; // 显示顺序
|
||||
}
|
||||
|
||||
// 用户可持久化的配置(只包含用户可控制的属性)
|
||||
interface UserTabConfig {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
order: number;
|
||||
}
|
||||
|
||||
// State 接口
|
||||
interface TabBarConfigState {
|
||||
configs: TabConfig[];
|
||||
@@ -34,7 +41,7 @@ export const DEFAULT_TAB_CONFIGS: TabConfig[] = [
|
||||
icon: 'pills.fill',
|
||||
titleKey: 'statistics.tabs.medications',
|
||||
enabled: true,
|
||||
canBeDisabled: false,
|
||||
canBeDisabled: true, // 用药管理可以被关闭
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
@@ -42,7 +49,7 @@ export const DEFAULT_TAB_CONFIGS: TabConfig[] = [
|
||||
icon: 'timer',
|
||||
titleKey: 'statistics.tabs.fasting',
|
||||
enabled: true,
|
||||
canBeDisabled: true, // 只有断食可以被关闭
|
||||
canBeDisabled: true, // 断食可以被关闭
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
@@ -50,7 +57,7 @@ export const DEFAULT_TAB_CONFIGS: TabConfig[] = [
|
||||
icon: 'trophy.fill',
|
||||
titleKey: 'statistics.tabs.challenges',
|
||||
enabled: true,
|
||||
canBeDisabled: false,
|
||||
canBeDisabled: true, // 挑战可以被关闭
|
||||
order: 4,
|
||||
},
|
||||
{
|
||||
@@ -120,10 +127,16 @@ const tabBarConfigSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
// 持久化配置到 AsyncStorage
|
||||
// 持久化配置到 AsyncStorage(只保存用户可控制的属性)
|
||||
const saveConfigsToStorage = async (configs: TabConfig[]) => {
|
||||
try {
|
||||
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(configs));
|
||||
// 只保存用户可控制的属性:enabled 和 order
|
||||
const userConfigs: UserTabConfig[] = configs.map(config => ({
|
||||
id: config.id,
|
||||
enabled: config.enabled,
|
||||
order: config.order,
|
||||
}));
|
||||
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(userConfigs));
|
||||
logger.info('底部栏配置已保存');
|
||||
} catch (error) {
|
||||
logger.error('保存底部栏配置失败:', error);
|
||||
@@ -136,12 +149,12 @@ export const loadTabBarConfigs = () => async (dispatch: any) => {
|
||||
const stored = await AsyncStorage.getItem(STORAGE_KEY);
|
||||
|
||||
if (stored) {
|
||||
const configs = JSON.parse(stored) as TabConfig[];
|
||||
const userConfigs = JSON.parse(stored) as UserTabConfig[];
|
||||
|
||||
// 验证配置有效性
|
||||
if (Array.isArray(configs) && configs.length > 0) {
|
||||
// 合并默认配置,确保新增的 tab 也能显示
|
||||
const mergedConfigs = mergeWithDefaults(configs);
|
||||
if (Array.isArray(userConfigs) && userConfigs.length > 0) {
|
||||
// 合并用户配置和默认配置
|
||||
const mergedConfigs = mergeWithDefaults(userConfigs);
|
||||
dispatch(setConfigs(mergedConfigs));
|
||||
logger.info('底部栏配置已加载');
|
||||
return;
|
||||
@@ -159,15 +172,24 @@ export const loadTabBarConfigs = () => async (dispatch: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 合并存储的配置和默认配置
|
||||
const mergeWithDefaults = (storedConfigs: TabConfig[]): TabConfig[] => {
|
||||
const merged = [...storedConfigs];
|
||||
// 合并用户配置和默认配置
|
||||
const mergeWithDefaults = (userConfigs: UserTabConfig[]): TabConfig[] => {
|
||||
const merged: TabConfig[] = [];
|
||||
|
||||
// 检查是否有新增的默认 tab
|
||||
// 遍历默认配置,将用户的 enabled 和 order 合并进来
|
||||
DEFAULT_TAB_CONFIGS.forEach(defaultConfig => {
|
||||
const exists = merged.find(c => c.id === defaultConfig.id);
|
||||
if (!exists) {
|
||||
// 新增的 tab,添加到末尾
|
||||
const userConfig = userConfigs.find(c => c.id === defaultConfig.id);
|
||||
|
||||
if (userConfig) {
|
||||
// 合并:系统配置(icon, titleKey, canBeDisabled)从默认配置读取
|
||||
// 用户配置(enabled, order)从用户配置读取
|
||||
merged.push({
|
||||
...defaultConfig,
|
||||
enabled: userConfig.enabled,
|
||||
order: userConfig.order,
|
||||
});
|
||||
} else {
|
||||
// 新增的 tab,使用默认配置
|
||||
merged.push({
|
||||
...defaultConfig,
|
||||
order: merged.length + 1,
|
||||
|
||||
Reference in New Issue
Block a user