BottomNavigationComponent.ets 11.1 KB
import { BottomNavi, CommonConstants, DisplayDirection } from 'wdConstant';
import { BottomNavDTO, NavigationBodyDTO, NavigationDetailDTO, TopNavDTO } from 'wdBean';
import { EmitterEventId, EmitterUtils, Logger, StringUtils } from 'wdKit';
import { TopNavigationComponent } from './TopNavigationComponent';
import { MinePageComponent } from './MinePageComponent';
import { CompUtils } from '../../utils/CompUtils';
import ChannelViewModel from '../../viewmodel/ChannelViewModel';
import HomeChannelUtils, { AssignChannelParam } from 'wdRouter';
import { VideoChannelPage } from './VideoChannelPage';
import { HttpUtils } from 'wdNetwork/Index';

const TAG = 'BottomNavigationComponent';
let storage = LocalStorage.getShared();

/**
 * 底部页签导航栏/底导
 */
@Entry(storage)
@Component
export struct BottomNavigationComponent {
  @Provide bottomRectHeight: number = 0
  @Provide topRectHeight: number = 0
  @Provide isLayoutFullScreen: boolean = false
  @Provide displayDirection: DisplayDirection = DisplayDirection.VERTICAL
  @Provide isImmersive: boolean = false // 是否开启沉浸式模式 http://192.168.1.3:3300/project/3802/interface/api/189229
  @Provide isNight: boolean = false // 是否开启夜间模式
  @Provide currentBottomNavInfo: BottomNavDTO = {} as BottomNavDTO; // 当前底导信息
  @Provide currentTopNavInfo: TopNavDTO = {} as TopNavDTO; // 当前顶导信息
  @Provide barBackgroundColor: Color = Color.Transparent
  @State bottomSafeHeight: number = AppStorage.get<number>('bottomSafeHeight') || 0
  @State topSafeHeight: number = AppStorage.get<number>('topSafeHeight') || 0
  @State @Watch('onBottomNavigationDataUpdated') bottomNavList: BottomNavDTO[] = [] // 底导/顶导全部数据
  @State currentNavIndex: number = BottomNavi.NEWS; // 底导当前选中/焦点下标
  @State topNavList: TopNavDTO[] = []
  // 底导TabsController
  private navController: TabsController = new TabsController();
  readonly ASPECT_RATIO_1_1: number = 1 / 1; // 底导图片宽高比
  /**
   * Component opacity value: 1.
   */
  readonly FULL_OPACITY: number = 1;
  /**
   * Component opacity value: 0.6.
   */
  readonly SIXTY_OPACITY: number = 0.6;
  // 用于传参到顶导组件,【不用channelParam,主要是时序问题,需要先底导处理完,再延时触发顶导处理】
  @State assignChannel: AssignChannelParam = new AssignChannelParam()
  // 自动刷新触发(双击tab自动刷新)
  @State autoRefresh: number = 0
  // 顶导数据,从接口获取 TODO 顶导业务逻辑没看懂,暂时不替换顶导list。频道管理数据待梳理
  @State topNavMap: Record<string, TopNavDTO[]> = {}

  async aboutToAppear() {
    Logger.info(TAG, `aboutToAppear currentNavIndex: ${this.currentNavIndex}`);
    this.getBottomData()

    EmitterUtils.receiveEvent(EmitterEventId.JUMP_HOME_CHANNEL, (str?: string) => {
      Logger.debug(TAG, 'receiveEvent JUMP_HOME_CHANNEL: ' + str)
      if (str) {
        // 跳转指定频道场景,传参底导id、频道id
        let assignChannel = JSON.parse(str) as AssignChannelParam
        this.changeBottomNav(assignChannel)
      }
    })
  }

  aboutToDisappear() {
    Logger.info(TAG, `aboutToDisappear, this.currentNavIndex: ${this.currentNavIndex}`);
  }

