9.1 发布表单预览

  @Builder createPostFormPreview() {
    Column() {
      Text('发布动态')
        .fontSize(16)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')

      Row() {
        Column()
          .width(40).height(40)
          .backgroundColor('#7B1FA2')
          .borderRadius(20)

        Column() {
          Text(currentUserName())
            .fontSize(13)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Text('选择你的宠物')
            .fontSize(11)
            .fontColor('#999999')
        }
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 10 })
        .layoutWeight(1)
      }
      .margin({ top: 12 })

      Column()
        .width('100%')
        .height(120)
        .backgroundColor('#F5F5F5')
        .borderRadius(10)
        .margin({ top: 10 })

      Row() {
        Column() {
          Text('📷')
            .fontSize(20)
          Text('图片')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)
        .margin({ right: 20 })

        Column() {
          Text('#️⃣ ')
            .fontSize(20)
          Text('话题')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)
        .margin({ right: 20 })

        Column() {
          Text('📍')
            .fontSize(20)
          Text('位置')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)

        Column().layoutWeight(1)

        Row() {
          Text('发布')
            .fontSize(13)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Medium)
        }
        .padding({ left: 20, right: 20, top: 8, bottom: 8 })
        .backgroundColor('#7B1FA2')
        .borderRadius(20)
      }
      .width('100%')
      .margin({ top: 10 })
    }
    .width('100%')
    .padding(16)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, top: 12, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

在这里插入图片描述

createPostFormPreview 是发布页顶部的动态创建表单,虽然当前为预览性质(无实际输入框),但完整模拟了发布流程的视觉布局。表单分为四个区域:标题"发布动态"、用户信息栏(头像 + 用户名 + 宠物选择提示)、内容占位区(120px 高的灰色圆角矩形,模拟图文编辑区域)、以及底部工具栏。用户信息栏中,用户名通过 currentUserName() 函数动态获取,而非写死,体现了数据驱动的思想。底部工具栏包含三个功能入口(图片、话题、位置),使用 Emoji 图标 + 小标签的垂直排列方式,右边距 20px 保证间距均匀。最右侧的"发布"按钮使用紫色实心填充、圆角 20px、白色文字,在视觉上作为首要操作(Primary Action)非常突出。三个功能入口与发布按钮之间通过 Column().layoutWeight(1) 的弹簧效果实现右对齐。整个卡片采用标准的白色背景、12px 圆角、轻微阴影和 16px 水平外边距,与其他卡片保持视觉统一。

9.2 草稿箱模块

  @Builder draftItem(item: PostModel) {
    Row() {
      Column()
        .width(48).height(48)
        .backgroundColor(item.imageColors[0] ?? '#F5F5F5')
        .borderRadius(8)

      Column() {
        Text(item.content)
          .fontSize(12)
          .fontColor('#555555')
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
        Row() {
          if (item.tags.length > 0) {
            ForEach(item.tags, (tag: string, idx: number) => {
              Text(tag)
                .fontSize(8)
                .fontColor(getTagColor(tag))
                .padding({ left: 6, right: 6, top: 1, bottom: 1 })
                .backgroundColor(getTagColor(tag) + '1A')
                .borderRadius(6)
                .margin({ right: 4 })
            })
          }
        }
        .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      Text('编辑')
        .fontSize(11)
        .fontColor('#7B1FA2')
        .fontWeight(FontWeight.Medium)
    }
    .width('100%')
    .padding({ top: 10, bottom: 10 })
  }

在这里插入图片描述

draftItem 渲染草稿箱中的单条草稿。布局为左中右三栏:左侧是 48x48 的缩略图,使用 item.imageColors[0] ?? '#F5F5F5' 获取首个色值,若不存在则回退到灰色,8px 圆角;中间区域显示内容摘要(单行省略)和标签列表,通过 layoutWeight(1) 占据剩余空间;右侧是"编辑"文本按钮,紫色中等粗细。标签渲染复用了 getTagColor 函数,但字号缩小到 8px、内边距和圆角也相应缩小,适应草稿项的紧凑空间。整个项的垂直内边距为 10px,形成了合适的点击热区。

  @Builder draftSection() {
    Column() {
      Text('草稿箱 (4)')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      this.draftItem(draftPosts[0])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[1])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[2])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[3])
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

在这里插入图片描述

draftSection 是草稿箱卡片容器,标题"草稿箱 (4)"硬编码了数量,实际项目中应动态计算 draftPosts.length。四条草稿项之间使用 0.5px 的浅灰色 Divider 分隔,形成清晰的分割线。整体视觉风格与其他卡片一致。

