在这里插入图片描述

📖 引言

功能守卫组件是知识问答学习应用中控制功能访问权限的核心组件,它需要根据用户等级、VIP状态或其他条件来决定是否允许访问某个功能,同时提供友好的提示和引导。

本文将详细讲解功能守卫组件的设计与实现,包括权限控制、状态管理、提示引导和样式定制。通过本文,你将掌握:

  • 如何设计可复用的功能守卫组件
  • 如何实现灵活的权限控制逻辑
  • 如何展示权限不足的提示信息
  • 如何优化组件的交互体验

🎯 学习目标

完成本文后,你将能够:

  • ✅ 理解功能守卫组件的核心功能和设计思路
  • ✅ 实现功能守卫的完整布局和样式
  • ✅ 处理灵活的权限控制逻辑
  • ✅ 实现权限引导和升级提示

💡 需求分析

功能模块设计

模块 功能描述 技术要点
权限检查 验证用户是否有权限 条件判断、状态管理
内容渲染 根据权限渲染内容 条件渲染、插槽机制
引导提示 权限不足时的提示 弹窗提示、升级引导
样式定制 支持自定义样式 属性参数、样式配置
状态管理 管理权限状态 AppStorage、响应式

🛠️ 核心实现

步骤1:功能守卫组件整体设计

功能说明

设计功能守卫组件的整体结构,包括权限检查、内容渲染和引导提示。

完整代码
// components/FeatureGuard/FeatureGuard.ets

import router from '@ohos.router';

// 权限类型枚举
enum PermissionType {
  VIP = 'vip',
  LEVEL = 'level',
  SIGN_IN = 'sign_in',
  QUIZ_COMPLETED = 'quiz_completed',
  ACHIEVEMENT = 'achievement'
}

// 守卫配置接口
interface GuardConfig {
  type: PermissionType;
  requiredValue?: number;
  title?: string;
  description?: string;
  buttonText?: string;
  buttonAction?: string;
}

@Component
export struct FeatureGuard {
  @State config: GuardConfig = { type: PermissionType.VIP };
  @State hasPermission: boolean = false;
  @State userLevel: number = 1;
  @State isVip: boolean = false;
  @State signInDays: number = 0;
  @State completedQuizzes: number = 0;

  /**
   * 组件初始化
   */
  aboutToAppear() {
    this.loadUserStatus();
    this.checkPermission();
  }

  /**
   * 加载用户状态
   */
  loadUserStatus(): void {
    // 实际项目中从服务或AppStorage获取
    this.userLevel = AppStorage.Get('userLevel', 1);
    this.isVip = AppStorage.Get('isVip', false);
    this.signInDays = AppStorage.Get('signInDays', 0);
    this.completedQuizzes = AppStorage.Get('completedQuizzes', 0);
  }

  /**
   * 检查权限
   */
  checkPermission(): void {
    switch (this.config.type) {
      case PermissionType.VIP:
        this.hasPermission = this.isVip;
        break;
      case PermissionType.LEVEL:
        this.hasPermission = this.userLevel >= (this.config.requiredValue || 1);
        break;
      case PermissionType.SIGN_IN:
        this.hasPermission = this.signInDays >= (this.config.requiredValue || 1);
        break;
      case PermissionType.QUIZ_COMPLETED:
        this.hasPermission = this.completedQuizzes >= (this.config.requiredValue || 1);
        break;
      case PermissionType.ACHIEVEMENT:
        this.hasPermission = this.hasAchievement(this.config.requiredValue || 0);
        break;
      default:
        this.hasPermission = true;
    }
  }

  /**
   * 检查是否拥有成就
   */
  hasAchievement(achievementId: number): boolean {
    const achievements = AppStorage.Get('achievements', []);
    return achievements.includes(achievementId);
  }

  build() {
    Column() {
      if (this.hasPermission) {
        // 有权限:渲染子内容
        this.RenderContent()
      } else {
        // 无权限:渲染引导提示
        this.RenderGuard()
      }
    }
  }

  /**
   * 渲染有权限的内容
   */
  @Builder
  RenderContent() {
    Column() {
      // 使用插槽渲染子组件
      Slot()
    }
  }

  /**
   * 渲染无权限的引导
   */
  @Builder
  RenderGuard() {
    Column({ space: 12 }) {
      // 图标
      Stack({ alignContent: Alignment.Center }) {
        Circle()
          .width(64)
          .height(64)
          .fill('#FFF8E1')

        Image(this.getGuardIcon())
          .width(32)
          .height(32)
          .fillColor('#FF9800')
      }

      // 标题和描述
      Column({ space: 4 }) {
        Text(this.config.title || '功能未解锁')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333')
          .textAlign(TextAlign.Center)

        Text(this.getGuardDescription())
          .fontSize(14)
          .fontColor('#999')
          .textAlign(TextAlign.Center)
          .maxLines(3)
      }

      // 操作按钮
      Button(this.config.buttonText || '立即解锁')
        .width('80%')
        .height(44)
        .backgroundColor('#FF9800')
        .fontColor('#fff')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .borderRadius(22)
        .onClick(() => {
          this.handleUnlockClick();
        })
    }
    .width('100%')
    .padding(24)
    .backgroundColor('#fafafa')
    .borderRadius(16)
    .alignItems(HorizontalAlign.Center)
  }