  build() {
    Tabs({ barPosition: BarPosition.End, index: this.currentNavIndex, controller: this.navController }) {
      ForEach(this.bottomNavList, (navItem: BottomNavDTO, index: number) => {
        TabContent() {
          if (CompUtils.isMine(navItem)) {
            // 我的页面组件数据列表
            MinePageComponent({ isMinePage: this.currentNavIndex === this.bottomNavList.length - 1 })
          } else if (navItem.name === '视频') {
            // 视频频道,包含视频和直播
            VideoChannelPage({
              topNavList: navItem.topNavChannelList.filter(item => item.channelId != 2073),
              _currentNavIndex: $currentNavIndex,
              autoRefresh: this.autoRefresh
            })
          } else {
            TopNavigationComponent({
              groupId: navItem.id,
              topNavList: navItem.topNavChannelList.filter(item => item.channelId != 2073),
              _currentNavIndex: $currentNavIndex,
              navIndex: index,
              currentBottomNavName: navItem.name,
              assignChannel: this.assignChannel,
              autoRefresh: this.autoRefresh
            })
          }
        }
        .tabBar(this.tabBarBuilder(navItem, index))

        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
      });

    }
    .zIndex(10)
    .scrollable(false)
    .animationDuration(0)
    .barHeight(this.displayDirection === DisplayDirection.VERTICAL ? $r('app.float.bottom_navigation_barHeight') :
      0.001)
    .barMode(BarMode.Fixed)
    .barBackgroundColor(this.barBackgroundColor)
    // 备注:鸿蒙目前只有修改三线导航背景方法,对于全面屏导航条手机需要设置背景色并使其扩散到导航区域
    .backgroundColor(this.barBackgroundColor)
    .expandSafeArea([SafeAreaType.SYSTEM])

    // .padding({ bottom: this.bottomRectHeight + 'px', top: this.topRectHeight + 'px' }) // 此处margin具体数值在实际中应与导航条区域高度保持一致

  }

  @Builder
  tabBarBuilder(navItem: BottomNavDTO, index: number) {
    Stack({ alignContent: Alignment.Bottom }) {
      Image(this.currentNavIndex === index ? navItem.iconC : navItem.icon)
        .height(CommonConstants.FULL_PARENT)
        .padding({
          bottom: 15,
          left: 10,
          right: 10,
          top: 2
        })
        .aspectRatio(this.ASPECT_RATIO_1_1)

      Text(navItem.name)
        .margin({ bottom: $r('app.float.bottom_navigation_margin_bottom') })
        .fontWeight(this.currentNavIndex === index ? FontWeight.Bold : FontWeight.Normal)
        .textAlign(TextAlign.Center)
        .fontSize($r('app.float.font_size_10'))// .fontColor(this.currentNavIndex === index ? Color.Red : Color.Gray)
        .fontColor(this.currentNavIndex === index ? navItem.nameCColor : navItem.nameColor)
        .opacity(this.currentNavIndex === index ? this.FULL_OPACITY : this.SIXTY_OPACITY)
    }
    .zIndex(10)
    .height($r('app.float.bottom_navigation_barHeight'))
    .hoverEffect(HoverEffect.Highlight)
    .visibility(this.displayDirection === DisplayDirection.VERTICAL ? Visibility.Visible : Visibility.None)
    .onClick(() => {
      Logger.info(TAG, `onChange, index: ${index}`);
      this.onBottomNavigationIndexChange(navItem, index)
    })

  }

  // 底导切换函数
  async onBottomNavigationIndexChange(navItem: BottomNavDTO, index: number) {
    Logger.info(TAG, `onBottomNavigationIndexChange this.currentNavIndex: ${this.currentNavIndex}`);
    if (navItem.name === '我的') {
      this.barBackgroundColor = Color.White
      this.currentBottomNavInfo = {} as BottomNavDTO
    } else {
      if (this.currentNavIndex === index) {
        // 当前tab,单击事件
        this.autoRefresh++
      } else {
        // 切换tab
        this.currentBottomNavInfo = navItem
      }
    }
    this.currentNavIndex = index;

    // 请求顶导数据(参数):
  }

  //请求顶导数据
  async getTopNavList(id: number) {
    let bottomNavDetail = await ChannelViewModel.getBottomNavDetailData(id)
    this.topNavList = bottomNavDetail?.topNavChannelList || []
  }

  onBottomNavigationDataUpdated() {
    // Logger.info(TAG, `onBottomNavigationDataUpdated currentNavIndex: ${this.currentNavIndex},length:${this.bottomNavItemList.length}`);
  }

