Showing
21 changed files
with
589 additions
and
50 deletions
| @@ -46,7 +46,7 @@ export struct EmptyComponent { | @@ -46,7 +46,7 @@ export struct EmptyComponent { | ||
| 46 | 46 | ||
| 47 | Text(this.buildNoDataTip()) | 47 | Text(this.buildNoDataTip()) |
| 48 | .fontSize($r('app.float.normal_text_size')) | 48 | .fontSize($r('app.float.normal_text_size')) |
| 49 | - .fontColor('#B2B2B2') | 49 | + .fontColor('#000000') |
| 50 | .fontWeight(FontWeight.Normal) | 50 | .fontWeight(FontWeight.Normal) |
| 51 | .opacity(this.TEXT_OPACITY) | 51 | .opacity(this.TEXT_OPACITY) |
| 52 | .margin({ top: this.EMPTY_TIP_TEXT_MARGIN_TOP }) | 52 | .margin({ top: this.EMPTY_TIP_TEXT_MARGIN_TOP }) |
| @@ -5,44 +5,99 @@ import PageViewModel from '../viewmodel/PageViewModel'; | @@ -5,44 +5,99 @@ import PageViewModel from '../viewmodel/PageViewModel'; | ||
| 5 | import { EmptyComponent } from './EmptyComponent'; | 5 | import { EmptyComponent } from './EmptyComponent'; |
| 6 | import { ErrorComponent } from './ErrorComponent'; | 6 | import { ErrorComponent } from './ErrorComponent'; |
| 7 | import { LabelComponent } from './LabelComponent'; | 7 | import { LabelComponent } from './LabelComponent'; |
| 8 | -import { LoadingComponent } from './LoadingComponent'; | ||
| 9 | import { TitleAbbrComponent } from './TitleAbbrComponent'; | 8 | import { TitleAbbrComponent } from './TitleAbbrComponent'; |
| 10 | import { TitleAllComponent } from './TitleAllComponent'; | 9 | import { TitleAllComponent } from './TitleAllComponent'; |
| 11 | -import {BannerComponent} from './BannerComponent' | 10 | +import { BannerComponent } from './BannerComponent' |
| 11 | +import PageModel from '../viewmodel/PageModel'; | ||
| 12 | +import { listTouchEvent } from '../utils/PullDownRefresh'; | ||
| 13 | +import RefreshLayout from './page/RefreshLayout'; | ||
| 14 | +import { RefreshLayoutBean } from './page/RefreshLayoutBean'; | ||
| 15 | +import NoMoreLayout from './page/NoMoreLayout'; | ||
| 16 | +import LoadMoreLayout from './page/LoadMoreLayout'; | ||
| 17 | +import CustomRefreshLoadLayout from './page/CustomRefreshLoadLayout'; | ||
| 12 | 18 | ||
| 13 | const TAG = 'PageComponent'; | 19 | const TAG = 'PageComponent'; |
| 14 | 20 | ||
| 15 | @Component | 21 | @Component |
| 16 | export struct PageComponent { | 22 | export struct PageComponent { |
| 17 | - @State viewType: number = ViewType.LOADED; | 23 | + // @State viewType: number = ViewType.LOADING; |
| 18 | // Group数据及子组件数据 | 24 | // Group数据及子组件数据 |
| 19 | - @State compList: LazyDataSource<CompDTO> = new LazyDataSource(); | ||
| 20 | - @State currentTopNavSelectedIndex: number = 0; | ||
| 21 | - @State pageId: string = ""; | ||
| 22 | - @State channelId: string = ""; | 25 | + // @State compList: LazyDataSource<CompDTO> = new LazyDataSource(); |
| 26 | + @State private pageModel: PageModel = new PageModel(); | ||
| 27 | + currentTopNavSelectedIndex: number = 0; | ||
| 28 | + pageId: string = ""; | ||
| 29 | + channelId: string = ""; | ||
| 23 | 30 | ||
| 24 | // @Link @Watch('onChange') paIndex:number | 31 | // @Link @Watch('onChange') paIndex:number |
| 25 | 32 | ||
| 26 | build() { | 33 | build() { |
| 27 | - if (this.viewType == ViewType.LOADING) { | ||
| 28 | - LoadingComponent() | ||
| 29 | - } else if (this.viewType == ViewType.ERROR) { | 34 | + Column() { |
| 35 | + if (this.pageModel.viewType == ViewType.LOADING) { | ||
| 36 | + // LoadingComponent() | ||
| 37 | + this.LoadingLayout() | ||
| 38 | + } else if (this.pageModel.viewType == ViewType.ERROR) { | ||
| 30 | ErrorComponent() | 39 | ErrorComponent() |
| 31 | - } else if (this.viewType == ViewType.EMPTY) { | 40 | + } else if (this.pageModel.viewType == ViewType.EMPTY) { |
| 32 | EmptyComponent() | 41 | EmptyComponent() |
| 33 | } else { | 42 | } else { |
| 43 | + this.ListLayout() | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + .width(CommonConstants.FULL_PARENT) | ||
| 47 | + .height(CommonConstants.FULL_PARENT) | ||
| 48 | + .onTouch((event: TouchEvent | undefined) => { | ||
| 49 | + if (event) { | ||
| 50 | + if (this.pageModel.viewType === ViewType.LOADED) { | ||
| 51 | + listTouchEvent(this.pageModel, event); | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + }) | ||
| 55 | + | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Builder ListLayout() { | ||
| 34 | List() { | 59 | List() { |
| 35 | - LazyForEach(this.compList, (compDTO: CompDTO, compIndex: number) => { | 60 | + // 下拉刷新 |
| 61 | + ListItem() { | ||
| 62 | + RefreshLayout({ | ||
| 63 | + refreshBean: new RefreshLayoutBean(this.pageModel.isVisiblePullDown, this.pageModel.pullDownRefreshImage, | ||
| 64 | + this.pageModel.pullDownRefreshText, this.pageModel.pullDownRefreshHeight) | ||
| 65 | + }) | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + LazyForEach(this.pageModel.compList, (compDTO: CompDTO, compIndex: number) => { | ||
| 36 | ListItem() { | 69 | ListItem() { |
| 37 | Column() { | 70 | Column() { |
| 38 | this.componentBuilder(compDTO, compIndex) | 71 | this.componentBuilder(compDTO, compIndex) |
| 39 | } | 72 | } |
| 40 | } | 73 | } |
| 41 | }) | 74 | }) |
| 75 | + | ||
| 76 | + // 加载更多 | ||
| 77 | + ListItem() { | ||
| 78 | + if (this.pageModel.hasMore) { | ||
| 79 | + LoadMoreLayout({ | ||
| 80 | + refreshBean: new RefreshLayoutBean(this.pageModel.isVisiblePullUpLoad, this.pageModel.pullUpLoadImage, | ||
| 81 | + this.pageModel.pullUpLoadText, this.pageModel.pullUpLoadHeight) | ||
| 82 | + }) | ||
| 83 | + } else { | ||
| 84 | + NoMoreLayout() | ||
| 85 | + } | ||
| 86 | + } | ||
| 42 | } | 87 | } |
| 43 | .cachedCount(5) | 88 | .cachedCount(5) |
| 44 | .height(CommonConstants.FULL_PARENT) | 89 | .height(CommonConstants.FULL_PARENT) |
| 90 | + .onScrollIndex((start: number, end: number) => { | ||
| 91 | + // Listen to the first index of the current list. | ||
| 92 | + this.pageModel.startIndex = start; | ||
| 93 | + // 包含了 头尾item,判断时需要考虑+2 | ||
| 94 | + this.pageModel.endIndex = end; | ||
| 95 | + }) | ||
| 45 | } | 96 | } |
| 97 | + | ||
| 98 | + @Builder LoadingLayout() { | ||
| 99 | + CustomRefreshLoadLayout({ refreshBean: new RefreshLayoutBean(true, | ||
| 100 | + $r('app.media.ic_pull_up_load'), $r('app.string.pull_up_load_text'), this.pageModel.pullDownRefreshHeight) }) | ||
| 46 | } | 101 | } |
| 47 | 102 | ||
| 48 | @Builder | 103 | @Builder |
| @@ -53,7 +108,7 @@ export struct PageComponent { | @@ -53,7 +108,7 @@ export struct PageComponent { | ||
| 53 | TitleAbbrComponent({ compDTO: compDTO }) | 108 | TitleAbbrComponent({ compDTO: compDTO }) |
| 54 | } else if (compDTO.compStyle === CompStyle.Title_All_01) { | 109 | } else if (compDTO.compStyle === CompStyle.Title_All_01) { |
| 55 | TitleAllComponent({ compDTO: compDTO }) | 110 | TitleAllComponent({ compDTO: compDTO }) |
| 56 | - } else if(compDTO.compStyle === CompStyle.Carousel_Layout_01) { | 111 | + } else if (compDTO.compStyle === CompStyle.Carousel_Layout_01) { |
| 57 | BannerComponent({ compDTO: compDTO }) | 112 | BannerComponent({ compDTO: compDTO }) |
| 58 | } else { | 113 | } else { |
| 59 | // todo:组件未实现 / Component Not Implemented | 114 | // todo:组件未实现 / Component Not Implemented |
| @@ -65,27 +120,25 @@ export struct PageComponent { | @@ -65,27 +120,25 @@ export struct PageComponent { | ||
| 65 | } | 120 | } |
| 66 | 121 | ||
| 67 | async aboutToAppear() { | 122 | async aboutToAppear() { |
| 68 | - Logger.info(TAG, `aboutToAppear, this.pageId: ${this.viewType} this.currentTopNavSelectedIndex: ${this.currentTopNavSelectedIndex}`); | ||
| 69 | - // if (this.currentTopNavSelectedIndex === 1) { // 顶导tab的第0个item是【热点】,第1个item是【推荐】 | ||
| 70 | - // this.compList.replaceAll() | ||
| 71 | - // let pageDto = await PageViewModel.getPageData2(getContext(this)) | ||
| 72 | - // if (pageDto && pageDto.compList) { | ||
| 73 | - // this.compList.push(...pageDto.compList) | ||
| 74 | - // } | ||
| 75 | - // } else { | ||
| 76 | - // let pageDto = await PageViewModel.getPageData1(getContext(this)) | ||
| 77 | - // if (pageDto && pageDto.compList) { | ||
| 78 | - // this.compList.push(...pageDto.compList) | ||
| 79 | - // } | ||
| 80 | - // } | ||
| 81 | - | ||
| 82 | - // if (this.currentTopNavSelectedIndex != 1) { // 顶导tab的第0个item是【热点】,第1个item是【推荐】 | ||
| 83 | - // return | ||
| 84 | - // } | ||
| 85 | - Logger.debug(TAG,'lllllalalal: ' + this.pageId+' , ' + this.channelId); | ||
| 86 | - let pageDto = await PageViewModel.getPageData(this.pageId, this.pageId, this.channelId, getContext(this)) | ||
| 87 | - if (pageDto && pageDto.compList) { | ||
| 88 | - this.compList.push(...pageDto.compList) | 123 | + Logger.info(TAG, `aboutToAppear id: ${this.pageId} , ${this.channelId} , navIndex: ${this.currentTopNavSelectedIndex}`); |
| 124 | + this.pageModel.pageId = this.pageId; | ||
| 125 | + this.pageModel.groupId = this.pageId; | ||
| 126 | + this.pageModel.channelId = this.channelId; | ||
| 127 | + this.pageModel.currentPage = 1; | ||
| 128 | + let pageDto = await PageViewModel.getPageData(this.pageModel.pageId, this.pageModel.pageId, this.pageModel.channelId | ||
| 129 | + , this.pageModel.currentPage, this.pageModel.pageSize, getContext(this)) | ||
| 130 | + if (pageDto && pageDto.compList && pageDto.compList.length > 0) { | ||
| 131 | + this.pageModel.viewType = ViewType.LOADED; | ||
| 132 | + this.pageModel.compList.push(...pageDto.compList) | ||
| 133 | + if (pageDto.compList.length === this.pageModel.pageSize) { | ||
| 134 | + this.pageModel.currentPage++; | ||
| 135 | + this.pageModel.hasMore = true; | ||
| 136 | + } else { | ||
| 137 | + this.pageModel.hasMore = false; | ||
| 138 | + } | ||
| 139 | + } else { | ||
| 140 | + Logger.debug(TAG, 'aboutToAppear, data response page ' + this.pageId + ', comp list is empty.'); | ||
| 141 | + this.pageModel.viewType = ViewType.EMPTY; | ||
| 89 | } | 142 | } |
| 90 | } | 143 | } |
| 91 | } | 144 | } |
| 1 | +import { RefreshConstants } from '../../utils/RefreshConstants'; | ||
| 2 | +import { RefreshLayoutBean } from './RefreshLayoutBean'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * Custom layout to show refresh or load. | ||
| 6 | + */ | ||
| 7 | +@Component | ||
| 8 | +export default struct CustomLayout { | ||
| 9 | + @ObjectLink refreshBean: RefreshLayoutBean; | ||
| 10 | + | ||
| 11 | + build() { | ||
| 12 | + Row() { | ||
| 13 | + Image(this.refreshBean.imageSrc) | ||
| 14 | + .width(RefreshConstants.RefreshLayout_IMAGE_WIDTH) | ||
| 15 | + .height(RefreshConstants.RefreshLayout_IMAGE_HEIGHT) | ||
| 16 | + | ||
| 17 | + Text(this.refreshBean.textValue) | ||
| 18 | + .margin({ | ||
| 19 | + left: RefreshConstants.RefreshLayout_TEXT_MARGIN_LEFT, | ||
| 20 | + bottom: RefreshConstants.RefreshLayout_TEXT_MARGIN_BOTTOM | ||
| 21 | + }) | ||
| 22 | + .fontSize(RefreshConstants.RefreshLayout_TEXT_FONT_SIZE) | ||
| 23 | + .textAlign(TextAlign.Center) | ||
| 24 | + } | ||
| 25 | + .clip(true) | ||
| 26 | + .width(RefreshConstants.FULL_WIDTH) | ||
| 27 | + .justifyContent(FlexAlign.Center) | ||
| 28 | + .height(this.refreshBean.heightValue) | ||
| 29 | + } | ||
| 30 | +} |
| 1 | +import CustomRefreshLoadLayout from './CustomRefreshLoadLayout'; | ||
| 2 | +import { RefreshLayoutBean } from './RefreshLayoutBean'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * The load more layout component. | ||
| 6 | + */ | ||
| 7 | +@Component | ||
| 8 | +export default struct LoadMoreLayout { | ||
| 9 | + @ObjectLink refreshBean: RefreshLayoutBean; | ||
| 10 | + | ||
| 11 | + build() { | ||
| 12 | + Column() { | ||
| 13 | + if (this.refreshBean.isVisible) { | ||
| 14 | + CustomRefreshLoadLayout({ | ||
| 15 | + refreshBean: new RefreshLayoutBean(this.refreshBean.isVisible, | ||
| 16 | + this.refreshBean.imageSrc, this.refreshBean.textValue, this.refreshBean.heightValue) | ||
| 17 | + }) | ||
| 18 | + } else { | ||
| 19 | + CustomRefreshLoadLayout({ | ||
| 20 | + refreshBean: new RefreshLayoutBean(this.refreshBean.isVisible, | ||
| 21 | + this.refreshBean.imageSrc, this.refreshBean.textValue, 0) | ||
| 22 | + }) | ||
| 23 | + } | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | +} |
| 1 | +import { RefreshConstants } from '../../utils/RefreshConstants' | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * The No more data layout component. | ||
| 5 | + */ | ||
| 6 | +@Component | ||
| 7 | +export default struct NoMoreLayout { | ||
| 8 | + build() { | ||
| 9 | + Row() { | ||
| 10 | + Text($r('app.string.footer_text')) | ||
| 11 | + .margin({ left: RefreshConstants.NoMoreLayoutConstant_NORMAL_PADDING }) | ||
| 12 | + .fontSize(RefreshConstants.NoMoreLayoutConstant_TITLE_FONT) | ||
| 13 | + .textAlign(TextAlign.Center) | ||
| 14 | + } | ||
| 15 | + .width(RefreshConstants.FULL_WIDTH) | ||
| 16 | + .justifyContent(FlexAlign.Center) | ||
| 17 | + .height(RefreshConstants.CUSTOM_LAYOUT_HEIGHT) | ||
| 18 | + } | ||
| 19 | +} |
| 1 | +import CustomRefreshLoadLayout from './CustomRefreshLoadLayout'; | ||
| 2 | +import { RefreshLayoutBean } from './RefreshLayoutBean'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * The refresh layout component. | ||
| 6 | + */ | ||
| 7 | +@Component | ||
| 8 | +export default struct RefreshLayout { | ||
| 9 | + @ObjectLink refreshBean: RefreshLayoutBean; | ||
| 10 | + | ||
| 11 | + build() { | ||
| 12 | + Column() { | ||
| 13 | + if (this.refreshBean.isVisible) { | ||
| 14 | + CustomRefreshLoadLayout({ refreshBean: new RefreshLayoutBean | ||
| 15 | + (this.refreshBean.isVisible, this.refreshBean.imageSrc, this.refreshBean.textValue, | ||
| 16 | + this.refreshBean.heightValue) }) | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + } | ||
| 20 | +} |
| 1 | +/** | ||
| 2 | + * Custom refresh load layout data. | ||
| 3 | + */ | ||
| 4 | +@Observed | ||
| 5 | +export class RefreshLayoutBean { | ||
| 6 | + /** | ||
| 7 | + * Custom refresh load layout isVisible. | ||
| 8 | + */ | ||
| 9 | + isVisible: boolean; | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * Custom refresh load layout imageSrc. | ||
| 13 | + */ | ||
| 14 | + imageSrc: Resource; | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * Custom refresh load layout textValue. | ||
| 18 | + */ | ||
| 19 | + textValue: Resource; | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * Custom refresh load layout heightValue. | ||
| 23 | + */ | ||
| 24 | + heightValue: number; | ||
| 25 | + | ||
| 26 | + constructor(isVisible: boolean, imageSrc: Resource, textValue: Resource, heightValue: number) { | ||
| 27 | + this.isVisible = isVisible; | ||
| 28 | + this.imageSrc = imageSrc; | ||
| 29 | + this.textValue = textValue; | ||
| 30 | + this.heightValue = heightValue; | ||
| 31 | + } | ||
| 32 | +} |
| @@ -46,7 +46,12 @@ export class HttpUrlUtils { | @@ -46,7 +46,12 @@ export class HttpUrlUtils { | ||
| 46 | // TODO 判断是否登录 | 46 | // TODO 判断是否登录 |
| 47 | headers.set('userId', this.getUserId()) | 47 | headers.set('userId', this.getUserId()) |
| 48 | headers.set('userType', this.getUserType()) | 48 | headers.set('userType', this.getUserType()) |
| 49 | - Logger.debug("TAG", 'getCommonHeaders headers: ' + headers); | 49 | + |
| 50 | + // Logger.debug("TAG", '******************* commonHeaders headers start ******************************** '); | ||
| 51 | + // headers.forEach((v,k)=>{ | ||
| 52 | + // Logger.debug("TAG", 'getCommonHeaders header: ' + k + ': ' + v); | ||
| 53 | + // }) | ||
| 54 | + // Logger.debug("TAG", '******************* commonHeaders headers end ******************************** '); | ||
| 50 | return headers; | 55 | return headers; |
| 51 | } | 56 | } |
| 52 | 57 | ||
| @@ -55,18 +60,21 @@ export class HttpUrlUtils { | @@ -55,18 +60,21 @@ export class HttpUrlUtils { | ||
| 55 | return this.HOST + this.BOTTOM_NAV_PATH; | 60 | return this.HOST + this.BOTTOM_NAV_PATH; |
| 56 | } | 61 | } |
| 57 | 62 | ||
| 58 | - static getCompInfoUrl(pageId: string, groupId: string, channelId: string) { | 63 | + static getCompInfoUrl(pageId: string, groupId: string, channelId: string, currentPage: number |
| 64 | + , pageSize: number) { | ||
| 59 | let url = this.HOST + this.COMP_PATH; | 65 | let url = this.HOST + this.COMP_PATH; |
| 60 | - // TODO 暂定只请求第一页,后续对接分页加载,参数再调整 | ||
| 61 | - url = url + "?channelStrategy=2&pageSize=20&pageNum=1&loadStrategy=first_load" | 66 | + // TODO 暂定只请求第一页,后续对接分页加载,参数再调整 first_load? |
| 67 | + url = url + "?channelStrategy=2&loadStrategy=first_load" | ||
| 62 | + "&districtCode=" + this.getDistrictCode() | 68 | + "&districtCode=" + this.getDistrictCode() |
| 63 | + "&provinceCode=" + this.getProvinceCode() | 69 | + "&provinceCode=" + this.getProvinceCode() |
| 64 | + "&cityCode=" + this.getCityCode() | 70 | + "&cityCode=" + this.getCityCode() |
| 65 | - // + "&refreshTime=" + DateTimeUtils.getCurrentTimeMillis() | ||
| 66 | - + "&refreshTime=" + "1703472405653" | 71 | + + "&refreshTime=" + DateTimeUtils.getCurrentTimeMillis() |
| 67 | + "&pageId=" + pageId | 72 | + "&pageId=" + pageId |
| 68 | + "&groupId=" + groupId | 73 | + "&groupId=" + groupId |
| 69 | - + "&channelId=" + channelId; | 74 | + + "&channelId=" + channelId |
| 75 | + + "&pageSize=" + pageSize | ||
| 76 | + + "&pageNum=" + currentPage; | ||
| 77 | + // Logger.debug("TAG", 'getCompInfoUrl url: '+url); | ||
| 70 | return url; | 78 | return url; |
| 71 | } | 79 | } |
| 72 | 80 | ||
| @@ -87,6 +95,7 @@ export class HttpUrlUtils { | @@ -87,6 +95,7 @@ export class HttpUrlUtils { | ||
| 87 | 95 | ||
| 88 | private static getTimestamp() { | 96 | private static getTimestamp() { |
| 89 | // return DateTimeUtils.getCurrentTime() + ''; | 97 | // return DateTimeUtils.getCurrentTime() + ''; |
| 98 | + // TODO 暂时写死,有些page 真实时间戳 返回数据为空 | ||
| 90 | return '155203523'; | 99 | return '155203523'; |
| 91 | } | 100 | } |
| 92 | 101 |
| @@ -11,8 +11,9 @@ export class PageRepository { | @@ -11,8 +11,9 @@ export class PageRepository { | ||
| 11 | return WDHttp.get<ResponseDTO<NavigationBodyDTO>>(url, headers) | 11 | return WDHttp.get<ResponseDTO<NavigationBodyDTO>>(url, headers) |
| 12 | }; | 12 | }; |
| 13 | 13 | ||
| 14 | - static fetchPageData(pageId: string, groupId: string, channelId: string) { | ||
| 15 | - let url = HttpUrlUtils.getCompInfoUrl(pageId, groupId, channelId) | 14 | + static fetchPageData(pageId: string, groupId: string, channelId: string, currentPage: number |
| 15 | + , pageSize: number) { | ||
| 16 | + let url = HttpUrlUtils.getCompInfoUrl(pageId, groupId, channelId, currentPage, pageSize) | ||
| 16 | let headers: HashMap<string, string> = HttpUrlUtils.getCommonHeaders(); | 17 | let headers: HashMap<string, string> = HttpUrlUtils.getCommonHeaders(); |
| 17 | return WDHttp.get<ResponseDTO<PageDTO>>(url, headers) | 18 | return WDHttp.get<ResponseDTO<PageDTO>>(url, headers) |
| 18 | }; | 19 | }; |
| 1 | +import promptAction from '@ohos.promptAction'; | ||
| 2 | +import PageModel from '../viewmodel/PageModel'; | ||
| 3 | +import { RefreshConstants as Const, RefreshState } from './RefreshConstants'; | ||
| 4 | +import PageViewModel from '../viewmodel/PageViewModel'; | ||
| 5 | +import { PageDTO } from '../repository/bean/PageDTO'; | ||
| 6 | +import { touchMoveLoadMore, touchUpLoadMore } from './PullUpLoadMore'; | ||
| 7 | + | ||
| 8 | +export function listTouchEvent(pageModel: PageModel, event: TouchEvent) { | ||
| 9 | + switch (event.type) { | ||
| 10 | + case TouchType.Down: | ||
| 11 | + pageModel.downY = event.touches[0].y; | ||
| 12 | + pageModel.lastMoveY = event.touches[0].y; | ||
| 13 | + break; | ||
| 14 | + case TouchType.Move: | ||
| 15 | + if ((pageModel.isRefreshing === true) || (pageModel.isLoading === true)) { | ||
| 16 | + return; | ||
| 17 | + } | ||
| 18 | + let isDownPull = event.touches[0].y - pageModel.lastMoveY > 0; | ||
| 19 | + if (((isDownPull === true) || (pageModel.isPullRefreshOperation === true)) && (pageModel.isCanLoadMore === false)) { | ||
| 20 | + // Finger movement, processing pull-down refresh. | ||
| 21 | + touchMovePullRefresh(pageModel, event); | ||
| 22 | + } else { | ||
| 23 | + // Finger movement, processing load more. | ||
| 24 | + touchMoveLoadMore(pageModel, event); | ||
| 25 | + } | ||
| 26 | + pageModel.lastMoveY = event.touches[0].y; | ||
| 27 | + break; | ||
| 28 | + case TouchType.Cancel: | ||
| 29 | + break; | ||
| 30 | + case TouchType.Up: | ||
| 31 | + if ((pageModel.isRefreshing === true) || (pageModel.isLoading === true)) { | ||
| 32 | + return; | ||
| 33 | + } | ||
| 34 | + if ((pageModel.isPullRefreshOperation === true)) { | ||
| 35 | + // Lift your finger and pull down to refresh. | ||
| 36 | + touchUpPullRefresh(pageModel); | ||
| 37 | + } else { | ||
| 38 | + // Fingers up, handle loading more. | ||
| 39 | + touchUpLoadMore(pageModel); | ||
| 40 | + } | ||
| 41 | + break; | ||
| 42 | + default: | ||
| 43 | + break; | ||
| 44 | + } | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +export function touchMovePullRefresh(pageModel: PageModel, event: TouchEvent) { | ||
| 48 | + if (pageModel.startIndex === 0) { | ||
| 49 | + pageModel.isPullRefreshOperation = true; | ||
| 50 | + let height = vp2px(pageModel.pullDownRefreshHeight); | ||
| 51 | + pageModel.offsetY = event.touches[0].y - pageModel.downY; | ||
| 52 | + // The sliding offset is greater than the pull-down refresh layout height, and the refresh condition is met. | ||
| 53 | + if (pageModel.offsetY >= height) { | ||
| 54 | + pullRefreshState(pageModel, RefreshState.Release); | ||
| 55 | + pageModel.offsetY = height + pageModel.offsetY * Const.Y_OFF_SET_COEFFICIENT; | ||
| 56 | + } else { | ||
| 57 | + pullRefreshState(pageModel, RefreshState.DropDown); | ||
| 58 | + } | ||
| 59 | + if (pageModel.offsetY < 0) { | ||
| 60 | + pageModel.offsetY = 0; | ||
| 61 | + pageModel.isPullRefreshOperation = false; | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +export function touchUpPullRefresh(pageModel: PageModel) { | ||
| 67 | + if (pageModel.isCanRefresh === true) { | ||
| 68 | + pageModel.offsetY = vp2px(pageModel.pullDownRefreshHeight); | ||
| 69 | + pullRefreshState(pageModel, RefreshState.Refreshing); | ||
| 70 | + pageModel.currentPage = 1; | ||
| 71 | + setTimeout(() => { | ||
| 72 | + let self: PageModel = pageModel; | ||
| 73 | + | ||
| 74 | + PageViewModel.getPageData(self.pageId, self.groupId, self.channelId, self.currentPage, self.pageSize) | ||
| 75 | + .then((data: PageDTO) => { | ||
| 76 | + if (data == null || data.compList == null || data.compList.length == 0) { | ||
| 77 | + self.hasMore = false; | ||
| 78 | + } else { | ||
| 79 | + if (data.compList.length == self.pageSize) { | ||
| 80 | + self.currentPage++; | ||
| 81 | + self.hasMore = true; | ||
| 82 | + } else { | ||
| 83 | + self.hasMore = false; | ||
| 84 | + } | ||
| 85 | + // 刷新,替换所有数据 | ||
| 86 | + self.compList.replaceAll(...data.compList) | ||
| 87 | + } | ||
| 88 | + closeRefresh(self, true); | ||
| 89 | + }).catch((err: string | Resource) => { | ||
| 90 | + promptAction.showToast({ message: err }); | ||
| 91 | + closeRefresh(self, false); | ||
| 92 | + }); | ||
| 93 | + }, Const.DELAY_TIME); | ||
| 94 | + } else { | ||
| 95 | + closeRefresh(pageModel, false); | ||
| 96 | + } | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +export function pullRefreshState(pageModel: PageModel, state: number) { | ||
| 100 | + switch (state) { | ||
| 101 | + case RefreshState.DropDown: | ||
| 102 | + pageModel.pullDownRefreshText = $r('app.string.pull_down_refresh_text'); | ||
| 103 | + pageModel.pullDownRefreshImage = $r('app.media.ic_pull_down_refresh'); | ||
| 104 | + pageModel.isCanRefresh = false; | ||
| 105 | + pageModel.isRefreshing = false; | ||
| 106 | + pageModel.isVisiblePullDown = true; | ||
| 107 | + break; | ||
| 108 | + case RefreshState.Release: | ||
| 109 | + pageModel.pullDownRefreshText = $r('app.string.release_refresh_text'); | ||
| 110 | + pageModel.pullDownRefreshImage = $r('app.media.ic_pull_up_refresh'); | ||
| 111 | + pageModel.isCanRefresh = true; | ||
| 112 | + pageModel.isRefreshing = false; | ||
| 113 | + break; | ||
| 114 | + case RefreshState.Refreshing: | ||
| 115 | + pageModel.offsetY = vp2px(pageModel.pullDownRefreshHeight); | ||
| 116 | + pageModel.pullDownRefreshText = $r('app.string.refreshing_text'); | ||
| 117 | + pageModel.pullDownRefreshImage = $r('app.media.ic_pull_up_load'); | ||
| 118 | + pageModel.isCanRefresh = true; | ||
| 119 | + pageModel.isRefreshing = true; | ||
| 120 | + break; | ||
| 121 | + case RefreshState.Success: | ||
| 122 | + pageModel.pullDownRefreshText = $r('app.string.refresh_success_text'); | ||
| 123 | + pageModel.pullDownRefreshImage = $r('app.media.ic_succeed_refresh'); | ||
| 124 | + pageModel.isCanRefresh = true; | ||
| 125 | + pageModel.isRefreshing = true; | ||
| 126 | + break; | ||
| 127 | + case RefreshState.Fail: | ||
| 128 | + pageModel.pullDownRefreshText = $r('app.string.refresh_fail_text'); | ||
| 129 | + pageModel.pullDownRefreshImage = $r('app.media.ic_fail_refresh'); | ||
| 130 | + pageModel.isCanRefresh = true; | ||
| 131 | + pageModel.isRefreshing = true; | ||
| 132 | + break; | ||
| 133 | + default: | ||
| 134 | + break; | ||
| 135 | + } | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +export function closeRefresh(pageModel: PageModel, isRefreshSuccess: boolean) { | ||
| 139 | + let self = pageModel; | ||
| 140 | + setTimeout(() => { | ||
| 141 | + let delay = Const.RefreshConstant_DELAY_PULL_DOWN_REFRESH; | ||
| 142 | + if (self.isCanRefresh === true) { | ||
| 143 | + pullRefreshState(pageModel, isRefreshSuccess ? RefreshState.Success : RefreshState.Fail); | ||
| 144 | + delay = Const.RefreshConstant_DELAY_SHRINK_ANIMATION_TIME; | ||
| 145 | + } | ||
| 146 | + animateTo({ | ||
| 147 | + duration: Const.RefreshConstant_CLOSE_PULL_DOWN_REFRESH_TIME, | ||
| 148 | + delay: delay, | ||
| 149 | + onFinish: () => { | ||
| 150 | + pullRefreshState(pageModel, RefreshState.DropDown); | ||
| 151 | + self.isVisiblePullDown = false; | ||
| 152 | + self.isPullRefreshOperation = false; | ||
| 153 | + } | ||
| 154 | + }, () => { | ||
| 155 | + self.offsetY = 0; | ||
| 156 | + }) | ||
| 157 | + }, self.isCanRefresh ? Const.DELAY_ANIMATION_DURATION : 0); | ||
| 158 | +} |
| 1 | +import promptAction from '@ohos.promptAction'; | ||
| 2 | +import PageModel from '../viewmodel/PageModel'; | ||
| 3 | +import { RefreshConstants as Const } from './RefreshConstants'; | ||
| 4 | +import PageViewModel from '../viewmodel/PageViewModel'; | ||
| 5 | +import { PageDTO } from '../repository/bean/PageDTO'; | ||
| 6 | + | ||
| 7 | +export function touchMoveLoadMore(model: PageModel, event: TouchEvent) { | ||
| 8 | + // list size +1 | ||
| 9 | + if (model.endIndex === model.compList.size() || model.endIndex === model.compList.size() + 1) { | ||
| 10 | + model.offsetY = event.touches[0].y - model.downY; | ||
| 11 | + if (Math.abs(model.offsetY) > vp2px(model.pullUpLoadHeight) / 2) { | ||
| 12 | + model.isCanLoadMore = true; | ||
| 13 | + model.isVisiblePullUpLoad = true; | ||
| 14 | + model.offsetY = -vp2px(model.pullUpLoadHeight) + model.offsetY * Const.Y_OFF_SET_COEFFICIENT; | ||
| 15 | + } | ||
| 16 | + } | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +export function touchUpLoadMore(model: PageModel) { | ||
| 20 | + let self: PageModel = model; | ||
| 21 | + animateTo({ | ||
| 22 | + duration: Const.ANIMATION_DURATION, | ||
| 23 | + }, () => { | ||
| 24 | + self.offsetY = 0; | ||
| 25 | + }) | ||
| 26 | + if ((self.isCanLoadMore === true) && (self.hasMore === true)) { | ||
| 27 | + self.isLoading = true; | ||
| 28 | + setTimeout(() => { | ||
| 29 | + closeLoadMore(model); | ||
| 30 | + PageViewModel.getPageData(self.pageId, self.groupId, self.channelId, self.currentPage, self.pageSize) | ||
| 31 | + .then((data: PageDTO) => { | ||
| 32 | + if (data == null || data.compList == null || data.compList.length == 0) { | ||
| 33 | + self.hasMore = false; | ||
| 34 | + } else { | ||
| 35 | + if (data.compList.length == self.pageSize) { | ||
| 36 | + self.currentPage++; | ||
| 37 | + self.hasMore = true; | ||
| 38 | + } else { | ||
| 39 | + self.hasMore = false; | ||
| 40 | + } | ||
| 41 | + self.compList.push(...data.compList) | ||
| 42 | + } | ||
| 43 | + }).catch((err: string | Resource) => { | ||
| 44 | + promptAction.showToast({ message: err }); | ||
| 45 | + }) | ||
| 46 | + }, Const.DELAY_TIME); | ||
| 47 | + } else { | ||
| 48 | + closeLoadMore(self); | ||
| 49 | + } | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +export function closeLoadMore(model: PageModel) { | ||
| 53 | + model.isCanLoadMore = false; | ||
| 54 | + model.isLoading = false; | ||
| 55 | + model.isVisiblePullUpLoad = false; | ||
| 56 | +} |
| 1 | +/** | ||
| 2 | + * The constant of refresh. | ||
| 3 | + */ | ||
| 4 | +export class RefreshConstants { | ||
| 5 | + /** | ||
| 6 | + * The off set coefficient. | ||
| 7 | + */ | ||
| 8 | + static readonly Y_OFF_SET_COEFFICIENT: number = 0.1; | ||
| 9 | + /** | ||
| 10 | + * The animation delay time. | ||
| 11 | + */ | ||
| 12 | + static readonly DELAY_ANIMATION_DURATION: number = 300; | ||
| 13 | + /** | ||
| 14 | + * The delay time. | ||
| 15 | + */ | ||
| 16 | + static readonly DELAY_TIME: number = 1000; | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * The animation duration. | ||
| 20 | + */ | ||
| 21 | + static readonly ANIMATION_DURATION: number = 2000; | ||
| 22 | + /** | ||
| 23 | + * The RefreshConstant constants. | ||
| 24 | + */ | ||
| 25 | + static readonly RefreshConstant_DELAY_PULL_DOWN_REFRESH: number = 50; | ||
| 26 | + static readonly RefreshConstant_CLOSE_PULL_DOWN_REFRESH_TIME: number = 150; | ||
| 27 | + static readonly RefreshConstant_DELAY_SHRINK_ANIMATION_TIME: number = 500; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * The page size. | ||
| 31 | + */ | ||
| 32 | + static readonly PAGE_SIZE: number = 20; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * The refresh and load height. | ||
| 36 | + */ | ||
| 37 | + static readonly CUSTOM_LAYOUT_HEIGHT: number = 70; | ||
| 38 | + /** | ||
| 39 | + * Full the width. | ||
| 40 | + */ | ||
| 41 | + static readonly FULL_WIDTH: string = '100%'; | ||
| 42 | + /** | ||
| 43 | + * The NoMoreLayout constants. | ||
| 44 | + */ | ||
| 45 | + static readonly NoMoreLayoutConstant_NORMAL_PADDING: number = 8; | ||
| 46 | + static readonly NoMoreLayoutConstant_TITLE_FONT: string = '16fp'; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * The RefreshLayout constants. | ||
| 50 | + */ | ||
| 51 | + static readonly RefreshLayout_MARGIN_LEFT: string = '40%'; | ||
| 52 | + static readonly RefreshLayout_TEXT_MARGIN_BOTTOM: number = 1; | ||
| 53 | + static readonly RefreshLayout_TEXT_MARGIN_LEFT: number = 7; | ||
| 54 | + static readonly RefreshLayout_TEXT_FONT_SIZE: number = 17; | ||
| 55 | + static readonly RefreshLayout_IMAGE_WIDTH: number = 18; | ||
| 56 | + static readonly RefreshLayout_IMAGE_HEIGHT: number = 18; | ||
| 57 | +} | ||
| 58 | +/** | ||
| 59 | + * The refresh state enum. | ||
| 60 | + */ | ||
| 61 | +export const enum RefreshState { | ||
| 62 | + DropDown = 0, | ||
| 63 | + Release = 1, | ||
| 64 | + Refreshing = 2, | ||
| 65 | + Success = 3, | ||
| 66 | + Fail = 4 | ||
| 67 | +} |
| 1 | +import { ViewType } from 'wdConstant/src/main/ets/enum/ViewType'; | ||
| 2 | +import { LazyDataSource } from 'wdKit'; | ||
| 3 | +import { CompDTO } from '../repository/bean/CompDTO'; | ||
| 4 | +import { RefreshConstants as Const } from '../utils/RefreshConstants'; | ||
| 5 | + | ||
| 6 | +export default class PageModel { | ||
| 7 | + pageId: string = ""; | ||
| 8 | + groupId: string = ""; | ||
| 9 | + channelId: string = ""; | ||
| 10 | + compList: LazyDataSource<CompDTO> = new LazyDataSource(); | ||
| 11 | + currentPage: number = 1; | ||
| 12 | + pageSize: number = Const.PAGE_SIZE; | ||
| 13 | + pullDownRefreshText: Resource = $r('app.string.pull_down_refresh_text'); | ||
| 14 | + pullDownRefreshImage: Resource = $r('app.media.ic_pull_down_refresh'); | ||
| 15 | + pullDownRefreshHeight: number = Const.CUSTOM_LAYOUT_HEIGHT; | ||
| 16 | + isVisiblePullDown: boolean = false; | ||
| 17 | + pullUpLoadText: Resource = $r('app.string.pull_up_load_text'); | ||
| 18 | + pullUpLoadImage: Resource = $r('app.media.ic_pull_up_load'); | ||
| 19 | + pullUpLoadHeight: number = Const.CUSTOM_LAYOUT_HEIGHT; | ||
| 20 | + isVisiblePullUpLoad: boolean = false; | ||
| 21 | + offsetY: number = 0; | ||
| 22 | + viewType: number = ViewType.LOADING; | ||
| 23 | + hasMore: boolean = true; | ||
| 24 | + startIndex = 0; | ||
| 25 | + endIndex = 0; | ||
| 26 | + downY = 0; | ||
| 27 | + lastMoveY = 0; | ||
| 28 | + isRefreshing: boolean = false; | ||
| 29 | + isCanRefresh = false; | ||
| 30 | + isPullRefreshOperation = false; | ||
| 31 | + isLoading: boolean = false; | ||
| 32 | + isCanLoadMore: boolean = false; | ||
| 33 | +} |
| @@ -67,7 +67,11 @@ export class PageViewModel extends BaseViewModel { | @@ -67,7 +67,11 @@ export class PageViewModel extends BaseViewModel { | ||
| 67 | * | 67 | * |
| 68 | * @return {GroupDTO} compRes.data | 68 | * @return {GroupDTO} compRes.data |
| 69 | */ | 69 | */ |
| 70 | - private async getPageData1(context?: Context): Promise<PageDTO> { | 70 | + private async getPageData1(currentPage: number, context?: Context): Promise<PageDTO> { |
| 71 | + if (currentPage > 1) { | ||
| 72 | + // 加载更多,返回无数据 | ||
| 73 | + return {} as PageDTO | ||
| 74 | + } | ||
| 71 | let compRes: ResponseDTO<PageDTO> | null = await ResourcesUtils.getResourcesJson<ResponseDTO<PageDTO>>('comp_list0.json', context); | 75 | let compRes: ResponseDTO<PageDTO> | null = await ResourcesUtils.getResourcesJson<ResponseDTO<PageDTO>>('comp_list0.json', context); |
| 72 | if (!compRes || !compRes.data) { | 76 | if (!compRes || !compRes.data) { |
| 73 | Logger.info(TAG, `getCompList compRes is empty`); | 77 | Logger.info(TAG, `getCompList compRes is empty`); |
| @@ -94,20 +98,23 @@ export class PageViewModel extends BaseViewModel { | @@ -94,20 +98,23 @@ export class PageViewModel extends BaseViewModel { | ||
| 94 | return compRes.data | 98 | return compRes.data |
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | - async getPageData(pageId: string, groupId: string, channelId: string, context?: Context): Promise<PageDTO> { | ||
| 98 | - Logger.error(TAG, 'getPageData pageId: ' + pageId); | 101 | + async getPageData(pageId: string, groupId: string, channelId: string, currentPage: number |
| 102 | + , pageSize: number, context?: Context): Promise<PageDTO> { | ||
| 103 | + Logger.debug(TAG, 'getPageData pageId: ' + pageId); | ||
| 99 | if (mock_switch) { | 104 | if (mock_switch) { |
| 100 | - return this.getPageData1(context); | 105 | + return this.getPageData1(currentPage, context); |
| 101 | } | 106 | } |
| 102 | return new Promise<PageDTO>((success, error) => { | 107 | return new Promise<PageDTO>((success, error) => { |
| 103 | - PageRepository.fetchPageData(pageId, groupId, channelId).then((resDTO: ResponseDTO<PageDTO>) => { | 108 | + PageRepository.fetchPageData(pageId, groupId, channelId, currentPage, pageSize) |
| 109 | + .then((resDTO: ResponseDTO<PageDTO>) => { | ||
| 104 | if (this.isRespondsInvalid(resDTO, 'getPageData')) { | 110 | if (this.isRespondsInvalid(resDTO, 'getPageData')) { |
| 105 | error("page data invalid"); | 111 | error("page data invalid"); |
| 106 | return | 112 | return |
| 107 | } | 113 | } |
| 108 | Logger.info(TAG, "getPageData then,resDTO.timeStamp:" + resDTO.timestamp); | 114 | Logger.info(TAG, "getPageData then,resDTO.timeStamp:" + resDTO.timestamp); |
| 109 | success(resDTO.data); | 115 | success(resDTO.data); |
| 110 | - }).catch((err: Error) => { | 116 | + }) |
| 117 | + .catch((err: Error) => { | ||
| 111 | Logger.error(TAG, `getPageData catch, error.name : ${err.name}, error.message:${err.message}`); | 118 | Logger.error(TAG, `getPageData catch, error.name : ${err.name}, error.message:${err.message}`); |
| 112 | error(err); | 119 | error(err); |
| 113 | }) | 120 | }) |
| @@ -7,6 +7,30 @@ | @@ -7,6 +7,30 @@ | ||
| 7 | { | 7 | { |
| 8 | "name": "footer_text", | 8 | "name": "footer_text", |
| 9 | "value": "已经到底了" | 9 | "value": "已经到底了" |
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "pull_up_load_text", | ||
| 13 | + "value": "加载中..." | ||
| 14 | + }, | ||
| 15 | + { | ||
| 16 | + "name": "pull_down_refresh_text", | ||
| 17 | + "value": "下拉刷新" | ||
| 18 | + }, | ||
| 19 | + { | ||
| 20 | + "name": "release_refresh_text", | ||
| 21 | + "value": "松开刷新" | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "name": "refreshing_text", | ||
| 25 | + "value": "正在刷新" | ||
| 26 | + }, | ||
| 27 | + { | ||
| 28 | + "name": "refresh_success_text", | ||
| 29 | + "value": "刷新成功" | ||
| 30 | + }, | ||
| 31 | + { | ||
| 32 | + "name": "refresh_fail_text", | ||
| 33 | + "value": "刷新失败" | ||
| 10 | } | 34 | } |
| 11 | ] | 35 | ] |
| 12 | } | 36 | } |
2.71 KB
1.88 KB
1.75 KB
2.15 KB
4.03 KB
| @@ -36,6 +36,10 @@ export class LazyDataSource<T> extends BasicDataSource<T> { | @@ -36,6 +36,10 @@ export class LazyDataSource<T> extends BasicDataSource<T> { | ||
| 36 | return this.dataArray; | 36 | return this.dataArray; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | + public size(): number { | ||
| 40 | + return this.dataArray.length | ||
| 41 | + } | ||
| 42 | + | ||
| 39 | // 增加/插入1个Item/数据,若index为undefined,则在数据尾部增加1个元素;否则,在指定索引(可为负/或大于数组长度)位置插入1个元素 | 43 | // 增加/插入1个Item/数据,若index为undefined,则在数据尾部增加1个元素;否则,在指定索引(可为负/或大于数组长度)位置插入1个元素 |
| 40 | public addItem(data: T, index?: number): void { | 44 | public addItem(data: T, index?: number): void { |
| 41 | if (index == undefined) { | 45 | if (index == undefined) { |
-
Please register or login to post a comment