  /**
   * 获取守卫图标
   */
  getGuardIcon(): string {
    switch (this.config.type) {
      case PermissionType.VIP:
        return 'https://example.com/icons/vip.png';
      case PermissionType.LEVEL:
        return 'https://example.com/icons/level.png';
      case PermissionType.SIGN_IN:
        return 'https://example.com/icons/calendar.png';
      case PermissionType.QUIZ_COMPLETED:
        return 'https://example.com/icons/checklist.png';
      case PermissionType.ACHIEVEMENT:
        return 'https://example.com/icons/achievement.png';
      default:
        return 'https://example.com/icons/lock.png';
    }
  }

  /**
   * 获取守卫描述
   */
  getGuardDescription(): string {
    if (this.config.description) {
      return this.config.description;
    }

    switch (this.config.type) {
      case PermissionType.VIP:
        return '开通VIP会员即可解锁此功能';
      case PermissionType.LEVEL:
        return `需要达到${this.config.requiredValue}级才能解锁`;
      case PermissionType.SIGN_IN:
        return `连续签到${this.config.requiredValue}天即可解锁`;
      case PermissionType.QUIZ_COMPLETED:
        return `完成${this.config.requiredValue}次测验即可解锁`;
      case PermissionType.ACHIEVEMENT:
        return '完成特定成就即可解锁';
      default:
        return '此功能尚未解锁';
    }
  }

  /**
   * 处理解锁按钮点击
   */
  handleUnlockClick(): void {
    if (this.config.buttonAction) {
      router.pushUrl({ url: this.config.buttonAction });
    } else {
      switch (this.config.type) {
        case PermissionType.VIP:
          router.pushUrl({ url: 'pages/VIP/VIP' });
          break;
        case PermissionType.LEVEL:
          router.pushUrl({ url: 'pages/Learn/Learn' });
          break;
        case PermissionType.SIGN_IN:
          router.pushUrl({ url: 'pages/Home/Home' });
          break;
        default:
          promptAction.showToast({ message: '请先满足解锁条件' });
      }
    }
  }

  /**
   * 刷新权限状态
   */
  refreshPermission(): void {
    this.loadUserStatus();
    this.checkPermission();
  }

  /**
   * 设置用户等级
   */
  setUserLevel(level: number): void {
    this.userLevel = level;
    AppStorage.SetOrCreate('userLevel', level);
    this.checkPermission();
  }

  /**
   * 设置VIP状态
   */
  setVipStatus(isVip: boolean): void {
    this.isVip = isVip;
    AppStorage.SetOrCreate('isVip', isVip);
    this.checkPermission();
  }
}
代码解析

1. 组件布局结构

┌─────────────────────────┐
│ ●图标                    │  权限图标
│ 功能未解锁               │  标题
│ 需要达到5级才能解锁       │  描述
│ [立即解锁]               │  按钮
└─────────────────────────┘

2. 核心功能说明

  • 权限检查:支持VIP、等级、签到、测验完成、成就等多种权限类型
  • 内容渲染:有权限渲染子组件,无权限渲染引导提示
  • 引导提示:显示解锁条件和操作按钮
  • 状态管理:使用AppStorage管理用户状态
  • 灵活配置:支持自定义标题、描述和按钮动作

步骤2:权限检查实现

功能说明

实现灵活的权限检查逻辑,支持多种权限类型。

完整代码
// 权限类型枚举
enum PermissionType {
  VIP = 'vip',
  LEVEL = 'level',
  SIGN_IN = 'sign_in',
  QUIZ_COMPLETED = 'quiz_completed',
  ACHIEVEMENT = 'achievement'
}

/**
 * 检查权限
 */
checkPermission(): void {
  switch (this.config.type) {
    case PermissionType.VIP:
      this.hasPermission = this.isVip;
      break;
    case PermissionType.LEVEL:
      this.hasPermission = this.userLevel >= (this.config.requiredValue || 1);
      break;
    case PermissionType.SIGN_IN:
      this.hasPermission = this.signInDays >= (this.config.requiredValue || 1);
      break;
    case PermissionType.QUIZ_COMPLETED:
      this.hasPermission = this.completedQuizzes >= (this.config.requiredValue || 1);
      break;
    case PermissionType.ACHIEVEMENT:
      this.hasPermission = this.hasAchievement(this.config.requiredValue || 0);
      break;
    default:
      this.hasPermission = true;
  }
}
代码解析
  • VIP权限:检查用户是否是VIP会员
  • 等级权限:检查用户等级是否达到要求
  • 签到权限:检查连续签到天数是否达到要求
  • 测验权限:检查完成测验数量是否达到要求
  • 成就权限:检查是否拥有特定成就

⚠️ 常见问题与解决方案

问题1:权限检查不生效

现象
权限配置后没有正确检查权限。