  /**
   * 底导id变化,即指定频道跳转场景
   */
  changeBottomNav(assignChannel: AssignChannelParam) {
    let index = -1
    for (let i = 0; i < this.bottomNavList.length; i++) {
      let bottomNavDTO: BottomNavDTO = this.bottomNavList[i]
      if (bottomNavDTO.id.toString() === assignChannel.bottomNavId) {
        index = i
        break
      }
    }
    if (index >= 0 && index != this.currentNavIndex) {
      // 切底导
      this.currentNavIndex = index
    }

    setTimeout(() => {
      // 底导切换后,触发顶导切换
      this.assignChannel = new AssignChannelParam()
      this.assignChannel.pageId = assignChannel.pageId
      this.assignChannel.channelId = assignChannel.channelId
      this.assignChannel.bottomNavId = assignChannel.bottomNavId
    }, 20)
  }

  private async getBottomData() {
    Logger.debug(TAG, 'getBottomData')
    // 1、缓存底导数据
    let bottomCache = await ChannelViewModel.getBottomNavCacheData()
    if (bottomCache && bottomCache.bottomNavList != null) {
      Logger.debug(TAG, 'getBottomData cache success')
      this.setData(bottomCache)
      this.getBottomDetail()
    }
    // TODO 2、预置底导数据
    // 3、接口底导数据
    let bottomNav = await ChannelViewModel.getBottomNavData()
    if (bottomNav && bottomNav.bottomNavList != null) {
      Logger.debug(TAG, 'getBottomNavData')
      HomeChannelUtils.setBottomNavData(bottomNav)
      if (this.bottomNavList == null || this.bottomNavList.length <= 0) {
        // 底导没展示,则用接口数据(接口数据回来,不去覆盖缓存数据,没有缓存才加载)
        this.setData(bottomNav)
        this.getBottomDetail()
      }
      // 将数据保存本地
      ChannelViewModel.saveBottomData(bottomNav)
    }
  }

  private getBottomDetail() {
    // // 1、获取顶导缓存数据
    // this.bottomNavList.forEach((value) => {
    //   // 先用底导带回的list初始化
    //   this.topNavMap[value.id] = value.topNavChannelList
    //   ChannelViewModel.getBottomNavDetailData(value?.id).then((bottomNavDetail) => {
    //     let list = bottomNavDetail?.topNavChannelList || []
    //     // 将顶导数据,单独存储
    //     this.topNavMap[value.id] = list
    //   })
    // })
    // // 2、获取顶导接口数据,返回后需要覆盖缓存数据
    // this.bottomNavList.forEach((value) => {
    //   ChannelViewModel.getBottomNavDetailData(value?.id).then((bottomNavDetail) => {
    //     let list = bottomNavDetail?.topNavChannelList || []
    //     // 将顶导数据,单独存储
    //     this.topNavMap[value.id] = list
    //     // 存储缓存
    //     ChannelViewModel.saveBottomDetailData(bottomNavDetail)
    //   })
    // })
  }

  private setData(data: NavigationBodyDTO) {
    Logger.debug(TAG, 'setData')
    if (data == null) {
      return
    }
    let list: BottomNavDTO[] = []
    let userId: string = HttpUtils.getUserId()
    // 先匹配换肤
    if (data.greyBottomNav != null && data.greyBottomNav.greyUserList != null &&
      data.greyBottomNav.greyUserList.length > 0) {
      // data.greyBottomNav.greyUserList.includes(userId)不生效,直接用循环匹配
      for (let i = 0; i < data.greyBottomNav.greyUserList.length; i++) {
        let id = data.greyBottomNav.greyUserList[i]
        if (id == userId) {
          list = data.greyBottomNav.bottomNavList
          break
        }
      }
    }
    // 没有匹配到换肤,则直接用data.bottomNavList
    if (list.length <= 0) {
      list = data.bottomNavList
    }
    if (list.length > 0) {
      Logger.info(TAG, `setData, bottomNav.length: ${list.length}`);
      // 使用filter方法移除name为'服务'的项
      list = list.filter(item => item.name !== '服务');
      this.bottomNavList = list
    }
  }
}