zhangbo1_wd

新增顶导新方案

import { BottomNavDTO, CompDTO, TopNavDTO } from 'wdBean';
import { SpConstants } from 'wdConstant';
import { DisplayUtils, LazyDataSource, Logger, NetworkUtil, SPHelper, ToastUtils } from 'wdKit';
import { ProcessUtils, WDRouterPage, WDRouterRule } from 'wdRouter';
import { PageComponent } from './PageComponent';
import { ChannelSubscriptionLayout } from './ChannelSubscriptionLayout';
import { FirstTabTopSearchComponent } from '../search/FirstTabTopSearchComponent';
import { AssignChannelParam } from 'wdRouter/src/main/ets/utils/HomeChannelUtils';
import { PeopleShipMainComponent } from '../peopleShip/PeopleShipMainComponent';
import { channelSkeleton } from '../skeleton/channelSkeleton';
const TAG = 'TopNavigationComponent';
PersistentStorage.persistProp('channelIds', '');
PersistentStorage.persistProp('indexSettingChannelId', 2002);
const storage = LocalStorage.getShared();
/**
* 顶部页签导航栏/顶导
*/
@Entry(storage)
@Component
export struct TopNavigationComponentNew {
/**
* @deprecated
*/
private groupId: number = 0
/**
* @deprecated TODO type判断
*/
private currentBottomNavName: string = ''
private swiperController: SwiperController = new SwiperController()
private listScroller: Scroller = new Scroller()
@Consume barBackgroundColor: Color
/**
* @deprecated
*/
@State bottomSafeHeight: number = AppStorage.get<number>('bottomSafeHeight') || 0
/**
* @deprecated
*/
@State topSafeHeight: number = AppStorage.get<number>('topSafeHeight') || 0
@Link _currentNavIndex?: number;
// 顶导当前选中/焦点下标
@State currentTopNavSelectedIndex: number = 0;
// 顶导数据
@State @Watch('onTopNavigationDataUpdated') topNavList: TopNavDTO[] = []
@StorageProp('indexSettingChannelId') indexSettingChannelId: number = 2002
//我的频道id列表
@State channelIds: number[] = []
//本地缓存频道id列表
@StorageProp('channelIds') storageChannelIds: string = ''
@State homeChannelList: TopNavDTO[] = []
// 我的频道列表
@State myChannelList: TopNavDTO[] = []
// 更多频道列表
@State moreChannelList: TopNavDTO[] = []
// 地方频道列表
@State localChannelList: TopNavDTO[] = []
readonly MAX_LINE: number = 1;
@ObjectLink @Watch('onAssignChannelChange') assignChannel: AssignChannelParam
// 底导传递过来的自动刷新通知
@Prop @Watch('onAutoRefresh') autoRefresh: number = 0
// 传递给page的自动刷新通知
@State autoRefresh2Page: number = 0
// 当前底导index
@State bottomNavIndex: number = 0
@State animationDuration: number = 0
@State indicatorLeftMargin: number = 0
@State indicatorWidth: number = 0
private tabsWidth: number = 0
build() {
// 频道分类list
RelativeContainer() {
this.tabBar()
Swiper(this.swiperController) {
ForEach(this.currentBottomNavName === '新闻' ? this.myChannelList : this.topNavList,
(navItem: TopNavDTO, index: number) => {
if (this.currentBottomNavName === '人民号' && navItem.name === '关注') {
PeopleShipMainComponent({
currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
navIndex: index,
pageId: navItem.pageId + '',
channelId: navItem.channelId + '',
})
} else
if (!this.isBroadcast(navItem) && !this.isLayout(navItem)) {
PageComponent({
currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
navIndex: index,
pageId: navItem.pageId + '',
channelId: navItem.channelId + '',
autoRefresh: this.autoRefresh2Page
})
} else {
channelSkeleton()
}
})
}
.loop(false)
.indicator(false)
.effectMode(EdgeEffect.None)
.index(this.currentTopNavSelectedIndex)
.cachedCount(0)
.id('pageContent')
.alignRules({
'top': { 'anchor': 'tabList', 'align': VerticalAlign.Bottom },
'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom }
})
.onChange((index) => {
Logger.info(TAG, `onChange index : ${index}`);
if (this.isBroadcastByIndex(index)) {
// 跳转到播报页面
ProcessUtils.gotoBroadcastPage(this.myChannelList[index].pageId)
let nextIndex = this.currentTopNavSelectedIndex > index ? index - 1 : index + 1
if (nextIndex < this.myChannelList.length) {
this.changePage(nextIndex)
}
} else if (this.isLayoutByIndex(index)) {
ProcessUtils.gotoENewsPaper()
let nextIndex = this.currentTopNavSelectedIndex > index ? index - 1 : index + 1
if (nextIndex < this.myChannelList.length) {
this.changePage(nextIndex)
}
} else {
this.currentTopNavSelectedIndex = index;
this.changePage(this.currentTopNavSelectedIndex)
}
})
}
.width('100%')
.height('100%')
}
@Builder
topBar() {
Column() {
Row() {
FirstTabTopSearchComponent()
Image($r('app.media.icon_ren_min_ri_bao'))
.width(72)
.height(29)
.onClick(() => {
ProcessUtils.gotoENewsPaper()
})
Stack({ alignContent: Alignment.Center }) {
Image($r('app.media.background_read_paper_home'))
.width('100%')
.height('100%')
.objectFit(ImageFit.Contain)
Row() {
Image($r('app.media.icon_read_paper_home'))
.width(18)
.height(18)
Text('早晚报')
.fontColor("#666666")
.fontSize($r('app.float.font_size_13'))
}
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
}
.height(30)
.width(124)
.onClick(() => {
if (NetworkUtil.isNetConnected()) {
ProcessUtils.gotoMorningEveningPaper()
} else {
ToastUtils.showToast('网络出小差了,请检查网络后重试', 1000)
}
})
}.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.height(40)
.padding({ top: 10 })
.backgroundColor($r('app.color.white'))
.id('topBar')
}
@Builder
tabBar() {
// TODO 判断是否新闻tab,修改方法(,_currentNavIndex==0 都不对,需要用type)(用topStyle)
if (this.currentBottomNavName === '新闻') {
// 顶部搜索、日报logo、早晚报
this.topBar()
ChannelSubscriptionLayout({
currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
indexSettingChannelId: this.indexSettingChannelId,
homeChannelList: this.homeChannelList,
myChannelList: $myChannelList,
moreChannelList: $moreChannelList,
localChannelList: $localChannelList,
channelIds: $channelIds,
changeTab: (index) => {
this.changePage(index)
}
})
.id('channelManageBtn')
.alignRules({
'top': { 'anchor': 'topBar', 'align': VerticalAlign.Bottom },
'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
})
List({ scroller: this.listScroller }) {
ForEach(this.myChannelList, (navItem: TopNavDTO, index: number) => {
ListItem() {
this.tabBarBuilder(navItem, index)
}
})
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
.padding({ left: 8, top: 6, right: 0 })
.height($r('app.float.top_tab_bar_height'))
.backgroundColor(this.barBackgroundColor)
.onAreaChange((oldValue: Area, newValue: Area) => {
let width = Number.parseFloat(newValue.width.toString())
this.tabsWidth = Number.isNaN(width) ? 0 : width
})
.id('tabList')
.alignRules({
'top': { 'anchor': 'topBar', 'align': VerticalAlign.Bottom },
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start },
'right': { 'anchor': 'channelManageBtn', 'align': HorizontalAlign.Start }
})
} else {
Row() {
Image($r('app.media.icon_search'))
.width('24vp')
.height('24vp')
}
.height('40vp')
.width('40vp')
.margin({ right: 10 })
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
.id('searchBtn')
.alignRules({
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
})
.onClick(() => {
WDRouterRule.jumpWithPage(WDRouterPage.searchPage)
})
List({ scroller: this.listScroller }) {
ForEach(this.topNavList, (navItem: TopNavDTO, index: number) => {
ListItem() {
this.tabBarBuilder(navItem, index)
}
})
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
.height($r('app.float.top_tab_bar_height'))
.backgroundColor(this.barBackgroundColor)
.padding({ top: 6 })
.onAreaChange((oldValue: Area, newValue: Area) => {
let width = Number.parseFloat(newValue.width.toString())
this.tabsWidth = Number.isNaN(width) ? 0 : width
})
.id('tabList')
.alignRules({
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center }
})
}
}
@Builder
tabBarBuilder(item: TopNavDTO, index: number) {
Column() {
Text(item?.name)
.fontSize($r('app.float.selected_text_size'))
.fontWeight(this.currentTopNavSelectedIndex === index ? FontWeight.Bold : FontWeight.Normal)
.fontColor(this.currentTopNavSelectedIndex === index ? Color.Black : "#999999")
.padding({ top: $r('app.float.top_tab_item_padding_top'), bottom: $r('app.float.top_tab_item_padding_bottom') })
.maxLines(this.MAX_LINE)
.id(index.toString())
.onAreaChange((oldValue: Area, newValue: Area) => {
if (this.currentTopNavSelectedIndex === index &&
(this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)) {
if (newValue.position.x != undefined) {
let positionX = Number.parseFloat(newValue.position.x.toString())
this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX
}
let width = Number.parseFloat(newValue.width.toString())
this.indicatorWidth = Number.isNaN(width) ? 0 : width
}
})
if (this.currentTopNavSelectedIndex === index) {
Row()
.width(20)
.height(3)
.backgroundImage($r('app.media.icon_channel_active'), ImageRepeat.NoRepeat)
.backgroundImageSize(ImageSize.Contain)
}
}
.hoverEffect(HoverEffect.Highlight)
.constraintSize({
minWidth: $r('app.float.top_tab_item_min_width'),
maxWidth: $r('app.float.top_tab_item_max_width')
})
// .backgroundColor(Color.Transparent)
.padding({
left: $r('app.float.top_tab_item_padding_horizontal'),
right: $r('app.float.top_tab_item_padding_horizontal'),
bottom: 2,
})
.id(`col_tabBar${index}`)
.margin({ right: this.myChannelList.length === index + 1 ? 36 : 0 })
.onClick(() => {
Logger.debug(TAG, `onClick, index: ${index}`);
if (this.currentTopNavSelectedIndex === index) {
// 当前tab,单击事件
this.doAutoRefresh()
} else {
if (this.isBroadcastByIndex(index)) {
// 跳转到播报页面
ProcessUtils.gotoBroadcastPage(this.myChannelList[index].pageId)
} else if (this.isLayoutByIndex(index)) {
ProcessUtils.gotoENewsPaper()
} else {
this.currentTopNavSelectedIndex = index
this.changePage(index)
}
}
})
}
private changePage(index: number) {
this.swiperController.changeIndex(index)
this.listScroller.scrollToIndex(index, true, ScrollAlign.CENTER)
}
//处理新闻tab顶导频道数据
topNavListHandle() {
let cityName = SPHelper.default.getSync(SpConstants.LOCATION_CITY_NAME, '') as string
let _channelIds: number [] = []
let _myChannelList: TopNavDTO [] = []
let _storageChannelIds: string [] = [] //list1
let defaultMyChannelList: TopNavDTO[] = []
let defaultList = [...this.topNavList]
defaultList.sort((a, b) => {
return a.num - b.num;
});
//defaultMyChannelList
defaultList.forEach(item => {
if (item.defaultPermitted === 1 || item.movePermitted === 0 || item.delPermitted === 0 ||
item.headlinesOn === 1) {
defaultMyChannelList.push(item);
}
if (item.defaultPermitted === 1) {
this.homeChannelList.push(item)
}
})
//有缓存频道id
if (this.storageChannelIds) {
_storageChannelIds = this.storageChannelIds.split(',')
}
defaultMyChannelList.forEach(item => {
item.myChannel = '1'
if (item.defaultPermitted === 1) {
item.homeChannel = '1'
}
let index = defaultList.findIndex(_item => _item.channelId === item.channelId)
if (index !== -1) {
defaultList.splice(index, 1)
}
})
defaultList.unshift(...defaultMyChannelList)
defaultList.forEach((item, index) => {
if (this.storageChannelIds && _storageChannelIds.includes(String(item.channelId))) {
item.myChannel = '1'
}
if (item.channelType === 2) {
if (cityName.includes(item.name)) {
item.myChannel = '1'
}
item.localChannel = '1'
}
if (index >= 11) {
if (item.channelType === 1) {
item.moreChannel = '1'
}
} else {
if (item.channelType === 1 && item.myChannel !== '1') {
item.moreChannel = '1'
}
}
//频道分类
if (item.name !== '播报') { //暂时隐藏播报
if (item.myChannel === '1') {
_myChannelList.push(item)
_channelIds.push(item.channelId)
} else if (item.moreChannel === '1') {
this.moreChannelList.push(item)
} else if (item.localChannel === '1' && item.myChannel !== '1') {
this.localChannelList.push(item)
}
}
})
if (cityName) {
let index = _myChannelList.findIndex(ele => cityName.includes(ele.name))
const localChannelitem = _myChannelList.splice(index, 1)[0];
// 将当前地区频道插入到第四个
_myChannelList.splice(3, 0, localChannelitem);
}
this.channelIds = _channelIds
this.myChannelList = _myChannelList
//缓存首页频道
let index = this.myChannelList.findIndex(_item => _item?.channelId === this.indexSettingChannelId)
if (index > -1) {
this.currentTopNavSelectedIndex = index
}
}
private isBroadcast(item?: TopNavDTO): boolean {
// TODO 用id channelId = '2066'
return item?.name === '播报'
}
private isLayout(item?: TopNavDTO): boolean {
// TODO 用id channelId = '2006'
return item?.name === '版面'
}
private isBroadcastByIndex(index: number): boolean {
let item = this._currentNavIndex === 0 ? this.myChannelList[index] : this.topNavList[index]
return this.isBroadcast(item)
}
private isLayoutByIndex(index: number): boolean {
let item = this._currentNavIndex === 0 ? this.myChannelList[index] : this.topNavList[index]
return this.isLayout(item)
}
private isSpecialChannel(item?: TopNavDTO) {
// 版面、播报,可以用这个判断
return item?.channelType === 3
}
aboutToAppear() {
//处理新闻tab顶导频道数据
this.topNavListHandle()
this.changePage(this.currentTopNavSelectedIndex)
}
aboutToDisappear() {
AppStorage.set('channelIds', this.channelIds.join(','))
}
onTopNavigationDataUpdated() {
Logger.info(TAG,
`onTopNavigationDataUpdated currentTopNavIndex: ${this.currentTopNavSelectedIndex},topNavList.length:${this.topNavList.length}`);
}
onAutoRefresh() {
if (this.bottomNavIndex != this._currentNavIndex) {
return
}
// 通知page刷新
this.autoRefresh2Page++
}
private doAutoRefresh() {
// 通知page刷新
this.autoRefresh2Page++
}
/**
* 频道id变化,即指定频道跳转场景
*/
onAssignChannelChange() {
let channelId = this.assignChannel.channelId
let index = -1
if (this._currentNavIndex === 0) {
// 第一个,新闻,先拿我的,再拿其他
index = this.getChannelByMine(channelId)
if (index == -1) {
// 不在我的里,需要临时新增频道展示
let channel = this.getChannelByOthers(channelId)
if (channel) {
this.myChannelList.push(channel)
setTimeout(() => {
this.changePage(this.myChannelList.length - 1)
}, 20)
}
} else {
// 直接切换
this.changePage(index)
}
} else {
index = this.getChannelByTopNav(channelId)
if (index > -1) {
// 找到了,直接切换,否则不处理
this.changePage(index)
}
}
}
/**
* 非新闻,从topNav里拿数据
*/
private getChannelByTopNav(channelId: string) {
for (let i = 0; i < this.topNavList.length; i++) {
let topNavDTO: TopNavDTO = this.topNavList[i]
if (topNavDTO.channelId.toString() === channelId) {
return i
}
}
return -1
}
/**
* 新闻,从myChannelList里拿数据
*/
private getChannelByMine(channelId: string) {
for (let i = 0; i < this.myChannelList.length; i++) {
let topNavDTO: TopNavDTO = this.myChannelList[i]
if (topNavDTO.channelId?.toString() === channelId) {
return i
}
}
return -1
}
/**
* 新闻,从其他里拿数据
*/
private getChannelByOthers(channelId: string) {
for (let i = 0; i < this.moreChannelList.length; i++) {
let topNavDTO: TopNavDTO = this.moreChannelList[i]
if (topNavDTO.channelId?.toString() === channelId) {
return topNavDTO
}
}
for (let j = 0; j < this.localChannelList.length; j++) {
let topNavDTO: TopNavDTO = this.localChannelList[j]
if (topNavDTO.channelId?.toString() === channelId) {
return topNavDTO
}
}
return null
}
private getTextInfo(index: number): Record<string, number> {
let strJson = getInspectorByKey(index.toString())
try {
let obj: Record<string, string> = JSON.parse(strJson)
let rectInfo: number[][] = JSON.parse('[' + obj.$rect + ']')
return { 'left': px2vp(rectInfo[0][0]), 'width': px2vp(rectInfo[1][0] - rectInfo[0][0]) }
} catch (error) {
return { 'left': 0, 'width': 0 }
}
}
private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
let nextIndex = index
if (index > 0 && event.currentOffset > 0) {
nextIndex--
} else if (index < 3 && event.currentOffset < 0) {
nextIndex++
}
let indexInfo = this.getTextInfo(index)
let nextIndexInfo = this.getTextInfo(nextIndex)
let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)
let currentIndex = swipeRatio > 0.5 ? nextIndex : index // 页面滑动超过一半,tabBar切换到下一页。
let currentLeft = indexInfo.left + (nextIndexInfo.left - indexInfo.left) * swipeRatio
let currentWidth = indexInfo.width + (nextIndexInfo.width - indexInfo.width) * swipeRatio
return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }
}
private startAnimateTo(duration: number, leftMargin: number, width: number) {
animateTo({
duration: duration, // 动画时长
curve: Curve.Linear, // 动画曲线
iterations: 1, // 播放次数
playMode: PlayMode.Normal, // 动画模式
onFinish: () => {
console.info('play end')
}
}, () => {
this.indicatorLeftMargin = leftMargin
this.indicatorWidth = width
})
}
}
\ No newline at end of file
... ...