错误代码

// ❌ 错误:没有调用检查方法
aboutToAppear() {
  this.loadUserStatus();
}

正确代码

// ✅ 正确:调用检查方法
aboutToAppear() {
  this.loadUserStatus();
  this.checkPermission();
}

规则/建议

  • 在加载用户状态后调用检查方法
  • 使用响应式状态管理
  • 确保权限检查在渲染前完成

问题2:子组件不渲染

现象
有权限时子组件没有渲染。

错误代码

// ❌ 错误:没有使用Slot
@Builder
RenderContent() {
  Column() {
    // 空实现
  }
}

正确代码

// ✅ 正确:使用Slot
@Builder
RenderContent() {
  Column() {
    Slot()
  }
}

规则/建议

  • 使用Slot组件渲染子内容
  • 确保Slot在正确的位置
  • 支持多个插槽

问题3:解锁按钮跳转错误

现象
点击解锁按钮后跳转到错误的页面。

错误代码

// ❌ 错误:固定跳转
handleUnlockClick(): void {
  router.pushUrl({ url: 'pages/VIP/VIP' });
}

正确代码

// ✅ 正确:根据权限类型跳转
handleUnlockClick(): void {
  if (this.config.buttonAction) {
    router.pushUrl({ url: this.config.buttonAction });
  } else {
    switch (this.config.type) {
      case PermissionType.VIP:
        router.pushUrl({ url: 'pages/VIP/VIP' });
        break;
      case PermissionType.LEVEL:
        router.pushUrl({ url: 'pages/Learn/Learn' });
        break;
      default:
        promptAction.showToast({ message: '请先满足解锁条件' });
    }
  }
}

规则/建议

  • 优先使用配置的按钮动作
  • 根据权限类型跳转到对应的页面
  • 提供友好的提示信息

问题4:用户状态不更新

现象
用户状态变化后权限没有重新检查。

错误代码

// ❌ 错误:没有刷新权限
setUserLevel(level: number): void {
  this.userLevel = level;
}

正确代码

// ✅ 正确:刷新权限
setUserLevel(level: number): void {
  this.userLevel = level;
  AppStorage.SetOrCreate('userLevel', level);
  this.checkPermission();
}

规则/建议

  • 更新状态后重新检查权限
  • 使用AppStorage同步状态
  • 确保响应式更新

问题5:引导描述不清晰

现象
权限不足时的提示信息不明确。

错误代码

// ❌ 错误:固定描述
Text('此功能尚未解锁')

正确代码

// ✅ 正确:动态描述
getGuardDescription(): string {
  if (this.config.description) {
    return this.config.description;
  }

  switch (this.config.type) {
    case PermissionType.LEVEL:
      return `需要达到${this.config.requiredValue}级才能解锁`;
    case PermissionType.SIGN_IN:
      return `连续签到${this.config.requiredValue}天即可解锁`;
    // ... 其他类型
  }
}

Text(this.getGuardDescription())

规则/建议

  • 使用动态描述
  • 显示具体的解锁条件
  • 提供清晰的引导信息

📝 本章小结

核心知识点

本文详细讲解了功能守卫组件的设计与实现,主要包括:

1. 组件布局设计

  • 条件渲染(有权限/无权限)
  • 引导提示布局(图标、标题、描述、按钮)
  • 圆角卡片设计(borderRadius: 16)

2. 核心功能实现

  • 权限检查(VIP、等级、签到、测验、成就)
  • 内容渲染(Slot插槽)
  • 状态管理(AppStorage)
  • 引导跳转(按钮事件处理)

3. 交互体验优化

  • 动态描述(根据权限类型)
  • 图标区分(不同权限类型不同图标)
  • 灵活配置(自定义标题、描述、按钮)

最佳实践总结

权限检查

checkPermission(): void {
  switch (this.config.type) {
    case PermissionType.VIP:
      this.hasPermission = this.isVip;
      break;
    case PermissionType.LEVEL:
      this.hasPermission = this.userLevel >= (this.config.requiredValue || 1);
      break;
    case PermissionType.SIGN_IN:
      this.hasPermission = this.signInDays >= (this.config.requiredValue || 1);
      break;
    // ... 其他类型
  }
}

条件渲染

build() {
  Column() {
    if (this.hasPermission) {
      this.RenderContent()
    } else {
      this.RenderGuard()
    }
  }
}

插槽使用

@Builder
RenderContent() {
  Column() {
    Slot()
  }
}

动态描述

getGuardDescription(): string {
  if (this.config.description) {
    return this.config.description;
  }

  switch (this.config.type) {
    case PermissionType.LEVEL:
      return `需要达到${this.config.requiredValue}级才能解锁`;
    // ... 其他类型
  }
}

下一步预告

在下一篇文章中,我们将完成公共组件设计系列,讲解以下内容:

  • 📊 ChartComponents 图表组件封装:数据可视化组件

🔗 相关链接


💡 提示:功能守卫组件是控制功能访问权限的重要组件,建议结合用户等级和VIP状态动态调整功能解锁条件,提供良好的用户体验。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