9.3 发布小贴士与互动统计

  @Builder postingTips() {
    Column() {
      Text('发布小贴士')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      Column() {
        Row() {
          Text('💡')
            .fontSize(16)
            .margin({ right: 8 })
          Text('配上清晰可爱的照片更容易获得关注哦')
            .fontSize(11)
            .fontColor('#666666')
            .layoutWeight(1)
        }
      }
      .width('100%')
      .padding({ top: 6, bottom: 6 })
      // ... 共3条提示
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

在这里插入图片描述

postingTips 是发布页的帮助提示模块,包含三条发布建议。每条提示使用 Row 水平排列 Emoji 图标和文本,图标固定宽度,文本通过 layoutWeight(1) 占据剩余空间。提示内容被包裹在独立的 Column 中,并设置上下内边距 6px,形成合适的行高。这种设计将运营文案与代码解耦,便于后续修改为从配置或后端接口动态获取。

  @Builder postTab() {
    Scroll() {
      Column() {
        this.createPostFormPreview()
        this.draftSection()
        this.postingTips()

        Text('最近发布统计')
          .fontSize(14)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .padding({ left: 16, right: 16, top: 8, bottom: 6 })

        Row() {
          ForEach(engagementData, (item: EngagementStats, index: number) => {
            Column() {
              Text(item.icon)
                .fontSize(22)
              Text(item.value.toString())
                .fontSize(18)
                .fontColor(item.color)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 4 })
              Text(item.type)
                .fontSize(10)
                .fontColor('#999999')
                .margin({ top: 2 })
            }
            .alignItems(HorizontalAlign.Center)
            .layoutWeight(1)
          })
        }
        .width('100%')
        .padding(14)
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .margin({ left: 16, right: 16, bottom: 8 })
        .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

在这里插入图片描述

postTab 是发布页的完整组装体,包含:发布表单 → 草稿箱 → 发布小贴士 → "最近发布统计"标题 → 互动数据卡片 → 底部占位。互动统计卡片使用 ForEach 循环渲染 engagementData 数组,每项包含 Emoji 图标、数值和类型标签,三项(获赞、评论、分享)通过 layoutWeight(1) 均分整行宽度。数值使用动态颜色(item.color)和粗体大字号(18px)突出显示,类型标签使用灰色小字号。发布页的设计体现了"创作 + 管理 + 洞察"的三位一体:顶部表单鼓励创作,草稿箱管理未完成内容,底部统计数据给予用户正向反馈,形成完整的内容生产闭环。


十、消息页:社交互动的集中枢纽

10.1 聊天会话项

  @Builder chatItem(item: ChatModel) {
    Row() {
      Stack() {
        Column()
          .width(44).height(44)
          .backgroundColor(item.petAvatarColor)
          .borderRadius(22)
        if (item.isOnline) {
          Column()
            .width(10).height(10)
            .backgroundColor('#4CAF50')
            .borderRadius(5)
            .border({ width: 2, color: '#FFFFFF' })
            .margin({ left: 34, top: 34 })
        }
      }

      Column() {
        Row() {
          Text(item.petName)
            .fontSize(14)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Column().layoutWeight(1)
          Text(item.timestamp)
            .fontSize(10)
            .fontColor('#CCCCCC')
        }
        .width('100%')

        Row() {
          Text(item.lastMessage)
            .fontSize(12)
            .fontColor('#999999')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .layoutWeight(1)
          if (item.unread > 0) {
            Column()
              .height(18)
              .backgroundColor('#F44336')
              .borderRadius(9)
              .justifyContent(FlexAlign.Center)
              .alignItems(HorizontalAlign.Center)
              .padding({ left: 5, right: 5 })
          }
        }
        .width('100%')
        .margin({ top: 3 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })
    }
    .width('100%')
    .padding({ top: 12, bottom: 12 })
  }

在这里插入图片描述

chatItem 是消息列表中的单个会话项,是消息页最核心的 UI 单元。它采用了经典的社交应用会话项布局:左侧头像区、中间信息区、右侧(内嵌于信息区)时间戳和未读角标。头像使用 Stack 堆叠布局实现双层效果:底层是 44x44 的圆形头像(borderRadius(22)),顶层是在线状态指示器——当 item.isOnlinetrue 时,渲染一个 10x10 的绿色小圆点(borderRadius(5)),并通过 margin({ left: 34, top: 34 }) 精确定位到头像右下角。小圆点的 2px 白色边框(border({ width: 2, color: '#FFFFFF' }))创造了与头像分离的切割感,这是在线指示器的标准设计模式。中间信息区分为上下两行:第一行是宠物名(14px 中等粗细)和时间戳(10px 浅灰),通过 layoutWeight(1) 将时间戳推到右侧;第二行是最后消息摘要(12px 灰色,单行省略)和未读角标。未读角标使用条件渲染,当 item.unread > 0 时显示一个高度 18px、圆角 9px(即正圆角胶囊)的红色背景块,内部可容纳具体数字(虽然当前代码中未添加数字文本,但结构已预留)。整个项的垂直内边距 12px,形成了舒适的点击热区。

10.2 通知模块

  @Builder notificationItem(icon: string, title: string, desc: string, time: string, color: string) {
    Row() {
      Column()
        .width(36).height(36)
        .backgroundColor(color + '1A')
        .borderRadius(18)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
      Column() {
        Text(title)
          .fontSize(12)
          .fontColor('#333333')
          .fontWeight(FontWeight.Medium)
        Text(desc)
          .fontSize(10)
          .fontColor('#999999')
          .margin({ top: 1 })
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      Text(time)
        .fontSize(9)
        .fontColor('#CCCCCC')
    }
    .width('100%')
    .padding({ top: 6, bottom: 6 })
  }

在这里插入图片描述

notificationItem 是通知列表的单项,采用与聊天项相似但更加紧凑的布局。左侧是 36x36 的圆形图标背景,使用 color + '1A' 实现同色系透明效果;中间是通知标题(12px 中等粗细)和描述(10px 灰色,单行省略);右侧是时间文本(9px 浅灰)。通知项的垂直内边距仅 6px,比聊天项的 12px 更紧凑,因为通知通常数量更多,需要更高的信息密度。通过参数化设计(icontitledesctimecolor),这个构建器可以复用于各类通知场景(点赞、评论、关注、系统推荐等)。

  @Builder notificationSection() {
    Column() {
      Text('通知')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      this.notificationItem('❤️', '毛毛妈赞了你的动态', '今天带毛毛去公园玩飞盘...', '5分钟前', '#EC407A')
      this.notificationItem('💬', '喵星人小宇评论了你', '布丁也太可爱了叭!!', '15分钟前', '#7B1FA2')
      this.notificationItem('👤', '铲屎官Linda关注了你', '你们可能认识', '30分钟前', '#FF6F00')
      this.notificationItem('⭐', '你的动态被推荐了', '阿福的小短腿上楼梯...', '1小时前', '#FFD700')
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

notificationSection 将四条不同类型的通知组装为卡片容器。四种通知分别使用了不同的 Emoji 图标和主题色(粉色点赞、紫色评论、橙色关注、黄色推荐),通过视觉色彩区分通知类型,帮助用户快速识别信息优先级。

10.3 消息页整体布局

  @Builder messagesTab() {
    Scroll() {
      Column() {
        Row() {
          Text('消息')
            .fontSize(18)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('+')
            .fontSize(22)
            .fontColor('#7B1FA2')
            .fontWeight(FontWeight.Bold)
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 12, bottom: 4 })

        Column() {
          this.chatItem(chatList[0])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[1])
          // ... 依次渲染 chatList[2] 到 [9],每项之间用 Divider 分隔
        }
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .margin({ left: 16, right: 16 })
        .width('100%')
        .constraintSize({ maxWidth: '100%' })

        this.notificationSection()

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

messagesTab 是消息页的完整组装体。顶部是标题栏"消息"和右侧的紫色加号按钮(发起新会话)。中间的聊天列表被包裹在一个白色圆角卡片中,10 条会话项之间使用 Divider 分隔,注意分隔线的左边距为 66px 而非 16px——这是为了与头像右侧对齐,创造视觉上的缩进效果,让分割线只出现在文字区域下方,而非贯穿整行,这是 iOS 风格列表的经典设计。.constraintSize({ maxWidth: '100%' }) 确保卡片宽度不会超出父容器。聊天列表卡片下方是通知区域,最后是底部占位。整个页面的垂直滚动由外层 Scroll 提供。


十一、个人中心页:用户身份与成就展示

11.1 用户资料头部

  @Builder userProfileHeader() {
    Column() {
      Row() {
        Column()
          .width(60).height(60)
          .backgroundColor('#7B1FA2')
          .borderRadius(30)

        Column() {
          Text('铲屎官小宇')
            .fontSize(18)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('ID: pet_lover_xiaoyu')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 12 })
        .layoutWeight(1)

        Text('›')
          .fontSize(24)
          .fontColor('#FFFFFF')
          .opacity(0.5)
      }
      .width('100%')

      Row() {
        Column() {
          Text(petProfiles.length.toString())
            .fontSize(16)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('宠物')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
        }
        .alignItems(HorizontalAlign.Center)
        .layoutWeight(1)
        // ... 动态、粉丝、关注 同理
      }
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
    .borderRadius(16)
    .linearGradient({
      angle: 135,
      colors: [['#7B1FA2', 0.0], ['#9C27B0', 0.5], ['#AB47BC', 1.0]]
    })
    .margin({ left: 16, right: 16, top: 12, bottom: 8 })
    .shadow({ radius: 8, color: '#1A000000', offsetY: 2 })
  }

userProfileHeader 是个人中心页顶部的用户资料卡片,采用了与 Feed 页横幅相同的紫色渐变主题,形成品牌视觉的一致性。卡片分为上下两部分:上半部分是用户信息行,左侧 60x60 的圆形头像占位(紫色背景),中间是用户名(18px 白色粗体)和用户 ID(10px 半透明白色),右侧是灰色的右箭头(›)暗示可点击进入详情页。下半部分是数据统计行,四个统计项(宠物数量、动态数量、粉丝数、关注数)通过 layoutWeight(1) 均分整行宽度,每个项垂直居中排列数值(16px 白色粗体)和标签(10px 半透明白色)。宠物数量使用了动态数据 petProfiles.length.toString(),其余三项为硬编码,模拟了真实应用中部分数据来自计算、部分来自后端接口的混合场景。卡片的圆角为 16px(比其他卡片的 12px 更大),阴影也更明显(半径 8px,颜色更深),使其在视觉上更加突出,形成页面的视觉焦点。

11.2 我的宠物与成就徽章

  @Builder myPetProfileCard(profile: PetProfileModel) {
    Row() {
      Column()
        .width(56).height(56)
        .backgroundColor(profile.avatarColor)
        .borderRadius(28)
        .border({ width: 3, color: '#7B1FA2' })

      Column() {
        Row() {
          Text(profile.name)
            .fontSize(16)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().width(6)
          Text(getPetTypeIcon(profile.type))
            .fontSize(14)
        }
        Text(profile.breed + ' · ' + profile.age + ' · ' + profile.gender)
          .fontSize(11)
          .fontColor('#999999')
          .margin({ top: 2 })
        Text(profile.bio)
          .fontSize(11)
          .fontColor('#666666')
          .margin({ top: 3 })
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })
    }
    .width('100%')
    .padding({ top: 8, bottom: 8 })
  }

myPetProfileCard 是"我的宠物"列表中的单项,与发现页的 nearbyPetItem 结构相似但信息更丰富。头像尺寸更大(56x56),同样有 3px 紫色边框。信息区包含三行:第一行是宠物名(16px 粗体)和类型 Emoji;第二行是品种、年龄和性别的组合文本,使用 “·” 作为分隔符;第三行是个性签名(11px 灰色,单行省略)。三行信息通过不同的字号和颜色形成清晰的视觉层级。

  @Builder achievementItem(item: AchievementData) {
    Row() {
      Column()
        .width(40).height(40)
        .backgroundColor(item.earned ? item.color + '1A' : '#F5F5F5')
        .borderRadius(20)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
        .opacity(item.earned ? 1.0 : 0.3)

      Column() {
        Text(item.title)
          .fontSize(13)
          .fontColor(item.earned ? '#333333' : '#CCCCCC')
          .fontWeight(FontWeight.Medium)
        Text(item.description)
          .fontSize(10)
          .fontColor(item.earned ? '#999999' : '#DDDDDD')
          .margin({ top: 1 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      if (item.earned) {
        Text('✓')
          .fontSize(16)
          .fontColor('#4CAF50')
          .fontWeight(FontWeight.Bold)
      }
    }
    .width('100%')
    .padding({ top: 6, bottom: 6 })
  }

achievementItem 是成就徽章的单项,展示了条件渲染在视觉状态差异化上的强大能力。未解锁成就(earned: false)时:背景色为灰色、不透明度 0.3、标题为浅灰色、描述为更浅的灰色,整体呈现置灰效果;已解锁成就时:背景色为同色系透明色、不透明度 1.0、标题为深灰色、描述为灰色,右侧还会显示绿色的"✓"标记。这种通过同一套数据驱动两种截然不同的视觉表现的能力,正是声明式 UI 的核心优势——开发者只需描述不同状态下的样式规则,框架自动处理状态切换时的过渡。

11.3 设置菜单与个人中心组装

  @Builder profileSettings() {
    Column() {
      Text('设置')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 6 })

      Row() {
        Text('个人资料')
          .fontSize(12)
          .fontColor('#555555')
          .layoutWeight(1)
        Text('›')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 8, bottom: 8 })
      // ... 隐私设置、通知管理、关于我们 同理
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

profileSettings 是设置菜单卡片,包含四个设置项:个人资料、隐私设置、通知管理、关于我们。每项使用 Row 布局,左侧文本通过 layoutWeight(1) 占据空间,右侧灰色右箭头暗示可点击进入下一级页面。设置项之间没有 Divider 分隔,而是通过上下内边距(8px)自然划分,这是一种更加现代的列表设计风格。

  @Builder profileTab() {
    Scroll() {
      Column() {
        this.userProfileHeader()
        this.myPetSection()
        this.achievementsSection()
        this.profileSettings()
        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

profileTab 是个人中心页的完整组装体:用户资料头部 → 我的宠物 → 成就徽章 → 设置菜单 → 底部占位。四个模块自上而下排列,信息量丰富但层次分明。个人中心页作为用户身份的主阵地,整合了资料展示、宠物管理、成就激励和系统设置四大功能域,是应用中功能密度最高的页面。


十二、弹窗层:模态交互的优雅实现

12.1 发布动态弹窗

  @Builder addPostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showAddPostModal = false })

      Column() {
        Row() {
          Text('发布动态')
            .fontSize(17)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('✕')
            .fontSize(18)
            .fontColor('#999999')
            .onClick(() => { this.showAddPostModal = false })
        }
        .width('100%')
        .padding({ bottom: 12 })

        Column()
          .width('100%')
          .height(120)
          .backgroundColor('#F5F5F5')
          .borderRadius(10)
          .margin({ bottom: 12 })

        Row() {
          Column() {
            Text('取消')
              .fontSize(14)
              .fontColor('#999999')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F5F5F5')
          .borderRadius(22)
          .onClick(() => { this.showAddPostModal = false })

          Column().width(12)

          Column() {
            Text('发布')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#7B1FA2')
          .borderRadius(22)
          .onClick(() => { this.showAddPostModal = false })
        }
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#FFFFFF')
      .borderRadius({ topLeft: 20, topRight: 20 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: -4 })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
  }

addPostDialog 实现了从底部弹出的半屏模态框(Bottom Sheet)。整体结构是一个全屏的 Column,内部包含三个部分:顶部可点击的遮罩区、中间的表单内容区、以及(隐含的)底部安全区。顶部遮罩区通过 layoutWeight(1) 占据遮罩上方的所有空间,并绑定点击事件关闭弹窗。内容区固定在底部,宽度 100%,白色背景,顶部圆角 20px(borderRadius({ topLeft: 20, topRight: 20 })),顶部阴影(offsetY: -4)营造出浮层效果。内容区内有标题栏(“发布动态” + 关闭按钮)、120px 的内容编辑占位区、以及底部双按钮(取消 + 发布)。取消按钮使用灰色背景和文字,发布按钮使用紫色背景和白色文字,两个按钮通过 layoutWeight(1) 均分宽度,中间 12px 间距。整个弹窗的最外层背景色为 #99000000(黑色 60% 不透明度),实现了常见的蒙层效果。值得注意的是,所有关闭弹窗的操作(点击遮罩、点击 ✕、点击取消、点击发布)都统一修改 this.showAddPostModal = false,体现了单一状态源驱动的简洁性。

12.2 编辑与删除弹窗

  @Builder editPostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showEditPostModal = false })

      Column() {
        Row() {
          Text('编辑动态')
            .fontSize(17)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('✕')
            .fontSize(18)
            .fontColor('#999999')
            .onClick(() => { this.showEditPostModal = false })
        }
        .width('100%')
        .padding({ bottom: 12 })

        Text('内容: ' + (this.selectedPost?.content ?? ''))
          .fontSize(13)
          .fontColor('#555555')
          .width('100%')
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .padding({ top: 8, bottom: 8 })

        Row() {
          Column() {
            Text('删除')
              .fontSize(14)
              .fontColor('#F44336')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#FFEBEE')
          .borderRadius(22)
          .onClick(() => { this.showEditPostModal = false })

          Column().width(12)

          Column() {
            Text('保存')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#7B1FA2')
          .borderRadius(22)
          .onClick(() => { this.showEditPostModal = false })
        }
        .margin({ top: 12 })
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#FFFFFF')
      .borderRadius({ topLeft: 20, topRight: 20 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: -4 })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
  }

editPostDialog 与发布弹窗结构几乎完全一致,但内容区略有不同:标题改为"编辑动态",内容区显示了当前选中帖子的内容摘要(this.selectedPost?.content ?? '' 使用了可选链和空值合并,防止 selectedPost 为 null 时崩溃),操作按钮改为"删除"(红色文字 + 浅红背景)和"保存"(紫色实心)。这种危险操作(删除)与正向操作(保存)的视觉区分,通过颜色心理学实现:红色暗示警告和不可逆,紫色暗示确认和推进。

  @Builder deletePostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showDeleteConfirm = false })

      Column() {
        Column()
          .width(48).height(48)
          .backgroundColor('#FFEBEE')
          .borderRadius(24)
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .margin({ bottom: 12 })

        Text('确认删除')
          .fontSize(17)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
        Text('删除后无法恢复,确定删除该动态吗?')
          .fontSize(13)
          .fontColor('#999999')
          .textAlign(TextAlign.Center)
          .margin({ top: 8, bottom: 16 })

        Row() {
          Column() {
            Text('取消')
              .fontSize(14)
              .fontColor('#999999')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F5F5F5')
          .borderRadius(22)
          .onClick(() => { this.showDeleteConfirm = false })

          Column().width(12)

          Column() {
            Text('确认删除')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F44336')
          .borderRadius(22)
          .onClick(() => { this.showDeleteConfirm = false })
        }
      }
      .width('100%')
      .padding(24)
      .backgroundColor('#FFFFFF')
      .borderRadius(20)
      .margin({ left: 32, right: 32 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: 4 })

      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showDeleteConfirm = false })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
    .justifyContent(FlexAlign.Center)
  }

deletePostDialog 是居中显示的确认对话框(Alert Dialog),与底部的半屏弹窗不同。它使用了 justifyContent(FlexAlign.Center) 将内容区垂直居中,内容区宽度为 100% 但带有左右 32px 的外边距,形成两侧留白。内容区顶部有一个 48x48 的浅红色圆形警示图标区域,下方是标题、描述文本和双按钮。描述文本使用 textAlign(TextAlign.Center) 保证在换行时居中对齐。确认删除按钮使用红色实心背景(#F44336),这是危险操作的标准配色。上下两个 Column().layoutWeight(1) 分别占据了内容区上方和下方的空间,确保点击这些区域也能关闭弹窗,提升了用户体验——用户不必精确点击小按钮就能取消危险操作。


十三、内容路由与底部导航:应用的主干架构

13.1 内容区域路由

  @Builder contentArea() {
    Column() {
      if (this.activeTab === PetTab.FEED) {
        this.feedTab()
      } else if (this.activeTab === PetTab.DISCOVER) {
        this.discoverTab()
      } else if (this.activeTab === PetTab.POST) {
        this.postTab()
      } else if (this.activeTab === PetTab.MESSAGES) {
        this.messagesTab()
      } else {
        this.profileTab()
      }
    }
    .layoutWeight(1)
  }

contentArea 是应用的内容路由中心,通过 if-else if 链根据 activeTab 状态决定渲染哪个 Tab 页的内容。这种基于状态的条件渲染是单页应用(SPA)架构的核心模式:整个应用只有一个页面,通过切换状态来显示不同的"虚拟页面"。layoutWeight(1) 确保内容区占据除底部导航栏外的所有垂直空间。使用枚举值进行条件判断,代码语义清晰且具备类型安全。

13.2 底部导航栏

  @Builder bottomTabItem(icon: string, label: string, tab: PetTab) {
    Column() {
      Text(icon)
        .fontSize(20)
        .opacity(this.activeTab === tab ? 1.0 : 0.45)
      Text(label)
        .fontSize(9)
        .fontColor(this.activeTab === tab ? '#7B1FA2' : '#999999')
        .margin({ top: 1 })
      if (this.activeTab === tab) {
        Column()
          .width(18)
          .height(3)
          .backgroundColor('#7B1FA2')
          .borderRadius(2)
          .margin({ top: 2 })
      }
    }
    .layoutWeight(1)
    .alignItems(HorizontalAlign.Center)
    .padding({ top: 5, bottom: 5 })
    .onClick(() => { this.activeTab = tab })
  }

bottomTabItem 是底部导航栏的单个 Tab 按钮,接收 Emoji 图标、标签文本和对应的 PetTab 枚举值。当 activeTab 与当前 Tab 匹配时,图标不透明度为 1.0,文字颜色为紫色,并显示一个 18x3 的紫色底部指示条;未选中时,图标不透明度降至 0.45,文字为灰色,无指示条。这种多维度(图标亮度、文字颜色、指示条)的选中反馈,确保用户在任何光照条件下都能清晰识别当前位置。.onClick(() => { this.activeTab = tab }) 点击时更新状态,触发 contentArea 重新渲染,实现 Tab 切换。每个 Tab 项通过 layoutWeight(1) 均分导航栏宽度,内容垂直居中。

  build() {
    Stack() {
      Column() {
        this.contentArea()
        Row() {
          this.bottomTabItem('🐾', '动态', PetTab.FEED)
          this.bottomTabItem('🔍', '发现', PetTab.DISCOVER)
          this.bottomTabItem('📷', '发布', PetTab.POST)
          this.bottomTabItem('💬', '消息', PetTab.MESSAGES)
          this.bottomTabItem('👤', '我的', PetTab.PROFILE)
        }
        .width('100%')
        .backgroundColor('#FFFFFF')
        .padding({ top: 4, bottom: 6 })
        .shadow({ radius: 8, color: '#1A000000', offsetY: -2 })
      }
      .width('100%').height('100%')

      if (this.showAddPostModal) {
        this.addPostDialog()
      }
      if (this.showEditPostModal) {
        this.editPostDialog()
      }
      if (this.showDeleteConfirm) {
        this.deletePostDialog()
      }
    }
    .width('100%').height('100%')
    .backgroundColor('#F5F7FA')
  }

build() 是组件的入口渲染方法,整个应用被包裹在 Stack 堆叠容器中。Stack 允许子组件层叠放置,后渲染的组件覆盖在先渲染的组件之上,这是实现弹窗蒙层的关键——弹窗组件在 contentArea 和底部导航之后渲染,因此会覆盖在主内容之上。Stack 的第一层是主界面结构:垂直排列的内容区和底部导航栏。内容区通过 contentArea() 动态渲染,底部导航栏是固定的 Row,包含五个 Tab 按钮。导航栏使用白色背景、顶部阴影(offsetY: -2),与内容区形成分隔。Stack 的第二层是三个条件渲染的弹窗:showAddPostModal 控制发布弹窗,showEditPostModal 控制编辑弹窗,showDeleteConfirm 控制删除确认弹窗。三个弹窗独立管理,互不干扰,可以同时存在(虽然当前业务逻辑下不会同时触发)。整个 Stack 的背景色为 #F5F7FA,这是一种极浅的蓝灰色,作为所有页面的统一底色,让白色卡片内容更加突出。


十四、全模块技术对比总结

下表从模块名称、核心功能、状态管理要点、关键组件/构建器、设计模式与亮点五个维度,对本应用五大 Tab 页进行横向对比总结。

维度 动态 Feed 页 发现页 发布页 消息页 个人中心页
模块名称 feedTab discoverTab postTab messagesTab profileTab
核心功能 社交内容流的沉浸式浏览,包含品牌横幅、宠物故事轮播和帖子卡片列表 多维度内容发现,包含分类筛选、热门推荐、品种排行榜、品种网格和附近宠物 内容创作入口,包含发布表单预览、草稿箱管理和发布数据统计洞察 即时通讯会话列表与系统通知的聚合展示 用户身份主页,包含资料展示、宠物管理、成就徽章和系统设置
状态管理要点 无独立状态,纯数据驱动渲染;帖子卡片的点赞/收藏状态通过 PostModel@Observed 属性预留响应式能力 activeCategory 状态驱动分类筛选条的高亮切换;帖子列表为静态精选子集 showAddPostModal / showEditPostModal / showDeleteConfirm / selectedPost 多弹窗独立管理;草稿数据为静态模拟 聊天数据依赖 ChatModelunreadisOnline 属性;未读角标和在线指示器通过条件渲染响应状态变化 宠物数量 petProfiles.length 为动态计算数据;成就解锁状态 earned 驱动视觉差异化(置灰/高亮)
关键组件/构建器 headerBanner(渐变品牌横幅)、storiesRow(水平滚动故事轮播)、postCard(高度复用的帖子卡片,含图文、标签、互动栏) categoryChipsRow(水平滚动分类胶囊)、breedRankingCard(比例条形图)、breedGridCard(等分网格布局)、nearbyPetsCard(附近宠物列表) createPostFormPreview(发布表单预览)、draftItem / draftSection(草稿箱列表)、postingTips(运营提示) chatItem(含在线指示器 Stack 和未读角标的会话项)、notificationItem(参数化通知项)、notificationSection userProfileHeader(渐变资料头图 + 数据统计)、myPetProfileCard(宠物档案卡片)、achievementItem(解锁状态差异化渲染)
设计模式与亮点 ① 经典 Feed 流布局;② @Builder 高度复用的 postCard;③ 故事轮播条模拟 Instagram Story 交互 ① 配置化驱动分类筛选(activeCategory 状态);② 排行榜使用比例计算实现水平条形图;③ 多内容形态聚合(帖子 + 数据 + 列表) ① 创作-管理-洞察三位一体闭环;② 多弹窗独立状态管理避免条件分支复杂化;③ 草稿箱数据复用 PostModel 统一模型 ① 会话项使用 Stack 实现头像 + 在线指示器层叠;② 通知项参数化设计支持多类型复用;③ 分割线缩进设计(margin: 66px ① 个人头部使用更大圆角和更强阴影形成视觉焦点;② 成就徽章通过 earned 布尔值实现全套视觉状态切换;③ 设置菜单使用右箭头暗示导航层级

安装DevEco Studio程序

在这里插入图片描述
选择目标安装目录:

在这里插入图片描述
设置环境变量,但是需要重启一下:

在这里插入图片描述
新建一个空白模板:

在这里插入图片描述
设置API为24的模板项目:
在这里插入图片描述
初始化项目,自动下载相关依赖:

在这里插入图片描述


完整代码:

// 宠物社交社区 - Pet Social Community

interface PostData {
  id: string
  authorName: string
  authorAvatarColor: string
  petName: string
  petType: string
  petBreed: string
  content: string
  imageColors: string[]
  likes: number
  comments: number
  shares: number
  timestamp: string
  location: string
  tags: string[]
  isLiked: boolean
  isFavorited: boolean
}

interface PetProfileData {
  id: string
  name: string
  type: string
  breed: string
  age: string
  gender: string
  avatarColor: string
  ownerName: string
  location: string
  bio: string
  followers: number
  posts: number
}

interface ChatConversation {
  id: string
  petName: string
  petAvatarColor: string
  lastMessage: string
  timestamp: string
  unread: number
  isOnline: boolean
}

interface PetTypeMetaConfig {
  label: string
  icon: string
  color: string
  bg: string
}

interface PostTagMetaConfig {
  label: string
  color: string
}

interface BreedInfo {
  name: string
  type: string
  count: number
  color: string
}

interface CategoryChipData {
  label: string
  icon: string
  color: string
  count: number
}

interface WeeklyActivity {
  day: string
  posts: number
  likes: number
}

interface BreedDistribution {
  breed: string
  count: number
  color: string
}

interface EngagementStats {
  type: string
  value: number
  color: string
  icon: string
}

interface AchievementData {
  title: string
  description: string
  icon: string
  color: string
  earned: boolean
}

@Observed
class PostModel {
  id: string
  authorName: string
  authorAvatarColor: string
  petName: string
  petType: string
  petBreed: string
  content: string
  imageColors: string[]
  likes: number
  comments: number
  shares: number
  timestamp: string
  location: string
  tags: string[]
  isLiked: boolean
  isFavorited: boolean

  constructor(id: string, authorName: string, authorAvatarColor: string, petName: string, petType: string, petBreed: string, content: string, imageColors: string[], likes: number, comments: number, shares: number, timestamp: string, location: string, tags: string[], isLiked: boolean, isFavorited: boolean) {
    this.id = id
    this.authorName = authorName
    this.authorAvatarColor = authorAvatarColor
    this.petName = petName
    this.petType = petType
    this.petBreed = petBreed
    this.content = content
    this.imageColors = imageColors
    this.likes = likes
    this.comments = comments
    this.shares = shares
    this.timestamp = timestamp
    this.location = location
    this.tags = tags
    this.isLiked = isLiked
    this.isFavorited = isFavorited
  }
}

@Observed
class PetProfileModel {
  id: string
  name: string
  type: string
  breed: string
  age: string
  gender: string
  avatarColor: string
  ownerName: string
  location: string
  bio: string
  followers: number
  posts: number

  constructor(id: string, name: string, type: string, breed: string, age: string, gender: string, avatarColor: string, ownerName: string, location: string, bio: string, followers: number, posts: number) {
    this.id = id
    this.name = name
    this.type = type
    this.breed = breed
    this.age = age
    this.gender = gender
    this.avatarColor = avatarColor
    this.ownerName = ownerName
    this.location = location
    this.bio = bio
    this.followers = followers
    this.posts = posts
  }
}

@Observed
class ChatModel {
  id: string
  petName: string
  petAvatarColor: string
  lastMessage: string
  timestamp: string
  unread: number
  isOnline: boolean

  constructor(id: string, petName: string, petAvatarColor: string, lastMessage: string, timestamp: string, unread: number, isOnline: boolean) {
    this.id = id
    this.petName = petName
    this.petAvatarColor = petAvatarColor
    this.lastMessage = lastMessage
    this.timestamp = timestamp
    this.unread = unread
    this.isOnline = isOnline
  }
}

const petTypeConfigs: Record<string, PetTypeMetaConfig> = {
  '狗': { label: '狗狗', icon: '🐕', color: '#FF6F00', bg: '#FFF3E0' },
  '猫': { label: '猫猫', icon: '🐱', color: '#7B1FA2', bg: '#F3E5F5' },
  '兔': { label: '兔子', icon: '🐰', color: '#EC407A', bg: '#FCE4EC' },
  '鸟': { label: '鸟类', icon: '🐦', color: '#0288D1', bg: '#E1F5FE' },
  '仓鼠': { label: '仓鼠', icon: '🐹', color: '#FFA000', bg: '#FFF8E1' },
  '爬宠': { label: '爬宠', icon: '🦎', color: '#43A047', bg: '#E8F5E9' },
}

const tagConfigs: Record<string, PostTagMetaConfig> = {
  '日常': { label: '日常', color: '#7B1FA2' },
  '美食': { label: '美食', color: '#FF6F00' },
  '健康': { label: '健康', color: '#4CAF50' },
  '搞笑': { label: '搞笑', color: '#FF9800' },
  '穿搭': { label: '穿搭', color: '#EC407A' },
  '训练': { label: '训练', color: '#2196F3' },
  '出游': { label: '出游', color: '#26C6DA' },
  '领养': { label: '领养', color: '#66BB6A' },
  '求助': { label: '求助', color: '#F44336' },
}

const posts: PostModel[] = [
  new PostModel('p1', '毛毛妈', '#FF6F00', '毛毛', '狗', '金毛', '今天带毛毛去公园玩飞盘,超级开心!捡了一上午的飞盘都不累🐕', ['#FFE082', '#FFD54F', '#FFCA28'], 234, 45, 12, '10分钟前', '北京·朝阳公园', ['日常', '出游'], false, false),
  new PostModel('p2', '喵星人小宇', '#7B1FA2', '布丁', '猫', '英短', '布丁今天学会了新的猫爬架pose,优雅又不失霸气😼', ['#CE93D8', '#BA68C8'], 567, 89, 34, '25分钟前', '上海·静安', ['日常', '搞笑'], true, false),
  new PostModel('p3', '兔兔饲养员', '#EC407A', '棉花糖', '兔', '荷兰垂耳兔', '棉花糖的腮帮子真是太治愈了,每天看它吃草我能看一整天🐰', ['#F48FB1', '#F06292'], 189, 32, 8, '1小时前', '杭州·西湖', ['日常', '美食'], false, true),
  new PostModel('p4', '鸟语花香', '#0288D1', '啾啾', '鸟', '虎皮鹦鹉', '啾啾学会说"你好"了!虽然发音还不太标准但是太可爱了🦜', ['#81D4FA', '#4FC3F7'], 345, 67, 22, '2小时前', '广州·天河', ['日常', '训练'], false, false),
  new PostModel('p5', '仓鼠小屋', '#FFA000', '团子', '仓鼠', '银狐', '团子的新窝到了,一个跑轮能玩一整天,囤粮技能也点满了🐹', ['#FFD54F', '#FFE082'], 123, 18, 5, '3小时前', '深圳·南山', ['日常'], false, false),
  new PostModel('p6', '爬宠爱好者', '#43A047', '小蜥', '爬宠', '豹纹守宫', '蜕皮完成!新皮肤颜色超漂亮,爬宠的蜕变总是让人惊叹🦎', ['#81C784', '#66BB6A'], 267, 41, 15, '3小时前', '成都·武侯', ['日常', '健康'], true, false),
  new PostModel('p7', '铲屎官Linda', '#FF6F00', '阿福', '狗', '柯基', '阿福的小短腿上楼梯最新记录!虽然很慢但是超级努力🐕', ['#FFCC80', '#FFB74D'], 892, 123, 56, '4小时前', '北京·海淀', ['搞笑', '日常'], false, true),
  new PostModel('p8', '喵了个咪', '#7B1FA2', '雪球', '猫', '布偶猫', '雪球今天去洗澡了,美容师说它是店里最乖的小猫咪,真的假的?', ['#E1BEE7', '#CE93D8'], 456, 78, 23, '5小时前', '上海·浦东', ['日常', '穿搭'], false, false),
  new PostModel('p9', '养狗达人老王', '#FF6F00', '壮壮', '狗', '哈士奇', '壮壮今天拆了第三个沙发...二哈的破坏力名不虚传,我已经习惯了🤦', ['#90CAF9', '#64B5F6'], 1023, 234, 89, '6小时前', '南京·鼓楼', ['搞笑', '求助'], true, false),
  new PostModel('p10', '兔年大吉', '#EC407A', '雪糕', '兔', '安哥拉兔', '雪糕换毛季到了,每天梳下来的毛能织围巾了🐰', ['#FCE4EC', '#F8BBD0'], 156, 25, 7, '7小时前', '重庆·渝中', ['健康', '日常'], false, false),
  new PostModel('p11', '金毛狮王', '#FF6F00', '大王', '狗', '金毛', '大王的游泳初体验!金毛果然是天生的游泳健将,第一次下水就游得超好', ['#FFE082', '#FFD54F'], 678, 98, 45, '8小时前', '青岛·崂山', ['日常', '出游', '训练'], false, false),
  new PostModel('p12', '暹罗猫主人', '#7B1FA2', '咖啡', '猫', '暹罗猫', '咖啡今天在窗口看鸟看了两小时,专注力满分,就是抓不到😹', ['#C5CAE9', '#9FA8DA'], 334, 52, 14, '9小时前', '武汉·武昌', ['搞笑', '日常'], false, false),
  new PostModel('p13', '铲屎官日记', '#FF6F00', '豆豆', '狗', '泰迪', '豆豆新发型!这次是韩系蓬蓬头,走在路上回头率超高🐩', ['#BCAAA4', '#A1887F'], 445, 67, 28, '10小时前', '苏州·姑苏', ['穿搭', '日常'], false, true),
  new PostModel('p14', '喵酱的日常', '#7B1FA2', '拿破仑', '猫', '曼基康猫', '小短腿拿破仑跳不上沙发急得团团转,最后是被我抱上去的😸', ['#FFE082', '#FFF176'], 789, 145, 67, '11小时前', '长沙·岳麓', ['搞笑', '日常'], true, false),
  new PostModel('p15', '鸟友之家', '#0288D1', '彩彩', '鸟', '玄凤鹦鹉', '彩彩学会吹口哨了!居然能吹出小星星的旋律,太聪明了🦜', ['#80DEEA', '#4DD0E1'], 278, 38, 12, '12小时前', '西安·雁塔', ['训练', '日常'], false, false),
  new PostModel('p16', '爬行天下', '#43A047', '龙龙', '爬宠', '鬃狮蜥', '龙龙今天的伙食是超大号杜比亚,吃得开心到点头👑', ['#A5D6A7', '#81C784'], 198, 29, 8, '13小时前', '厦门·思明', ['美食', '日常'], false, false),
  new PostModel('p17', '柴犬之家', '#FF6F00', '小柴', '狗', '柴犬', '今天去网红狗咖遇到了超多小伙伴,小柴社恐又犯了躲在我身后🐕', ['#FFCC80', '#FFB74D'], 567, 89, 34, '14小时前', '成都·锦江', ['日常', '搞笑'], false, false),
  new PostModel('p18', '领养代替购买', '#66BB6A', '小幸运', '狗', '中华田园犬', '小幸运来家一个月了,从流浪到有家,每一天都是新的开始。领养不放弃!', ['#A5D6A7', '#81C784'], 1234, 267, 156, '15小时前', '杭州·余杭', ['领养', '日常'], true, true),
]

const petProfiles: PetProfileModel[] = [
  new PetProfileModel('pp1', '毛毛', '狗', '金毛寻回犬', '3岁', '男孩', '#FF6F00', '毛毛妈', '北京·朝阳', '爱玩飞盘的阳光大男孩,每天都要出门撒欢!', 2345, 156),
  new PetProfileModel('pp2', '布丁', '猫', '英短蓝猫', '2岁', '女孩', '#7B1FA2', '喵星人小宇', '上海·静安', '高冷优雅的女王大人,只对罐头感兴趣', 5678, 289),
  new PetProfileModel('pp3', '棉花糖', '兔', '荷兰垂耳兔', '1岁', '女孩', '#EC407A', '兔兔饲养员', '杭州·西湖', '腮帮子永远鼓鼓的吃货小公主', 1234, 67),
  new PetProfileModel('pp4', '啾啾', '鸟', '虎皮鹦鹉', '8个月', '男孩', '#0288D1', '鸟语花香', '广州·天河', '正在学说话的语言小天才', 890, 45),
  new PetProfileModel('pp5', '团子', '仓鼠', '银狐仓鼠', '6个月', '男孩', '#FFA000', '仓鼠小屋', '深圳·南山', '囤粮专业户,跑轮十级爱好者', 678, 34),
  new PetProfileModel('pp6', '小蜥', '爬宠', '豹纹守宫', '2岁', '女孩', '#43A047', '爬宠爱好者', '成都·武侯', '安静的美女子,蜕皮时最美', 456, 23),
  new PetProfileModel('pp7', '阿福', '狗', '柯基', '2岁', '男孩', '#FF6F00', '铲屎官Linda', '北京·海淀', '短腿界的马拉松选手', 3456, 198),
  new PetProfileModel('pp8', '雪球', '猫', '布偶猫', '1岁', '女孩', '#7B1FA2', '喵了个咪', '上海·浦东', '人见人爱的仙女猫,性格温柔', 4567, 167),
  new PetProfileModel('pp9', '壮壮', '狗', '哈士奇', '3岁', '男孩', '#FF6F00', '养狗达人老王', '南京·鼓楼', '拆家专业户,但眼神永远无辜', 8901, 345),
  new PetProfileModel('pp10', '小幸运', '狗', '中华田园犬', '1岁', '女孩', '#66BB6A', '领养人Lily', '杭州·余杭', '从流浪到幸福,用爱治愈一切', 5678, 89),
]

const chatList: ChatModel[] = [
  new ChatModel('c1', '毛毛', '#FF6F00', '好的,那明天上午10点公园见!毛毛已经迫不及待了~', '刚刚', 3, true),
  new ChatModel('c2', '布丁', '#7B1FA2', '布丁最近吃的这款罐头不错,我发链接给你', '5分钟前', 1, true),
  new ChatModel('c3', '阿福', '#FF6F00', '哈哈柯基上楼梯太逗了,我家那个也是', '15分钟前', 0, false),
  new ChatModel('c4', '雪球', '#7B1FA2', '谢谢推荐的美容师!雪球很喜欢她', '1小时前', 0, true),
  new ChatModel('c5', '壮壮', '#FF6F00', '你家也拆沙发?我推荐一个防咬喷雾,亲测有效!', '2小时前', 5, true),
  new ChatModel('c6', '小幸运', '#66BB6A', '看到小幸运的故事好感动,领养真的改变生命', '3小时前', 0, false),
  new ChatModel('c7', '棉花糖', '#EC407A', '兔子草推荐哪个牌子呀?我家宝最近挑食了', '昨天', 2, false),
  new ChatModel('c8', '啾啾', '#0288D1', '小星星我家的也会吹了!花了大概两周时间', '昨天', 0, false),
  new ChatModel('c9', '小柴', '#FF6F00', '柴犬社交恐惧症+1,握手!', '昨天', 1, true),
  new ChatModel('c10', '团子', '#FFA000', '新跑轮链接能再发一次吗?我找不到了', '2天前', 0, false),
]

const breedList: BreedInfo[] = [
  { name: '金毛', type: '狗', count: 234, color: '#FF6F00' },
  { name: '英短', type: '猫', count: 198, color: '#7B1FA2' },
  { name: '柯基', type: '狗', count: 167, color: '#FF6F00' },
  { name: '布偶猫', type: '猫', count: 145, color: '#7B1FA2' },
  { name: '哈士奇', type: '狗', count: 123, color: '#FF6F00' },
  { name: '柴犬', type: '狗', count: 98, color: '#FF6F00' },
]

const categoryChips: CategoryChipData[] = [
  { label: '全部', icon: '🏠', color: '#7B1FA2', count: 1256 },
  { label: '狗狗', icon: '🐕', color: '#FF6F00', count: 456 },
  { label: '猫猫', icon: '🐱', color: '#7B1FA2', count: 389 },
  { label: '兔子', icon: '🐰', color: '#EC407A', count: 67 },
  { label: '鸟类', icon: '🐦', color: '#0288D1', count: 45 },
  { label: '仓鼠', icon: '🐹', color: '#FFA000', count: 38 },
  { label: '爬宠', icon: '🦎', color: '#43A047', count: 23 },
]

const weeklyActivity: WeeklyActivity[] = [
  { day: '周一', posts: 45, likes: 1234 },
  { day: '周二', posts: 52, likes: 1456 },
  { day: '周三', posts: 48, likes: 1345 },
  { day: '周四', posts: 56, likes: 1567 },
  { day: '周五', posts: 67, likes: 1890 },
  { day: '周六', posts: 89, likes: 2345 },
  { day: '周日', posts: 78, likes: 2100 },
]

const breedDistribution: BreedDistribution[] = [
  { breed: '金毛', count: 234, color: '#FF6F00' },
  { breed: '英短', count: 198, color: '#7B1FA2' },
  { breed: '柯基', count: 167, color: '#FF6F00' },
  { breed: '布偶猫', count: 145, color: '#7B1FA2' },
  { breed: '哈士奇', count: 123, color: '#FF6F00' },
  { breed: '柴犬', count: 98, color: '#FF6F00' },
  { breed: '泰迪', count: 87, color: '#BCAAA4' },
  { breed: '暹罗猫', count: 76, color: '#7B1FA2' },
]

const engagementData: EngagementStats[] = [
  { type: '获赞', value: 12890, color: '#EC407A', icon: '❤️' },
  { type: '评论', value: 3456, color: '#7B1FA2', icon: '💬' },
  { type: '分享', value: 1890, color: '#4CAF50', icon: '🔄' },
]

const achievements: AchievementData[] = [
  { title: '百帖达人', description: '发布100条动态', icon: '🏆', color: '#FFD700', earned: true },
  { title: '万人瞩目', description: '获得10000个赞', icon: '⭐', color: '#FF6F00', earned: true },
  { title: '社交达人', description: '与50位宠友互动', icon: '🤝', color: '#7B1FA2', earned: true },
  { title: '连续签到', description: '连续签到30天', icon: '📅', color: '#4CAF50', earned: true },
  { title: '热门精选', description: '10次入选热门', icon: '🔥', color: '#F44336', earned: true },
  { title: '摄影大师', description: '发布100张美图', icon: '📸', color: '#2196F3', earned: false },
  { title: '领养先锋', description: '帮助5只宠物找到家', icon: '💕', color: '#EC407A', earned: false },
  { title: '百科专家', description: '回答100个求助', icon: '📚', color: '#26C6DA', earned: true },
]

const draftPosts: PostModel[] = [
  new PostModel('d1', '当前用户', '#7B1FA2', '毛毛', '狗', '金毛', '今天去新开的宠物乐园...', ['#E1BEE7'], 0, 0, 0, '草稿', '', ['出游'], false, false),
  new PostModel('d2', '当前用户', '#7B1FA2', '毛毛', '狗', '金毛', '毛毛的生日派对准备中...', ['#FFE082'], 0, 0, 0, '草稿', '', ['日常'], false, false),
  new PostModel('d3', '当前用户', '#7B1FA2', '毛毛', '狗', '金毛', '分享一个训练小技巧...', ['#CE93D8'], 0, 0, 0, '草稿', '', ['训练'], false, false),
  new PostModel('d4', '当前用户', '#7B1FA2', '毛毛', '狗', '金毛', '大家推荐哪种狗粮...', ['#FFD54F'], 0, 0, 0, '草稿', '', ['求助'], false, false),
]

function formatTimestamp(ts: string): string {
  return ts
}

function getPetTypeIcon(type: string): string {
  const cfg = petTypeConfigs[type]
  if (cfg !== undefined) {
    return cfg.icon
  }
  return '🐾'
}

function getPetTypeColor(type: string): string {
  const cfg = petTypeConfigs[type]
  if (cfg !== undefined) {
    return cfg.color
  }
  return '#999999'
}

function getPetTypeBg(type: string): string {
  const cfg = petTypeConfigs[type]
  if (cfg !== undefined) {
    return cfg.bg
  }
  return '#F5F5F5'
}

function getTagColor(tag: string): string {
  const cfg = tagConfigs[tag]
  if (cfg !== undefined) {
    return cfg.color
  }
  return '#7B1FA2'
}

function getLikeIconColor(isLiked: boolean): string {
  if (isLiked) {
    return '#EC407A'
  }
  return '#999999'
}

function getMaxActivityValue(data: WeeklyActivity[]): number {
  let maxVal: number = 0
  for (let i = 0; i < data.length; i++) {
    if (data[i].likes > maxVal) {
      maxVal = data[i].likes
    }
  }
  return maxVal + 200
}

function getMaxBreedCount(data: BreedDistribution[]): number {
  let maxVal: number = 0
  for (let i = 0; i < data.length; i++) {
    if (data[i].count > maxVal) {
      maxVal = data[i].count
    }
  }
  return maxVal + 50
}

function getPostCountForPet(petId: string): number {
  for (let i = 0; i < petProfiles.length; i++) {
    if (petProfiles[i].id === petId) {
      return petProfiles[i].posts
    }
  }
  return 0
}

function getDogPosts(): PostModel[] {
  const result: PostModel[] = posts
  return result
}

function getOnlineColor(isOnline: boolean): string {
  if (isOnline) {
    return '#4CAF50'
  }
  return '#CCCCCC'
}

function getChatUnreadBadge(unread: number): string {
  if (unread > 0) {
    return unread.toString()
  }
  return ''
}

enum PetTab {
  FEED = 0,
  DISCOVER = 1,
  POST = 2,
  MESSAGES = 3,
  PROFILE = 4
}

function getPetTabIcon(tab: PetTab): string {
  if (tab === PetTab.FEED) {
    return '🐾'
  } else if (tab === PetTab.DISCOVER) {
    return '🔍'
  } else if (tab === PetTab.POST) {
    return '📷'
  } else if (tab === PetTab.MESSAGES) {
    return '💬'
  } else {
    return '👤'
  }
}

function getPetTabLabel(tab: PetTab): string {
  if (tab === PetTab.FEED) {
    return '动态'
  } else if (tab === PetTab.DISCOVER) {
    return '发现'
  } else if (tab === PetTab.POST) {
    return '发布'
  } else if (tab === PetTab.MESSAGES) {
    return '消息'
  } else {
    return '我的'
  }
}

function getTimeDisplay(ts: string): string {
  if (ts === '草稿') {
    return '草稿'
  }
  return ts
}

@Entry
@Component
struct PetSocialApp {
  @State activeTab: PetTab = PetTab.FEED
  @State showAddPostModal: boolean = false
  @State showEditPostModal: boolean = false
  @State showDeleteConfirm: boolean = false
  @State selectedPost: PostModel | null = null
  @State activeCategory: string = '全部'

  @Builder headerBanner() {
    Column() {
      Row() {
        Text('🐾 宠物社区')
          .fontSize(20)
          .fontColor('#FFFFFF')
          .fontWeight(FontWeight.Bold)
        Column().layoutWeight(1)
        Row() {
          Column()
            .width(32).height(32)
            .backgroundColor('#FFFFFF')
            .opacity(0.2)
            .borderRadius(16)
            .justifyContent(FlexAlign.Center)
            .alignItems(HorizontalAlign.Center)
          Column().width(8)
          Column()
            .width(32).height(32)
            .backgroundColor('#FFFFFF')
            .opacity(0.2)
            .borderRadius(16)
            .justifyContent(FlexAlign.Center)
            .alignItems(HorizontalAlign.Center)
        }
      }
      .width('100%')

      Row() {
        Text('分享宝贝日常 · 遇见同频宠友')
          .fontSize(12)
          .fontColor('#FFFFFF')
          .opacity(0.7)
      }
      .width('100%')
      .margin({ top: 6 })
    }
    .width('100%')
    .padding({ top: 12, bottom: 16, left: 16, right: 16 })
    .linearGradient({
      angle: 135,
      colors: [['#7B1FA2', 0.0], ['#9C27B0', 0.5], ['#AB47BC', 1.0]]
    })
  }

  @Builder petStoryItem(profile: PetProfileModel) {
    Column() {
      Column()
        .width(52).height(52)
        .backgroundColor(profile.avatarColor)
        .borderRadius(26)
        .border({ width: 3, color: '#7B1FA2' })
      Text(profile.name)
        .fontSize(11)
        .fontColor('#333333')
        .fontWeight(FontWeight.Medium)
        .margin({ top: 4 })
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
    }
    .alignItems(HorizontalAlign.Center)
    .width(64)
    .margin({ right: 12 })
  }

  @Builder storiesRow() {
    Scroll() {
      Row() {
        Column() {
          Stack() {
            Column()
              .width(52).height(52)
              .backgroundColor('#F3E5F5')
              .borderRadius(26)
              .border({ width: 3, color: '#7B1FA2' })
            Text('+')
              .fontSize(22)
              .fontColor('#7B1FA2')
              .fontWeight(FontWeight.Bold)
          }
          Text('我的故事')
            .fontSize(11)
            .fontColor('#333333')
            .margin({ top: 4 })
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
        }
        .alignItems(HorizontalAlign.Center)
        .width(64)
        .margin({ right: 12 })

        this.petStoryItem(petProfiles[0])
        this.petStoryItem(petProfiles[1])
        this.petStoryItem(petProfiles[2])
        this.petStoryItem(petProfiles[3])
        this.petStoryItem(petProfiles[4])
        this.petStoryItem(petProfiles[5])
        this.petStoryItem(petProfiles[6])
        this.petStoryItem(petProfiles[7])
      }
      .padding({ left: 16, right: 16 })
    }
    .scrollable(ScrollDirection.Horizontal)
    .scrollBar(BarState.Off)
    .height(90)
  }

  @Builder postCard(item: PostModel) {
    Column() {
      Row() {
        Column()
          .width(40).height(40)
          .backgroundColor(item.authorAvatarColor)
          .borderRadius(20)
        Column() {
          Text(item.authorName)
            .fontSize(13)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Row() {
            Text(getPetTypeIcon(item.petType) + ' ' + item.petName)
              .fontSize(10)
              .fontColor('#999999')
            Column().width(8)
            Text(item.petBreed)
              .fontSize(10)
              .fontColor('#AAAAAA')
          }
          .margin({ top: 1 })
        }
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 8 })
        .layoutWeight(1)

        Text(item.timestamp)
          .fontSize(10)
          .fontColor('#CCCCCC')
      }
      .width('100%')

      if (item.content.length > 0) {
        Text(item.content)
          .fontSize(13)
          .fontColor('#444444')
          .width('100%')
          .margin({ top: 10 })
          .maxLines(4)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }

      if (item.imageColors.length > 0) {
        Row() {
          ForEach(item.imageColors, (color: string, idx: number) => {
            Column()
              .layoutWeight(1)
              .height(120)
              .backgroundColor(color)
              .borderRadius(8)
              .margin({ right: idx < item.imageColors.length - 1 ? 4 : 0 })
          })
        }
        .width('100%')
        .margin({ top: 8 })
      }

      if (item.tags.length > 0) {
        Row() {
          ForEach(item.tags, (tag: string, idx: number) => {
            Text(tag)
              .fontSize(9)
              .fontColor(getTagColor(tag))
              .padding({ left: 8, right: 8, top: 2, bottom: 2 })
              .backgroundColor(getTagColor(tag) + '1A')
              .borderRadius(8)
              .margin({ right: 6 })
          })
        }
        .width('100%')
        .margin({ top: 8 })
      }

      if (item.location.length > 0) {
        Row() {
          Text('📍 ' + item.location)
            .fontSize(10)
            .fontColor('#AAAAAA')
        }
        .width('100%')
        .margin({ top: 6 })
      }

      Divider()
        .color('#F5F5F5')
        .height(0.5)
        .margin({ top: 10, bottom: 6 })

      Row() {
        Row() {
          Text(item.isLiked ? '❤️' : '🤍')
            .fontSize(16)
          Text(item.likes.toString())
            .fontSize(11)
            .fontColor(item.isLiked ? '#EC407A' : '#999999')
            .margin({ left: 4 })
        }
        .onClick(() => {})

        Column().layoutWeight(1)

        Row() {
          Text('💬')
            .fontSize(14)
          Text(item.comments.toString())
            .fontSize(11)
            .fontColor('#999999')
            .margin({ left: 4 })
        }

        Column().layoutWeight(1)

        Row() {
          Text('🔄')
            .fontSize(14)
          Text(item.shares.toString())
            .fontSize(11)
            .fontColor('#999999')
            .margin({ left: 4 })
        }

        Column().layoutWeight(1)

        Text(item.isFavorited ? '⭐' : '☆')
          .fontSize(16)
          .onClick(() => {})
      }
      .width('100%')
    }
    .width('100%')
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder feedTab() {
    Scroll() {
      Column() {
        this.headerBanner()
        this.storiesRow()

        Divider()
          .color('#F0F0F0')
          .height(1)
          .margin({ left: 16, right: 16, bottom: 8 })

        this.postCard(posts[0])
        this.postCard(posts[1])
        this.postCard(posts[2])
        this.postCard(posts[6])
        this.postCard(posts[7])
        this.postCard(posts[8])
        this.postCard(posts[3])
        this.postCard(posts[10])
        this.postCard(posts[11])
        this.postCard(posts[13])
        this.postCard(posts[14])
        this.postCard(posts[16])
        this.postCard(posts[17])

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

  @Builder categoryChipsRow() {
    Scroll() {
      Row() {
        ForEach(categoryChips, (item: CategoryChipData, index: number) => {
          Row() {
            Text(item.icon)
              .fontSize(14)
              .margin({ right: 3 })
            Text(item.label)
              .fontSize(12)
              .fontColor(this.activeCategory === item.label ? '#FFFFFF' : '#666666')
              .fontWeight(FontWeight.Medium)
            Text(' ' + item.count.toString())
              .fontSize(10)
              .fontColor(this.activeCategory === item.label ? '#FFFFFF' : '#AAAAAA')
              .opacity(0.7)
          }
          .padding({ left: 12, right: 12, top: 6, bottom: 6 })
          .backgroundColor(this.activeCategory === item.label ? '#7B1FA2' : '#F5F5F5')
          .borderRadius(16)
          .margin({ right: 8 })
          .onClick(() => { this.activeCategory = item.label })
        })
      }
      .padding({ left: 16, right: 16 })
    }
    .scrollable(ScrollDirection.Horizontal)
    .scrollBar(BarState.Off)
    .height(44)
    .margin({ top: 8, bottom: 8 })
  }

  @Builder breedRankingCard() {
    Column() {
      Text('品种排行榜')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 10 })

      ForEach(breedDistribution, (item: BreedDistribution, index: number) => {
        Row() {
          Text((index + 1).toString())
            .fontSize(14)
            .fontColor('#7B1FA2')
            .fontWeight(FontWeight.Bold)
            .width(24)

          Text(item.breed)
            .fontSize(12)
            .fontColor('#555555')
            .width(60)

          Column() {
            Column()
              .width((item.count / getMaxBreedCount(breedDistribution)) * 180 + '%')
              .height(16)
              .backgroundColor(item.color)
              .borderRadius(8)
          }
          .layoutWeight(1)
          .height(16)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Text(item.count.toString() + '只')
            .fontSize(10)
            .fontColor('#999999')
            .margin({ left: 8 })
            .width(36)
        }
        .width('100%')
        .margin({ bottom: 6 })
      })
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder breedGridCard() {
    Column() {
      Text('品种展示')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      Row() {
        Column() {
          this.breedGridItem(breedList[0])
        }
        .layoutWeight(1)

        Column() {
          this.breedGridItem(breedList[1])
        }
        .layoutWeight(1)

        Column() {
          this.breedGridItem(breedList[2])
        }
        .layoutWeight(1)
      }

      Row() {
        Column() {
          this.breedGridItem(breedList[3])
        }
        .layoutWeight(1)

        Column() {
          this.breedGridItem(breedList[4])
        }
        .layoutWeight(1)

        Column() {
          this.breedGridItem(breedList[5])
        }
        .layoutWeight(1)
      }
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder breedGridItem(item: BreedInfo) {
    Column() {
      Column()
        .width(48).height(48)
        .backgroundColor(item.color + '1A')
        .borderRadius(24)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
      Text(item.name)
        .fontSize(11)
        .fontColor('#555555')
        .fontWeight(FontWeight.Medium)
        .margin({ top: 4 })
      Text(item.count.toString() + '只')
        .fontSize(9)
        .fontColor('#AAAAAA')
    }
    .alignItems(HorizontalAlign.Center)
    .padding({ top: 6, bottom: 6 })
    .margin({ left: 2, right: 2, bottom: 6 })
    .backgroundColor('#FAFAFA')
    .borderRadius(10)
  }

  @Builder nearbyPetItem(profile: PetProfileModel) {
    Row() {
      Column()
        .width(44).height(44)
        .backgroundColor(profile.avatarColor)
        .borderRadius(22)
      Column() {
        Row() {
          Text(profile.name)
            .fontSize(13)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Column().width(6)
          Text(getPetTypeIcon(profile.type) + profile.breed)
            .fontSize(10)
            .fontColor('#AAAAAA')
        }
        Text(profile.location)
          .fontSize(10)
          .fontColor('#BBBBBB')
          .margin({ top: 2 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      Row() {
        Text('关注')
          .fontSize(11)
          .fontColor('#7B1FA2')
          .fontWeight(FontWeight.Medium)
      }
      .padding({ left: 12, right: 12, top: 5, bottom: 5 })
      .border({ width: 1, color: '#7B1FA2' })
      .borderRadius(14)
    }
    .width('100%')
    .padding({ top: 8, bottom: 8, left: 0, right: 8 })
  }

  @Builder nearbyPetsCard() {
    Column() {
      Text('附近的宠物')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      this.nearbyPetItem(petProfiles[0])
      this.nearbyPetItem(petProfiles[1])
      this.nearbyPetItem(petProfiles[2])
      this.nearbyPetItem(petProfiles[3])
      this.nearbyPetItem(petProfiles[4])
      this.nearbyPetItem(petProfiles[5])
      this.nearbyPetItem(petProfiles[6])
      this.nearbyPetItem(petProfiles[7])
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder discoverTab() {
    Scroll() {
      Column() {
        this.categoryChipsRow()

        Text('热门推荐')
          .fontSize(15)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .padding({ left: 16, right: 16, top: 4, bottom: 6 })

        this.postCard(posts[0])
        this.postCard(posts[1])
        this.postCard(posts[6])
        this.postCard(posts[8])
        this.postCard(posts[10])
        this.postCard(posts[13])
        this.postCard(posts[14])
        this.postCard(posts[17])

        this.breedRankingCard()
        this.breedGridCard()
        this.nearbyPetsCard()

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

  @Builder createPostFormPreview() {
    Column() {
      Text('发布动态')
        .fontSize(16)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')

      Row() {
        Column()
          .width(40).height(40)
          .backgroundColor('#7B1FA2')
          .borderRadius(20)

        Column() {
          Text(currentUserName())
            .fontSize(13)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Text('选择你的宠物')
            .fontSize(11)
            .fontColor('#999999')
        }
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 10 })
        .layoutWeight(1)
      }
      .margin({ top: 12 })

      Column()
        .width('100%')
        .height(120)
        .backgroundColor('#F5F5F5')
        .borderRadius(10)
        .margin({ top: 10 })

      Row() {
        Column() {
          Text('📷')
            .fontSize(20)
          Text('图片')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)
        .margin({ right: 20 })

        Column() {
          Text('#️⃣ ')
            .fontSize(20)
          Text('话题')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)
        .margin({ right: 20 })

        Column() {
          Text('📍')
            .fontSize(20)
          Text('位置')
            .fontSize(9)
            .fontColor('#999999')
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Center)

        Column().layoutWeight(1)

        Row() {
          Text('发布')
            .fontSize(13)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Medium)
        }
        .padding({ left: 20, right: 20, top: 8, bottom: 8 })
        .backgroundColor('#7B1FA2')
        .borderRadius(20)
      }
      .width('100%')
      .margin({ top: 10 })
    }
    .width('100%')
    .padding(16)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, top: 12, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder draftItem(item: PostModel) {
    Row() {
      Column()
        .width(48).height(48)
        .backgroundColor(item.imageColors[0] ?? '#F5F5F5')
        .borderRadius(8)

      Column() {
        Text(item.content)
          .fontSize(12)
          .fontColor('#555555')
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
        Row() {
          if (item.tags.length > 0) {
            ForEach(item.tags, (tag: string, idx: number) => {
              Text(tag)
                .fontSize(8)
                .fontColor(getTagColor(tag))
                .padding({ left: 6, right: 6, top: 1, bottom: 1 })
                .backgroundColor(getTagColor(tag) + '1A')
                .borderRadius(6)
                .margin({ right: 4 })
            })
          }
        }
        .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      Text('编辑')
        .fontSize(11)
        .fontColor('#7B1FA2')
        .fontWeight(FontWeight.Medium)
    }
    .width('100%')
    .padding({ top: 10, bottom: 10 })
  }

  @Builder draftSection() {
    Column() {
      Text('草稿箱 (4)')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      this.draftItem(draftPosts[0])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[1])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[2])
      Divider().color('#F5F5F5').height(0.5)
      this.draftItem(draftPosts[3])
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder postingTips() {
    Column() {
      Text('发布小贴士')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      Column() {
        Row() {
          Text('💡')
            .fontSize(16)
            .margin({ right: 8 })
          Text('配上清晰可爱的照片更容易获得关注哦')
            .fontSize(11)
            .fontColor('#666666')
            .layoutWeight(1)
        }
      }
      .width('100%')
      .padding({ top: 6, bottom: 6 })

      Column() {
        Row() {
          Text('🏷️')
            .fontSize(16)
            .margin({ right: 8 })
          Text('添加相关标签可以让更多人看到你的动态')
            .fontSize(11)
            .fontColor('#666666')
            .layoutWeight(1)
        }
      }
      .width('100%')
      .padding({ top: 6, bottom: 6 })

      Column() {
        Row() {
          Text('❤️')
            .fontSize(16)
            .margin({ right: 8 })
          Text('积极互动回复评论,打造你的宠友圈')
            .fontSize(11)
            .fontColor('#666666')
            .layoutWeight(1)
        }
      }
      .width('100%')
      .padding({ top: 6, bottom: 6 })
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder postTab() {
    Scroll() {
      Column() {
        this.createPostFormPreview()
        this.draftSection()
        this.postingTips()

        Text('最近发布统计')
          .fontSize(14)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .padding({ left: 16, right: 16, top: 8, bottom: 6 })

        Row() {
          ForEach(engagementData, (item: EngagementStats, index: number) => {
            Column() {
              Text(item.icon)
                .fontSize(22)
              Text(item.value.toString())
                .fontSize(18)
                .fontColor(item.color)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 4 })
              Text(item.type)
                .fontSize(10)
                .fontColor('#999999')
                .margin({ top: 2 })
            }
            .alignItems(HorizontalAlign.Center)
            .layoutWeight(1)
          })
        }
        .width('100%')
        .padding(14)
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .margin({ left: 16, right: 16, bottom: 8 })
        .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

  @Builder chatItem(item: ChatModel) {
    Row() {
      Stack() {
        Column()
          .width(44).height(44)
          .backgroundColor(item.petAvatarColor)
          .borderRadius(22)
        if (item.isOnline) {
          Column()
            .width(10).height(10)
            .backgroundColor('#4CAF50')
            .borderRadius(5)
            .border({ width: 2, color: '#FFFFFF' })
            .margin({ left: 34, top: 34 })
        }
      }

      Column() {
        Row() {
          Text(item.petName)
            .fontSize(14)
            .fontColor('#333333')
            .fontWeight(FontWeight.Medium)
          Column().layoutWeight(1)
          Text(item.timestamp)
            .fontSize(10)
            .fontColor('#CCCCCC')
        }
        .width('100%')

        Row() {
          Text(item.lastMessage)
            .fontSize(12)
            .fontColor('#999999')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .layoutWeight(1)
          if (item.unread > 0) {
            Column()
              .height(18)
              .backgroundColor('#F44336')
              .borderRadius(9)
              .justifyContent(FlexAlign.Center)
              .alignItems(HorizontalAlign.Center)
              .padding({ left: 5, right: 5 })
          }
        }
        .width('100%')
        .margin({ top: 3 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })
    }
    .width('100%')
    .padding({ top: 12, bottom: 12 })
  }

  @Builder notificationItem(icon: string, title: string, desc: string, time: string, color: string) {
    Row() {
      Column()
        .width(36).height(36)
        .backgroundColor(color + '1A')
        .borderRadius(18)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
      Column() {
        Text(title)
          .fontSize(12)
          .fontColor('#333333')
          .fontWeight(FontWeight.Medium)
        Text(desc)
          .fontSize(10)
          .fontColor('#999999')
          .margin({ top: 1 })
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      Text(time)
        .fontSize(9)
        .fontColor('#CCCCCC')
    }
    .width('100%')
    .padding({ top: 6, bottom: 6 })
  }

  @Builder notificationSection() {
    Column() {
      Text('通知')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      this.notificationItem('❤️', '毛毛妈赞了你的动态', '今天带毛毛去公园玩飞盘...', '5分钟前', '#EC407A')
      this.notificationItem('💬', '喵星人小宇评论了你', '布丁也太可爱了叭!!', '15分钟前', '#7B1FA2')
      this.notificationItem('👤', '铲屎官Linda关注了你', '你们可能认识', '30分钟前', '#FF6F00')
      this.notificationItem('⭐', '你的动态被推荐了', '阿福的小短腿上楼梯...', '1小时前', '#FFD700')
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder messagesTab() {
    Scroll() {
      Column() {
        Row() {
          Text('消息')
            .fontSize(18)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('+')
            .fontSize(22)
            .fontColor('#7B1FA2')
            .fontWeight(FontWeight.Bold)
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 12, bottom: 4 })

        Column() {
          this.chatItem(chatList[0])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[1])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[2])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[3])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[4])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[5])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[6])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[7])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[8])
          Divider().color('#F5F5F5').height(0.5).margin({ left: 66, right: 16 })
          this.chatItem(chatList[9])
        }
        .backgroundColor('#FFFFFF')
        .borderRadius(12)
        .margin({ left: 16, right: 16 })
        .width('100%')
        .constraintSize({ maxWidth: '100%' })

        this.notificationSection()

        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

  @Builder myPetProfileCard(profile: PetProfileModel) {
    Row() {
      Column()
        .width(56).height(56)
        .backgroundColor(profile.avatarColor)
        .borderRadius(28)
        .border({ width: 3, color: '#7B1FA2' })

      Column() {
        Row() {
          Text(profile.name)
            .fontSize(16)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().width(6)
          Text(getPetTypeIcon(profile.type))
            .fontSize(14)
        }
        Text(profile.breed + ' · ' + profile.age + ' · ' + profile.gender)
          .fontSize(11)
          .fontColor('#999999')
          .margin({ top: 2 })
        Text(profile.bio)
          .fontSize(11)
          .fontColor('#666666')
          .margin({ top: 3 })
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })
    }
    .width('100%')
    .padding({ top: 8, bottom: 8 })
  }

  @Builder myPetSection() {
    Column() {
      Row() {
        Text('我的宠物')
          .fontSize(14)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
        Column().layoutWeight(1)
        Text('+ 添加')
          .fontSize(12)
          .fontColor('#7B1FA2')
          .fontWeight(FontWeight.Medium)
      }
      .width('100%')
      .padding({ left: 0, bottom: 8 })

      this.myPetProfileCard(petProfiles[1])
      Divider().color('#F5F5F5').height(0.5)
      this.myPetProfileCard(petProfiles[5])
      Divider().color('#F5F5F5').height(0.5)
      this.myPetProfileCard(petProfiles[9])
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder userProfileHeader() {
    Column() {
      Row() {
        Column()
          .width(60).height(60)
          .backgroundColor('#7B1FA2')
          .borderRadius(30)

        Column() {
          Text('铲屎官小宇')
            .fontSize(18)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('ID: pet_lover_xiaoyu')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
            .margin({ top: 2 })
        }
        .alignItems(HorizontalAlign.Start)
        .margin({ left: 12 })
        .layoutWeight(1)

        Text('›')
          .fontSize(24)
          .fontColor('#FFFFFF')
          .opacity(0.5)
      }
      .width('100%')

      Row() {
        Column() {
          Text(petProfiles.length.toString())
            .fontSize(16)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('宠物')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
        }
        .alignItems(HorizontalAlign.Center)
        .layoutWeight(1)

        Column() {
          Text('156')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('动态')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
        }
        .alignItems(HorizontalAlign.Center)
        .layoutWeight(1)

        Column() {
          Text('2345')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('粉丝')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
        }
        .alignItems(HorizontalAlign.Center)
        .layoutWeight(1)

        Column() {
          Text('678')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .fontWeight(FontWeight.Bold)
          Text('关注')
            .fontSize(10)
            .fontColor('#FFFFFF')
            .opacity(0.7)
        }
        .alignItems(HorizontalAlign.Center)
        .layoutWeight(1)
      }
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
    .borderRadius(16)
    .linearGradient({
      angle: 135,
      colors: [['#7B1FA2', 0.0], ['#9C27B0', 0.5], ['#AB47BC', 1.0]]
    })
    .margin({ left: 16, right: 16, top: 12, bottom: 8 })
    .shadow({ radius: 8, color: '#1A000000', offsetY: 2 })
  }

  @Builder achievementItem(item: AchievementData) {
    Row() {
      Column()
        .width(40).height(40)
        .backgroundColor(item.earned ? item.color + '1A' : '#F5F5F5')
        .borderRadius(20)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
        .opacity(item.earned ? 1.0 : 0.3)

      Column() {
        Text(item.title)
          .fontSize(13)
          .fontColor(item.earned ? '#333333' : '#CCCCCC')
          .fontWeight(FontWeight.Medium)
        Text(item.description)
          .fontSize(10)
          .fontColor(item.earned ? '#999999' : '#DDDDDD')
          .margin({ top: 1 })
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 10 })

      if (item.earned) {
        Text('✓')
          .fontSize(16)
          .fontColor('#4CAF50')
          .fontWeight(FontWeight.Bold)
      }
    }
    .width('100%')
    .padding({ top: 6, bottom: 6 })
  }

  @Builder achievementsSection() {
    Column() {
      Text('成就徽章')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 8 })

      ForEach(achievements, (item: AchievementData, index: number) => {
        this.achievementItem(item)
      })
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder profileSettings() {
    Column() {
      Text('设置')
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .padding({ left: 0, bottom: 6 })

      Row() {
        Text('个人资料')
          .fontSize(12)
          .fontColor('#555555')
          .layoutWeight(1)
        Text('›')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 8, bottom: 8 })

      Row() {
        Text('隐私设置')
          .fontSize(12)
          .fontColor('#555555')
          .layoutWeight(1)
        Text('›')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 8, bottom: 8 })

      Row() {
        Text('通知管理')
          .fontSize(12)
          .fontColor('#555555')
          .layoutWeight(1)
        Text('›')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 8, bottom: 8 })

      Row() {
        Text('关于我们')
          .fontSize(12)
          .fontColor('#555555')
          .layoutWeight(1)
        Text('›')
          .fontSize(16)
          .fontColor('#CCCCCC')
      }
      .width('100%')
      .padding({ top: 8, bottom: 8 })
    }
    .width('100%')
    .padding(14)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .margin({ left: 16, right: 16, bottom: 8 })
    .shadow({ radius: 3, color: '#0D000000', offsetY: 1 })
  }

  @Builder profileTab() {
    Scroll() {
      Column() {
        this.userProfileHeader()
        this.myPetSection()
        this.achievementsSection()
        this.profileSettings()
        Column().height(16)
      }
      .width('100%')
    }
    .layoutWeight(1)
    .scrollBar(BarState.Off)
  }

  @Builder addPostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showAddPostModal = false })

      Column() {
        Row() {
          Text('发布动态')
            .fontSize(17)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('✕')
            .fontSize(18)
            .fontColor('#999999')
            .onClick(() => { this.showAddPostModal = false })
        }
        .width('100%')
        .padding({ bottom: 12 })

        Column()
          .width('100%')
          .height(120)
          .backgroundColor('#F5F5F5')
          .borderRadius(10)
          .margin({ bottom: 12 })

        Row() {
          Column() {
            Text('取消')
              .fontSize(14)
              .fontColor('#999999')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F5F5F5')
          .borderRadius(22)
          .onClick(() => { this.showAddPostModal = false })

          Column().width(12)

          Column() {
            Text('发布')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#7B1FA2')
          .borderRadius(22)
          .onClick(() => { this.showAddPostModal = false })
        }
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#FFFFFF')
      .borderRadius({ topLeft: 20, topRight: 20 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: -4 })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
  }

  @Builder editPostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showEditPostModal = false })

      Column() {
        Row() {
          Text('编辑动态')
            .fontSize(17)
            .fontColor('#333333')
            .fontWeight(FontWeight.Bold)
          Column().layoutWeight(1)
          Text('✕')
            .fontSize(18)
            .fontColor('#999999')
            .onClick(() => { this.showEditPostModal = false })
        }
        .width('100%')
        .padding({ bottom: 12 })

        Text('内容: ' + (this.selectedPost?.content ?? ''))
          .fontSize(13)
          .fontColor('#555555')
          .width('100%')
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .padding({ top: 8, bottom: 8 })

        Row() {
          Column() {
            Text('删除')
              .fontSize(14)
              .fontColor('#F44336')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#FFEBEE')
          .borderRadius(22)
          .onClick(() => { this.showEditPostModal = false })

          Column().width(12)

          Column() {
            Text('保存')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#7B1FA2')
          .borderRadius(22)
          .onClick(() => { this.showEditPostModal = false })
        }
        .margin({ top: 12 })
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#FFFFFF')
      .borderRadius({ topLeft: 20, topRight: 20 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: -4 })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
  }

  @Builder deletePostDialog() {
    Column() {
      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showDeleteConfirm = false })

      Column() {
        Column()
          .width(48).height(48)
          .backgroundColor('#FFEBEE')
          .borderRadius(24)
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .margin({ bottom: 12 })

        Text('确认删除')
          .fontSize(17)
          .fontColor('#333333')
          .fontWeight(FontWeight.Bold)
        Text('删除后无法恢复,确定删除该动态吗?')
          .fontSize(13)
          .fontColor('#999999')
          .textAlign(TextAlign.Center)
          .margin({ top: 8, bottom: 16 })

        Row() {
          Column() {
            Text('取消')
              .fontSize(14)
              .fontColor('#999999')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F5F5F5')
          .borderRadius(22)
          .onClick(() => { this.showDeleteConfirm = false })

          Column().width(12)

          Column() {
            Text('确认删除')
              .fontSize(14)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Medium)
          }
          .layoutWeight(1)
          .height(44)
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#F44336')
          .borderRadius(22)
          .onClick(() => { this.showDeleteConfirm = false })
        }
      }
      .width('100%')
      .padding(24)
      .backgroundColor('#FFFFFF')
      .borderRadius(20)
      .margin({ left: 32, right: 32 })
      .shadow({ radius: 16, color: '#1A000000', offsetY: 4 })

      Column()
        .layoutWeight(1)
        .width('100%')
        .onClick(() => { this.showDeleteConfirm = false })
    }
    .width('100%').height('100%')
    .backgroundColor('#99000000')
    .justifyContent(FlexAlign.Center)
  }

  @Builder contentArea() {
    Column() {
      if (this.activeTab === PetTab.FEED) {
        this.feedTab()
      } else if (this.activeTab === PetTab.DISCOVER) {
        this.discoverTab()
      } else if (this.activeTab === PetTab.POST) {
        this.postTab()
      } else if (this.activeTab === PetTab.MESSAGES) {
        this.messagesTab()
      } else {
        this.profileTab()
      }
    }
    .layoutWeight(1)
  }

  @Builder bottomTabItem(icon: string, label: string, tab: PetTab) {
    Column() {
      Text(icon)
        .fontSize(20)
        .opacity(this.activeTab === tab ? 1.0 : 0.45)
      Text(label)
        .fontSize(9)
        .fontColor(this.activeTab === tab ? '#7B1FA2' : '#999999')
        .margin({ top: 1 })
      if (this.activeTab === tab) {
        Column()
          .width(18)
          .height(3)
          .backgroundColor('#7B1FA2')
          .borderRadius(2)
          .margin({ top: 2 })
      }
    }
    .layoutWeight(1)
    .alignItems(HorizontalAlign.Center)
    .padding({ top: 5, bottom: 5 })
    .onClick(() => { this.activeTab = tab })
  }

  build() {
    Stack() {
      Column() {
        this.contentArea()
        Row() {
          this.bottomTabItem('🐾', '动态', PetTab.FEED)
          this.bottomTabItem('🔍', '发现', PetTab.DISCOVER)
          this.bottomTabItem('📷', '发布', PetTab.POST)
          this.bottomTabItem('💬', '消息', PetTab.MESSAGES)
          this.bottomTabItem('👤', '我的', PetTab.PROFILE)
        }
        .width('100%')
        .backgroundColor('#FFFFFF')
        .padding({ top: 4, bottom: 6 })
        .shadow({ radius: 8, color: '#1A000000', offsetY: -2 })
      }
      .width('100%').height('100%')

      if (this.showAddPostModal) {
        this.addPostDialog()
      }
      if (this.showEditPostModal) {
        this.editPostDialog()
      }
      if (this.showDeleteConfirm) {
        this.deletePostDialog()
      }
    }
    .width('100%').height('100%')
    .backgroundColor('#F5F7FA')
  }
}

function currentUserName(): string {
  return '铲屎官小宇'
}


十五、结语

在这里插入图片描述

通过对这个宠物社交社区应用的逐段解析,我们可以清晰地看到 HarmonyOS ArkTS 在构建复杂移动端应用时的强大能力。从数据层的接口与 @Observed 响应式模型,到逻辑层的配置化驱动与工具函数封装,再到表现层的 @Builder 组件化与声明式渲染,每一层都体现了现代前端工程化的最佳实践。特别是 @State 状态管理与条件渲染的配合,使得弹窗切换、Tab 导航、分类筛选等交互场景的实现变得异常简洁。五个 Tab 页虽然功能各异,但都遵循了统一的视觉规范(卡片式布局、12px 圆角、阴影、16px 水平外边距)和代码风格(layoutWeight 弹性布局、ForEach 循环渲染、条件渲染控制显隐),这种一致性是大型项目可维护性的基石。希望本文的详细解析能够帮助读者深入理解 ArkTS 的设计哲学,并在实际项目中灵活运用这些技术要点。

Logo

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

更多推荐