王士厅
Showing 69 changed files with 1693 additions and 31 deletions
  1 +import { SearchComponent } from 'wdComponent'
  2 +
  3 +@Entry
  4 +@Component
  5 +struct SearchPage {
  6 +
  7 + build() {
  8 + Column(){
  9 + SearchComponent()
  10 + }.height('100%')
  11 + .width('100%')
  12 + }
  13 +}
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 "pages/LaunchPage", 14 "pages/LaunchPage",
15 "pages/LaunchAdvertisingPage", 15 "pages/LaunchAdvertisingPage",
16 "pages/PrivacyPage", 16 "pages/PrivacyPage",
17 - "pages/OtherNormalUserHomePage" 17 + "pages/OtherNormalUserHomePage",
  18 + "pages/SearchPage"
18 ] 19 ]
19 } 20 }
  1 +{
  2 + "code": "0",
  3 + "data": [
  4 + {
  5 + "hotEntry": "习语",
  6 + "mark": 1,
  7 + "sequence": 1
  8 + },
  9 + {
  10 + "hotEntry": "党史学习教育工作",
  11 + "mark": 1,
  12 + "sequence": 2
  13 + },
  14 + {
  15 + "hotEntry": "2024年全国两会新闻中心启用",
  16 + "mark": 1,
  17 + "sequence": 3
  18 + },
  19 + {
  20 + "hotEntry": "第二艘国产大型邮轮后年底交付",
  21 + "mark": 0,
  22 + "sequence": 4
  23 + },
  24 + {
  25 + "hotEntry": "268名跨境电诈犯罪嫌疑人移交",
  26 + "mark": 0,
  27 + "sequence": 5
  28 + },
  29 + {
  30 + "hotEntry": "高考倒计时100天",
  31 + "mark": 2,
  32 + "sequence": 6
  33 + },
  34 + {
  35 + "hotEntry": "日本开始第四轮核污染水排放",
  36 + "mark": 0,
  37 + "sequence": 7
  38 + },
  39 + {
  40 + "hotEntry": "国台办点名管碧玲",
  41 + "mark": 0,
  42 + "sequence": 8
  43 + },
  44 + {
  45 + "hotEntry": "美方称不会向乌克兰派兵",
  46 + "mark": 2,
  47 + "sequence": 9
  48 + },
  49 + {
  50 + "hotEntry": "电动车乱停乱充电",
  51 + "mark": 2,
  52 + "sequence": 10
  53 + }
  54 + ],
  55 + "message": "Success",
  56 + "success": true,
  57 + "timestamp": 1712631086296
  58 +}
@@ -45,3 +45,5 @@ export { FollowFirstTabsComponent } from "./components/page/mine/follow/FollowFi @@ -45,3 +45,5 @@ export { FollowFirstTabsComponent } from "./components/page/mine/follow/FollowFi
45 export { MyHomeComponent } from "./components/page/mine/home/MyHomeComponent" 45 export { MyHomeComponent } from "./components/page/mine/home/MyHomeComponent"
46 46
47 export { OtherUserHomeComponent } from "./components/page/mine/home/OtherUserHomeComponent" 47 export { OtherUserHomeComponent } from "./components/page/mine/home/OtherUserHomeComponent"
  48 +
  49 +export { SearchComponent } from "./components/page/search/SearchComponent"
1 import SearcherAboutDataModel from '../../model/SearcherAboutDataModel' 1 import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
  2 +import RouteManager from '../../utils/RouteManager'
2 /** 3 /**
3 * 首页顶部搜索导航栏 4 * 首页顶部搜索导航栏
4 * 竖向滚动实现方案 5 * 竖向滚动实现方案
@@ -61,6 +62,9 @@ export struct FirstTabTopComponent{ @@ -61,6 +62,9 @@ export struct FirstTabTopComponent{
61 .backgroundImage($r('app.media.top_search_bg')) 62 .backgroundImage($r('app.media.top_search_bg'))
62 .backgroundImageSize(ImageSize.Cover) 63 .backgroundImageSize(ImageSize.Cover)
63 .alignItems(VerticalAlign.Center) 64 .alignItems(VerticalAlign.Center)
  65 + .onClick(()=>{
  66 + RouteManager.jumpNewPage("pages/SearchPage")
  67 + })
64 68
65 Image($r('app.media.top_title_bg')) 69 Image($r('app.media.top_title_bg'))
66 .objectFit(ImageFit.Auto) 70 .objectFit(ImageFit.Auto)
  1 +import router from '@ohos.router'
  2 +import { StringUtils } from 'wdKit/src/main/ets/utils/StringUtils'
  3 +import SearcherAboutDataModel from '../../../model/SearcherAboutDataModel'
  4 +import { SearchHistoryItem } from '../../../viewmodel/SearchHistoryItem'
  5 +import { SearchHistoryComponent } from './SearchHistoryComponent'
  6 +import { SearchHotsComponent } from './SearchHotsComponent'
  7 +
  8 +const TAG = "SearchComponent"
  9 +
  10 +@Component
  11 +export struct SearchComponent {
  12 + @State searchTextData: string[] = []
  13 + @State hasInputContent: boolean = false
  14 + @State hasChooseSearch: boolean = false
  15 + private swiperController: SwiperController = new SwiperController()
  16 + @State searchText: string = ''
  17 + controller: TextInputController = new TextInputController()
  18 + @State searchHistoryData: SearchHistoryItem[] = []
  19 + scroller: Scroller = new Scroller()
  20 +
  21 + aboutToAppear() {
  22 + //获取提示滚动
  23 + this.getSearchHint()
  24 + //获取搜索历史
  25 + this.getSearchHistoryData()
  26 + }
  27 +
  28 + getSearchHint() {
  29 + SearcherAboutDataModel.getSearchHintData(getContext(this)).then((value) => {
  30 + if (value != null) {
  31 + this.searchTextData = value
  32 + }
  33 + }).catch((err: Error) => {
  34 + console.log(TAG, JSON.stringify(err))
  35 + })
  36 + }
  37 +
  38 + getSearchHistoryData() {
  39 + this.searchHistoryData = SearcherAboutDataModel.getSearchHistoryData()
  40 + }
  41 +
  42 + build() {
  43 + Column() {
  44 + this.searchInputComponent()
  45 + if (!this.hasInputContent) {
  46 + Scroll(this.scroller) {
  47 + Column() {
  48 + SearchHistoryComponent({ searchHistoryData: $searchHistoryData })
  49 +
  50 + //分隔符
  51 + Divider()
  52 + .width('100%')
  53 + .height('1lpx')
  54 + .color($r('app.color.color_EDEDED'))
  55 + .strokeWidth('1lpx')
  56 +
  57 + SearchHotsComponent()
  58 + }
  59 + }
  60 + .scrollable(ScrollDirection.Vertical)
  61 + .scrollBar(BarState.Off)
  62 + .width('100%')
  63 + .height('100%')
  64 + .padding({ left: '31lpx', right: '31lpx' })
  65 + } else {
  66 + if (this.hasChooseSearch) {
  67 + //搜索结果
  68 +
  69 + //搜索结果为null(空布局 + 为你推荐)
  70 + } else {
  71 + //联想搜索
  72 + }
  73 + }
  74 + }.height('100%')
  75 + .width('100%')
  76 + }
  77 +
  78 + //搜索框
  79 + @Builder searchInputComponent() {
  80 + Row() {
  81 + //左
  82 + Stack({ alignContent: Alignment.Start }) {
  83 + if (this.searchTextData != null && this.searchTextData.length > 0 && !this.hasInputContent) {
  84 + Swiper(this.swiperController) {
  85 + ForEach(this.searchTextData, (item: string, index: number) => {
  86 + Text(item)
  87 + .fontWeight('400lpx')
  88 + .fontSize('25lpx')
  89 + .fontColor($r('app.color.color_666666'))
  90 + .lineHeight('35lpx')
  91 + .textAlign(TextAlign.Start)
  92 + .maxLines(1)
  93 + .textOverflow({ overflow: TextOverflow.Clip })
  94 + .margin({ left: '40lpx' })
  95 + })
  96 + }
  97 + .loop(true)
  98 + .autoPlay(true)
  99 + .interval(3000)
  100 + .indicator(false)
  101 + .vertical(true)
  102 + .enabled(false)
  103 + .focusable(false)
  104 + }
  105 + TextInput({ text: this.searchText, placeholder: '', controller: this.controller })
  106 + .caretColor(Color.Pink)
  107 + .fontSize('27lpx')
  108 + .fontColor(Color.Black)
  109 + .onChange((value: string) => {
  110 + this.searchText = value
  111 + if (this.searchText.length > 0) {
  112 + this.hasInputContent = false
  113 + } else {
  114 + this.hasInputContent = true
  115 + }
  116 + })
  117 + .backgroundColor($r('app.color.color_transparent'))
  118 + }
  119 + .backgroundImage($r('app.media.search_page_input_bg'))
  120 + .backgroundImageSize(ImageSize.Cover)
  121 + .layoutWeight(1)
  122 + .height('69lpx')
  123 +
  124 + //右
  125 + Text("取消")
  126 + .textAlign(TextAlign.Center)
  127 + .fontWeight('400lpx')
  128 + .fontSize('31lpx')
  129 + .lineHeight('58lpx')
  130 + .fontColor($r('app.color.color_222222'))
  131 + .width('125lpx')
  132 + .height('58lpx')
  133 + .onClick(() => {
  134 + router.back()
  135 + // SearcherAboutDataModel.putSearchHistoryData(this.searchText)
  136 + // if(StringUtils.isNotEmpty(this.searchText)){
  137 + // if(this.searchHistoryData.length===10){
  138 + // this.searchHistoryData.pop()
  139 + // }
  140 + // this.searchHistoryData.push(new SearchHistoryItem(this.searchText))
  141 + // }
  142 + })
  143 + }
  144 + .height('85lpx')
  145 + .padding({ left: '31lpx' })
  146 + .alignItems(VerticalAlign.Center)
  147 + .margin({ bottom: '36lpx' })
  148 + }
  149 +}
  1 +import { SearchHistoryItem } from '../../../viewmodel/SearchHistoryItem'
  2 +
  3 +
  4 +/**
  5 + * 搜索历史
  6 + */
  7 +@Component
  8 +export struct SearchHistoryComponent{
  9 + @Link searchHistoryData:SearchHistoryItem[]
  10 +
  11 + build(){
  12 + Column(){
  13 + Row(){
  14 + Text("搜索历史")
  15 + .textAlign(TextAlign.Center)
  16 + .fontWeight('400lpx')
  17 + .fontSize('27lpx')
  18 + .lineHeight('38lpx')
  19 + .fontColor($r('app.color.color_999999'))
  20 + .height('38lpx')
  21 +
  22 + Image($r('app.media.search_delete_icon'))
  23 + .height('31lpx')
  24 + .width('31lpx')
  25 + .objectFit(ImageFit.Auto)
  26 + .onClick(()=>{
  27 + //清空记录
  28 + })
  29 + }.justifyContent(FlexAlign.SpaceBetween)
  30 + .margin({bottom:'17lpx'})
  31 + .width('100%')
  32 +
  33 + Grid(){
  34 + ForEach(this.searchHistoryData,(item:SearchHistoryItem,index:number)=>{
  35 + GridItem(){
  36 + Row(){
  37 + Text(`${item.searchContent}`)
  38 + .height('23lpx')
  39 + .fontColor($r('app.color.color_222222'))
  40 + .fontSize('23lpx')
  41 + .maxLines(1)
  42 + .constraintSize({maxWidth:index%2 === 0?'270lpx':'250lpx'})
  43 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  44 + .textAlign(TextAlign.Start)
  45 +
  46 + Image($r('app.media.search_item_delete_icon'))
  47 + .width('46lpx')
  48 + .height('46lpx')
  49 + .margin({right:'31lpx',left:'4lpx'})
  50 + .interpolation(ImageInterpolation.Medium)
  51 + .objectFit(ImageFit.Auto)
  52 +
  53 + Blank()
  54 +
  55 + if(index%2 === 0 && index != this.searchHistoryData.length-1 ){
  56 + Divider()
  57 + .width('2lpx')
  58 + .height('23lpx')
  59 + .color($r('app.color.color_CCCCCC'))
  60 + .strokeWidth('2lpx')
  61 + .vertical(true)
  62 + }
  63 + }.height('100%')
  64 + .alignItems(VerticalAlign.Center)
  65 + .width('100%')
  66 + .margin({left:index%2 === 1?'23lpx':'0lpx'})
  67 + }.onClick(()=>{
  68 + })
  69 + .height('46lpx')
  70 + })
  71 + }
  72 + .height(this.getCategoryViewHeight())
  73 + .rowsTemplate(this.getCategoryRowTmpl())
  74 + .columnsTemplate('1fr 1fr')
  75 + .rowsGap('23lpx')
  76 + }
  77 + .margin({bottom:'46lpx'})
  78 + }
  79 +
  80 + getCategoryRowCount() {
  81 + return Math.ceil(this.searchHistoryData.length / 2);
  82 + }
  83 +
  84 + getCategoryRowTmpl() {
  85 + const count = this.getCategoryRowCount();
  86 + const arr: string[] = new Array(count || 1).fill('1fr');
  87 + console.log('tmpl ', arr.join(' '))
  88 + return arr.join(' ');
  89 + }
  90 +
  91 + getCategoryViewHeight() {
  92 + return `${46 * this.getCategoryRowCount()}lpx`;
  93 + }
  94 +}
  1 +import SearcherAboutDataModel from '../../../model/SearcherAboutDataModel'
  2 +import { SearchHotContentItem } from '../../../viewmodel/SearchHotContentItem'
  3 +
  4 +const TAG = "SearchHotsComponent"
  5 +
  6 +/**
  7 + * 热门搜索
  8 + */
  9 +@Component
  10 +export struct SearchHotsComponent{
  11 + @State searchHotsData:SearchHotContentItem[] = []
  12 +
  13 + aboutToAppear(){
  14 + //获取搜索热词
  15 + this.getSearchHotsData()
  16 + }
  17 +
  18 + getSearchHotsData(){
  19 + SearcherAboutDataModel.getSearchHotsData(getContext(this)).then((value)=>{
  20 + if(value!=null){
  21 + this.searchHotsData = value
  22 + }
  23 + }).catch((err:Error)=>{
  24 + console.log(TAG,JSON.stringify(err))
  25 + })
  26 + }
  27 +
  28 + build(){
  29 + Column(){
  30 + Row() {
  31 + Image($r('app.media.search_hot_icon'))
  32 + .width('46lpx')
  33 + .height('46lpx')
  34 + .objectFit(ImageFit.Auto)
  35 + .margin({right:'8lpx'})
  36 + .interpolation(ImageInterpolation.Medium)
  37 +
  38 + Text("热门搜索")
  39 + .textAlign(TextAlign.Center)
  40 + .fontWeight('600lpx')
  41 + .fontSize('33lpx')
  42 + .lineHeight('46lpx')
  43 + .fontColor($r('app.color.color_222222'))
  44 + .height('38lpx')
  45 + }
  46 + .width('100%')
  47 +
  48 + List(){
  49 + ForEach(this.searchHotsData,(item:SearchHotContentItem,index:number)=>{
  50 + ListItem(){
  51 + Column(){
  52 + Column(){
  53 + Row(){
  54 + Row(){
  55 + if(item.sequence <=3){
  56 + Image(item.sequence===1?$r('app.media.search_hot_num1'):item.sequence===2?$r('app.media.search_hot_num2'):$r('app.media.search_hot_num3'))
  57 + .width('27lpx')
  58 + .height('35lpx')
  59 + .objectFit(ImageFit.Auto)
  60 + .margin({right:'12lpx'})
  61 + }else {
  62 + Text(`${item.sequence}`)
  63 + .height('31lpx')
  64 + .fontColor($r('app.color.color_666666'))
  65 + .fontSize('27lpx')
  66 + .fontWeight('400lpx')
  67 + .lineHeight('31lpx')
  68 + .margin({right:'12lpx'})
  69 + }
  70 + Text(`${item.hotEntry}`)
  71 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  72 + .fontColor($r('app.color.color_222222'))
  73 + .fontSize('31lpx')
  74 + .maxLines(1)
  75 + .fontWeight('400lpx')
  76 + .lineHeight('42lpx')
  77 + }.layoutWeight(1)
  78 + if(item.mark!=0){
  79 + Image(item.mark===1?$r('app.media.search_hots_mark1'):$r('app.media.search_hots_mark2'))
  80 + .width('42lpx')
  81 + .height('31lpx')
  82 + .objectFit(ImageFit.Auto)
  83 + .interpolation(ImageInterpolation.Medium)
  84 + }
  85 + }.alignItems(VerticalAlign.Center)
  86 + .height('84lpx')
  87 + .justifyContent(FlexAlign.SpaceBetween)
  88 +
  89 + if(index != this.searchHotsData.length-1 ){
  90 + Divider()
  91 + .width('100%')
  92 + .height('1lpx')
  93 + .color($r('app.color.color_F5F5F5'))
  94 + .strokeWidth('1lpx')
  95 + }
  96 + }.height('108lpx')
  97 + .justifyContent(FlexAlign.Center)
  98 + .alignItems(HorizontalAlign.Start)
  99 + .padding({left:'27lpx'})
  100 + }
  101 + }
  102 + .onClick(()=>{
  103 + })
  104 + .height('117lpx')
  105 + })
  106 + }.onScrollFrameBegin((offset, state) => {
  107 + return { offsetRemain: 0 }
  108 + }).layoutWeight(1)
  109 + }.width('100%')
  110 + .height('100%')
  111 + .margin({top:'46lpx'})
  112 + }
  113 +}
@@ -3,6 +3,8 @@ import { Logger, ResourcesUtils } from 'wdKit'; @@ -3,6 +3,8 @@ import { Logger, ResourcesUtils } from 'wdKit';
3 import { ResponseDTO, WDHttp } from 'wdNetwork'; 3 import { ResponseDTO, WDHttp } from 'wdNetwork';
4 import HashMap from '@ohos.util.HashMap'; 4 import HashMap from '@ohos.util.HashMap';
5 import { HttpUrlUtils } from '../network/HttpUrlUtils'; 5 import { HttpUrlUtils } from '../network/HttpUrlUtils';
  6 +import { SearchHistoryItem } from '../viewmodel/SearchHistoryItem';
  7 +import { SearchHotContentItem } from '../viewmodel/SearchHotContentItem';
6 const TAG = "SearcherAboutDataModel" 8 const TAG = "SearcherAboutDataModel"
7 9
8 /** 10 /**
@@ -10,6 +12,7 @@ const TAG = "SearcherAboutDataModel" @@ -10,6 +12,7 @@ const TAG = "SearcherAboutDataModel"
10 */ 12 */
11 class SearcherAboutDataModel{ 13 class SearcherAboutDataModel{
12 private static instance: SearcherAboutDataModel; 14 private static instance: SearcherAboutDataModel;
  15 + searchHistoryData:SearchHistoryItem[] = []
13 16
14 private constructor() { } 17 private constructor() { }
15 18
@@ -25,6 +28,53 @@ class SearcherAboutDataModel{ @@ -25,6 +28,53 @@ class SearcherAboutDataModel{
25 } 28 }
26 29
27 /** 30 /**
  31 + * 静态搜索历史记录
  32 + */
  33 + getSearchHistoryData():SearchHistoryItem[]{
  34 + if(this.searchHistoryData.length > 0){
  35 + return this.searchHistoryData
  36 + }
  37 + this.searchHistoryData.push(new SearchHistoryItem("评论"))
  38 + this.searchHistoryData.push(new SearchHistoryItem("关注关注"))
  39 + this.searchHistoryData.push(new SearchHistoryItem("收藏收藏收藏"))
  40 + this.searchHistoryData.push(new SearchHistoryItem("历史"))
  41 + this.searchHistoryData.push(new SearchHistoryItem("消息消息消息消息消息"))
  42 + this.searchHistoryData.push(new SearchHistoryItem("留言板留言板留言板留言板留言板"))
  43 + this.searchHistoryData.push(new SearchHistoryItem("预约"))
  44 + return this.searchHistoryData
  45 + }
  46 +
  47 + // async putSearchHistoryData(content:string){
  48 + // let history = await SPHelper.default.get(SearcherAboutDataModel.SEARCH_HISTORY_KEY, '[]') as string;
  49 + // this.searchHistoryData = JSON.parse(history)
  50 + // this.searchHistoryData.push(new SearchHistoryItem(content))
  51 + // await SPHelper.default.save(SearcherAboutDataModel.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistoryData));
  52 + // }
  53 +
  54 + // getSearchHistoryData():Promise<SearchHistoryItem[]>{
  55 + // return new Promise<SearchHistoryItem[]>(async (success, error) => {
  56 + // if(this.searchHistoryData!=null && this.searchHistoryData.length>0){
  57 + // success(this.searchHistoryData)
  58 + // return
  59 + // }
  60 + // try {
  61 + // let history = await SPHelper.default.get(SearcherAboutDataModel.SEARCH_HISTORY_KEY, '') as string;
  62 + // console.log('ycg',history);
  63 + // }catch (error){
  64 + // console.log('ycg',"1111");
  65 + // }
  66 + //
  67 + // this.searchHistoryData = JSON.parse(history)
  68 + // this.searchHistoryData.push(new SearchHistoryItem("评论"))
  69 + // this.searchHistoryData.push(new SearchHistoryItem("关注关注"))
  70 + // this.searchHistoryData.push(new SearchHistoryItem("收藏收藏收藏"))
  71 + //
  72 + // success(this.searchHistoryData)
  73 + // })
  74 + // }
  75 +
  76 +
  77 + /**
28 * 首页 搜索提示滚动内容 78 * 首页 搜索提示滚动内容
29 */ 79 */
30 getSearchHintData(context: Context): Promise<string[]> { 80 getSearchHintData(context: Context): Promise<string[]> {
@@ -62,6 +112,45 @@ class SearcherAboutDataModel{ @@ -62,6 +112,45 @@ class SearcherAboutDataModel{
62 return compRes.data 112 return compRes.data
63 } 113 }
64 114
  115 + /**
  116 + * 搜索主页 热词
  117 + */
  118 + getSearchHotsData(context: Context): Promise<SearchHotContentItem[]> {
  119 + return new Promise<SearchHotContentItem[]>((success, error) => {
  120 + Logger.info(TAG, `getSearchHintData start`);
  121 + this.fetchSearchHotsData().then((navResDTO: ResponseDTO<SearchHotContentItem[]>) => {
  122 + if (!navResDTO || navResDTO.code != 0) {
  123 + success(this.getSearchHotsDataLocal(context))
  124 + return
  125 + }
  126 + Logger.info(TAG, "getSearchHotsData then,getSearchHotsData.timeStamp:" + navResDTO.timestamp);
  127 + let navigationBean = navResDTO.data as SearchHotContentItem[]
  128 + success(navigationBean);
  129 + }).catch((err: Error) => {
  130 + Logger.error(TAG, `getSearchHotsData catch, error.name : ${err.name}, error.message:${err.message}`);
  131 + success(this.getSearchHotsDataLocal(context))
  132 + })
  133 + })
  134 + }
  135 +
  136 + fetchSearchHotsData() {
  137 + let url = HttpUrlUtils.getSearchHotsDataUrl()
  138 + let headers: HashMap<string, string> = HttpUrlUtils.getYcgCommonHeaders();
  139 + return WDHttp.get<ResponseDTO<SearchHotContentItem[]>>(url, headers)
  140 + };
  141 +
  142 + async getSearchHotsDataLocal(context: Context): Promise<SearchHotContentItem[]> {
  143 + Logger.info(TAG, `getSearchHotsDataLocal start`);
  144 + let compRes: ResponseDTO<SearchHotContentItem[]> | null = await ResourcesUtils.getResourcesJson<ResponseDTO<SearchHotContentItem[]>>('search_hots_data.json' ,context);
  145 + if (!compRes || !compRes.data) {
  146 + Logger.info(TAG, `getSearchHotsDataLocal compRes is empty`);
  147 + return []
  148 + }
  149 + Logger.info(TAG, `getSearchHotsDataLocal compRes : ${JSON.stringify(compRes)}`);
  150 + return compRes.data
  151 + }
  152 +
  153 +
65 154
66 155
67 } 156 }
@@ -121,6 +121,11 @@ export class HttpUrlUtils { @@ -121,6 +121,11 @@ export class HttpUrlUtils {
121 */ 121 */
122 static readonly SEARCH_HINT_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hints"; 122 static readonly SEARCH_HINT_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hints";
123 123
  124 + /**
  125 + * 搜索主页 热词
  126 + */
  127 + static readonly SEARCH_HOTS_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hots";
  128 +
124 private static hostUrl: string = HttpUrlUtils.HOST_UAT; 129 private static hostUrl: string = HttpUrlUtils.HOST_UAT;
125 130
126 static getCommonHeaders(): HashMap<string, string> { 131 static getCommonHeaders(): HashMap<string, string> {
@@ -308,6 +313,10 @@ export class HttpUrlUtils { @@ -308,6 +313,10 @@ export class HttpUrlUtils {
308 return url 313 return url
309 } 314 }
310 315
  316 + static getSearchHotsDataUrl() {
  317 + let url = HttpUrlUtils.HOST_SIT + HttpUrlUtils.SEARCH_HOTS_DATA_PATH
  318 + return url
  319 + }
311 320
312 /** 321 /**
313 * 点赞操作 322 * 点赞操作
@@ -40,7 +40,7 @@ export class PageViewModel extends BaseViewModel { @@ -40,7 +40,7 @@ export class PageViewModel extends BaseViewModel {
40 if (mock_switch) { 40 if (mock_switch) {
41 return this.getBottomNavDataMock(context); 41 return this.getBottomNavDataMock(context);
42 } 42 }
43 - return this.getNavData(); 43 + return this.getNavData(context);
44 } 44 }
45 45
46 async getBottomNavDataMock(context?: Context): Promise<NavigationBodyDTO> { 46 async getBottomNavDataMock(context?: Context): Promise<NavigationBodyDTO> {
@@ -54,12 +54,13 @@ export class PageViewModel extends BaseViewModel { @@ -54,12 +54,13 @@ export class PageViewModel extends BaseViewModel {
54 return compRes.data 54 return compRes.data
55 } 55 }
56 56
57 - private getNavData(): Promise<NavigationBodyDTO> { 57 + private getNavData(context?: Context): Promise<NavigationBodyDTO> {
58 return new Promise<NavigationBodyDTO>((success, error) => { 58 return new Promise<NavigationBodyDTO>((success, error) => {
59 Logger.info(TAG, `getNavData start`); 59 Logger.info(TAG, `getNavData start`);
60 PageRepository.fetchNavigationDataApi().then((navResDTO: ResponseDTO<NavigationBodyDTO>) => { 60 PageRepository.fetchNavigationDataApi().then((navResDTO: ResponseDTO<NavigationBodyDTO>) => {
61 if (this.isRespondsInvalid(navResDTO, 'getNavData')) { 61 if (this.isRespondsInvalid(navResDTO, 'getNavData')) {
62 - error("page data invalid"); 62 + // error("page data invalid");
  63 + success(this.getBottomNavDataMock(context))
63 return 64 return
64 } 65 }
65 Logger.info(TAG, "getNavData then,navResDTO.timeStamp:" + navResDTO.timestamp); 66 Logger.info(TAG, "getNavData then,navResDTO.timeStamp:" + navResDTO.timestamp);
  1 +export class SearchHistoryItem{
  2 + searchContent:string = ""
  3 + searchUserId:string = ""
  4 + searchTime:string = ""
  5 +
  6 + constructor(searchContent: string,searchUserId?:string,searchTime?:string) {
  7 + this.searchContent = searchContent
  8 + this.searchUserId = searchUserId
  9 + this.searchTime = searchTime
  10 + }
  11 +}
  1 +export class SearchHotContentItem{
  2 + hotEntry:string = ""
  3 + mark:number = 0 //1 热 2 新
  4 + sequence:number = 0//序号
  5 +
  6 + constructor(hotEntry: string , mark: number , sequence: number ) {
  7 + this.hotEntry = hotEntry
  8 + this.mark = mark
  9 + this.sequence = sequence
  10 + }
  11 +}
@@ -98,9 +98,12 @@ instance.interceptors.response.use(// 响应拦截器response类型就是Axios.r @@ -98,9 +98,12 @@ instance.interceptors.response.use(// 响应拦截器response类型就是Axios.r
98 // console.log(error.request) 98 // console.log(error.request)
99 // console.log(error.response) 99 // console.log(error.response)
100 // 这里用来处理http常见错误,进行全局提示 100 // 这里用来处理http常见错误,进行全局提示
101 - let message = buildErrorMsg(error.response?.status); 101 + if(error!=null && error.response!=null ){
  102 + let message = buildErrorMsg(error.response.status);
102 // 错误消息可以使用全局弹框展示出来 103 // 错误消息可以使用全局弹框展示出来
103 console.log(`httpStatus:${error.response?.status}-${message},请检查网络或联系管理员!`) 104 console.log(`httpStatus:${error.response?.status}-${message},请检查网络或联系管理员!`)
  105 + }
  106 +
104 return Promise.reject(error); 107 return Promise.reject(error);
105 } 108 }
106 ); 109 );
@@ -100,7 +100,7 @@ export class HttpUrlUtils { @@ -100,7 +100,7 @@ export class HttpUrlUtils {
100 */ 100 */
101 static readonly APPOINTMENT_editUserDetail_PATH: string = "/api/rmrb-user-center/user/zh/c/editUserDetail"; 101 static readonly APPOINTMENT_editUserDetail_PATH: string = "/api/rmrb-user-center/user/zh/c/editUserDetail";
102 /** 102 /**
103 - * 资料编辑 更新普通用户名 103 + * 资料编辑 更新普通用户名(用户名、身份证号、邮箱、手机号修改url)
104 */ 104 */
105 static readonly APPOINTMENT_editUserDetail1_PATH: string = "/api/rmrb-user-center/user/zh/c/completeUserInfo"; 105 static readonly APPOINTMENT_editUserDetail1_PATH: string = "/api/rmrb-user-center/user/zh/c/completeUserInfo";
106 /** 106 /**
@@ -177,6 +177,10 @@ export class HttpUrlUtils { @@ -177,6 +177,10 @@ export class HttpUrlUtils {
177 */ 177 */
178 static readonly SEARCH_HINT_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hints"; 178 static readonly SEARCH_HINT_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hints";
179 /** 179 /**
  180 + * 搜索主页 热词
  181 + */
  182 + static readonly SEARCH_HOTS_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hots";
  183 + /**
180 * 早晚报列表 184 * 早晚报列表
181 * 根据页面id获取页面楼层列表 185 * 根据页面id获取页面楼层列表
182 * https://pdapis.pdnews.cn/api/rmrb-bff-display-zh/display/zh/c/pageInfo?pageId=28927 186 * https://pdapis.pdnews.cn/api/rmrb-bff-display-zh/display/zh/c/pageInfo?pageId=28927
@@ -527,6 +531,11 @@ export class HttpUrlUtils { @@ -527,6 +531,11 @@ export class HttpUrlUtils {
527 return url 531 return url
528 } 532 }
529 533
  534 + static getSearchHotsDataUrl() {
  535 + let url = HttpUrlUtils.HOST_SIT + HttpUrlUtils.SEARCH_HOTS_DATA_PATH
  536 + return url
  537 + }
  538 +
530 539
531 540
532 // static getYcgCommonHeaders(): HashMap<string, string> { 541 // static getYcgCommonHeaders(): HashMap<string, string> {
1 import ArrayList from '@ohos.util.ArrayList'; 1 import ArrayList from '@ohos.util.ArrayList';
2 -import { Action } from 'wdBean'  
3 -import { WDRouterPage } from './WDRouterPage' 2 +import { Action } from 'wdBean';
  3 +import { WDRouterPage } from './WDRouterPage';
4 4
5 interface HandleObject { 5 interface HandleObject {
6 handle: (action: Action) => (WDRouterPage | undefined) 6 handle: (action: Action) => (WDRouterPage | undefined)
@@ -75,6 +75,8 @@ export function registerRouter() { @@ -75,6 +75,8 @@ export function registerRouter() {
75 return WDRouterPage.imageTextDetailPage 75 return WDRouterPage.imageTextDetailPage
76 } else if (action.params?.pageID == "BroadcastPage") { 76 } else if (action.params?.pageID == "BroadcastPage") {
77 return WDRouterPage.broadcastPage 77 return WDRouterPage.broadcastPage
  78 + } else if (action.params?.pageID == "LIVE_DETAILS_PAGER") {
  79 + return WDRouterPage.detailPlayLivePage
78 } 80 }
79 return undefined 81 return undefined
80 }) 82 })
@@ -86,4 +86,6 @@ export class WDRouterPage { @@ -86,4 +86,6 @@ export class WDRouterPage {
86 86
87 //播报页面 87 //播报页面
88 static broadcastPage = new WDRouterPage("phone", "ets/pages/broadcast/BroadcastPage"); 88 static broadcastPage = new WDRouterPage("phone", "ets/pages/broadcast/BroadcastPage");
  89 + //搜索主页
  90 + static searchPage = new WDRouterPage("wdComponent", "ets/pages/SearchPage");
89 } 91 }
This file is too large to display.
@@ -60,3 +60,5 @@ export { BroadcastPageComponent } from "./src/main/ets/components/broadcast/Broa @@ -60,3 +60,5 @@ export { BroadcastPageComponent } from "./src/main/ets/components/broadcast/Broa
60 60
61 export { FirstTabTopSearchComponent } from "./src/main/ets/components/search/FirstTabTopSearchComponent" 61 export { FirstTabTopSearchComponent } from "./src/main/ets/components/search/FirstTabTopSearchComponent"
62 62
  63 +export { ListHasNoMoreDataUI } from "./src/main/ets/components/reusable/ListHasNoMoreDataUI"
  64 +
@@ -130,10 +130,9 @@ export struct BroadcastPageComponent { @@ -130,10 +130,9 @@ export struct BroadcastPageComponent {
130 } 130 }
131 } 131 }
132 .layoutWeight(1) 132 .layoutWeight(1)
133 - // @ts-ignore  
134 - .onScrollFrameBegin((offset, state) => {  
135 - console.log('ccc',String(offset), state)  
136 - }) 133 + // .onScrollFrameBegin((offset, state) => {
  134 + // console.log('ccc',String(offset), state)
  135 + // })
137 .onReachStart(() => { 136 .onReachStart(() => {
138 console.log('onReachStart----->',) 137 console.log('onReachStart----->',)
139 }) 138 })
@@ -15,7 +15,6 @@ const LOCAL_CHANNEL: string = '地方频道' @@ -15,7 +15,6 @@ const LOCAL_CHANNEL: string = '地方频道'
15 @CustomDialog 15 @CustomDialog
16 struct ChannelDialog { 16 struct ChannelDialog {
17 @State dragItem: number = -1 17 @State dragItem: number = -1
18 - @State item: number = -1  
19 private dragRefOffsetX: number = 0 18 private dragRefOffsetX: number = 0
20 private dragRefOffsetY: number = 0 19 private dragRefOffsetY: number = 0
21 @State offsetX: number = 0 20 @State offsetX: number = 0
@@ -235,7 +234,7 @@ struct ChannelDialog { @@ -235,7 +234,7 @@ struct ChannelDialog {
235 .alignContent(Alignment.Start) 234 .alignContent(Alignment.Start)
236 .height(36) 235 .height(36)
237 .onClick(() => { 236 .onClick(() => {
238 - AppStorage.set('indexSettingChannelId',item.channelId) 237 + AppStorage.set('indexSettingChannelId', item.channelId)
239 }) 238 })
240 }) 239 })
241 } 240 }
@@ -308,6 +307,7 @@ struct ChannelDialog { @@ -308,6 +307,7 @@ struct ChannelDialog {
308 }) 307 })
309 .zIndex(this.dragItem == item.num ? 1 : 0) 308 .zIndex(this.dragItem == item.num ? 1 : 0)
310 .translate(this.dragItem == item.num ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 }) 309 .translate(this.dragItem == item.num ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
  310 +
311 .gesture( 311 .gesture(
312 GestureGroup(GestureMode.Sequence, 312 GestureGroup(GestureMode.Sequence,
313 PanGesture({ fingers: 1, direction: null, distance: 0 }) 313 PanGesture({ fingers: 1, direction: null, distance: 0 })
@@ -324,12 +324,20 @@ struct ChannelDialog { @@ -324,12 +324,20 @@ struct ChannelDialog {
324 .onActionEnd((event: GestureEvent) => { 324 .onActionEnd((event: GestureEvent) => {
325 animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { 325 animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
326 this.dragItem = -1 326 this.dragItem = -1
  327 + this.offsetX = 0
  328 + this.offsetY = 0
  329 + this.dragRefOffsetX = 0
  330 + this.dragRefOffsetY = 0
327 }) 331 })
328 }) 332 })
329 ) 333 )
330 .onCancel(() => { 334 .onCancel(() => {
331 animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { 335 animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
332 this.dragItem = -1 336 this.dragItem = -1
  337 + this.offsetX = 0
  338 + this.offsetY = 0
  339 + this.dragRefOffsetX = 0
  340 + this.dragRefOffsetY = 0
333 }) 341 })
334 })) 342 }))
335 343
@@ -103,8 +103,7 @@ struct EditUserInfoPage { @@ -103,8 +103,7 @@ struct EditUserInfoPage {
103 .width('100%') 103 .width('100%')
104 .onClick(()=>{ 104 .onClick(()=>{
105 if (i === 1){ 105 if (i === 1){
106 -  
107 - 106 + // TODO 缺失特殊用户判断
108 let params: editModelParams = { 107 let params: editModelParams = {
109 editContent: this.currentUserInfo.userName 108 editContent: this.currentUserInfo.userName
110 } 109 }
@@ -177,7 +176,6 @@ struct EditUserInfoPage { @@ -177,7 +176,6 @@ struct EditUserInfoPage {
177 this.listData.push(...EditInfoViewModel.getEditListInfo(editModel)) 176 this.listData.push(...EditInfoViewModel.getEditListInfo(editModel))
178 }); 177 });
179 } 178 }
180 -  
181 getAreaList(){ 179 getAreaList(){
182 EditInfoViewModel.getAreaList(getContext(this)).then((value) =>{ 180 EditInfoViewModel.getAreaList(getContext(this)).then((value) =>{
183 this.dataSource.push(...value) 181 this.dataSource.push(...value)
@@ -134,6 +134,15 @@ export struct TopNavigationComponent { @@ -134,6 +134,15 @@ export struct TopNavigationComponent {
134 }; 134 };
135 WDRouterRule.jumpWithAction(taskAction) 135 WDRouterRule.jumpWithAction(taskAction)
136 } 136 }
  137 + jumpToLiveDetailsPaper() {
  138 + let taskAction: Action = {
  139 + type: 'JUMP_INNER_NEW_PAGE',
  140 + params: {
  141 + pageID: 'LIVE_DETAILS_PAGER'
  142 + } as Params,
  143 + };
  144 + WDRouterRule.jumpWithAction(taskAction)
  145 + }
137 146
138 build() { 147 build() {
139 Column() { 148 Column() {
@@ -146,7 +155,8 @@ export struct TopNavigationComponent { @@ -146,7 +155,8 @@ export struct TopNavigationComponent {
146 .width(72) 155 .width(72)
147 .height(29) 156 .height(29)
148 .onClick((event: ClickEvent) => { 157 .onClick((event: ClickEvent) => {
149 - this.jumpToENewPaper() 158 + this.jumpToLiveDetailsPaper()
  159 + // this.jumpToENewPaper()
150 }) 160 })
151 161
152 Stack({ alignContent: Alignment.Center }) { 162 Stack({ alignContent: Alignment.Center }) {
1 -import { MineAppointmentItem } from '../../viewmodel/MineAppointmentItem'  
2 -  
3 @CustomDialog 1 @CustomDialog
4 export struct MyCustomDialog { 2 export struct MyCustomDialog {
5 @State title: string = "标题" 3 @State title: string = "标题"
  4 + @State titleShow: boolean = true
6 @State tipValue: string ="提示文字" 5 @State tipValue: string ="提示文字"
  6 + @State tipShow: boolean = true
7 7
8 @State leftText: string = "取消" 8 @State leftText: string = "取消"
9 @State rightText: string = "确认" 9 @State rightText: string = "确认"
@@ -16,16 +16,21 @@ export struct MyCustomDialog { @@ -16,16 +16,21 @@ export struct MyCustomDialog {
16 16
17 build() { 17 build() {
18 Column() { 18 Column() {
  19 + if(this.titleShow){
19 Text(this.title) 20 Text(this.title)
20 .fontSize("32lpx") 21 .fontSize("32lpx")
21 .margin({ top: "40lpx", bottom: "15lpx" }) 22 .margin({ top: "40lpx", bottom: "15lpx" })
22 .fontColor($r('app.color.color_333333')) 23 .fontColor($r('app.color.color_333333'))
23 .fontSize('35lpx') 24 .fontSize('35lpx')
24 .fontWeight('600lpx') 25 .fontWeight('600lpx')
  26 + }
  27 +
  28 + if(this.tipShow){
25 Text(this.tipValue) 29 Text(this.tipValue)
26 .margin({ bottom: "30lpx" }) 30 .margin({ bottom: "30lpx" })
27 .fontSize("27lpx") 31 .fontSize("27lpx")
28 .fontColor($r('app.color.color_B0B0B0')) 32 .fontColor($r('app.color.color_B0B0B0'))
  33 + }
29 34
30 Divider() 35 Divider()
31 .width("100%") 36 .width("100%")
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 * 方案一 使用动画 + 定时器 4 * 方案一 使用动画 + 定时器
5 * 方案二 使用容器组件Swiper(当前) 5 * 方案二 使用容器组件Swiper(当前)
6 */ 6 */
  7 +import { WDRouterPage, WDRouterRule } from 'wdRouter'
7 import SearcherAboutDataModel from '../../model/SearcherAboutDataModel' 8 import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
8 9
9 const TAG = "FirstTabTopSearchComponent" 10 const TAG = "FirstTabTopSearchComponent"
@@ -56,6 +57,9 @@ export struct FirstTabTopSearchComponent{ @@ -56,6 +57,9 @@ export struct FirstTabTopSearchComponent{
56 .padding({left:15}) 57 .padding({left:15})
57 .backgroundImage($r('app.media.background_search')) 58 .backgroundImage($r('app.media.background_search'))
58 .backgroundImageSize(ImageSize.Cover) 59 .backgroundImageSize(ImageSize.Cover)
  60 + .onClick(()=>{
  61 + WDRouterRule.jumpWithPage(WDRouterPage.searchPage)
  62 + })
59 } 63 }
60 64
61 } 65 }
  1 +import router from '@ohos.router'
  2 +import { StringUtils, ToastUtils } from 'wdKit'
  3 +import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
  4 +import { SearchHistoryItem } from '../../viewmodel/SearchHistoryItem'
  5 +import { SearchHistoryComponent } from './SearchHistoryComponent'
  6 +import { SearchHotsComponent } from './SearchHotsComponent'
  7 +
  8 +const TAG = "SearchComponent"
  9 +
  10 +@Component
  11 +export struct SearchComponent {
  12 + @State searchTextData: string[] = []
  13 + @State hasInputContent: boolean = false
  14 + @State hasChooseSearch: boolean = false
  15 + private swiperController: SwiperController = new SwiperController()
  16 + @State searchText: string = ''
  17 + controller: TextInputController = new TextInputController()
  18 + @State searchHistoryData: SearchHistoryItem[] = []
  19 + scroller: Scroller = new Scroller()
  20 +
  21 + aboutToAppear() {
  22 + //获取提示滚动
  23 + this.getSearchHint()
  24 + //获取搜索历史
  25 + this.getSearchHistoryData()
  26 + }
  27 +
  28 +
  29 +
  30 + getSearchHint() {
  31 + SearcherAboutDataModel.getSearchHintData(getContext(this)).then((value) => {
  32 + if (value != null) {
  33 + this.searchTextData = value
  34 + }
  35 + }).catch((err: Error) => {
  36 + console.log(TAG, JSON.stringify(err))
  37 + })
  38 + }
  39 +
  40 + getSearchHistoryData() {
  41 + this.searchHistoryData = SearcherAboutDataModel.getSearchHistoryData()
  42 + }
  43 +
  44 + build() {
  45 + Column() {
  46 + this.searchInputComponent()
  47 + if (!this.hasInputContent) {
  48 + Scroll(this.scroller) {
  49 + Column() {
  50 + if(this.searchHistoryData!=null && this.searchHistoryData.length>0){
  51 + SearchHistoryComponent({ searchHistoryData: $searchHistoryData, onDelHistory: (): void => this.getSearchHistoryData() })
  52 + }
  53 +
  54 + //分隔符
  55 + Divider()
  56 + .width('100%')
  57 + .height('1lpx')
  58 + .color($r('app.color.color_EDEDED'))
  59 + .strokeWidth('1lpx')
  60 +
  61 + SearchHotsComponent()
  62 + }
  63 + }
  64 + .scrollable(ScrollDirection.Vertical)
  65 + .scrollBar(BarState.Off)
  66 + .width('100%')
  67 + .height('100%')
  68 + .padding({ left: '31lpx', right: '31lpx' })
  69 + } else {
  70 + if (this.hasChooseSearch) {
  71 + //搜索结果
  72 +
  73 + //搜索结果为null(空布局 + 为你推荐)
  74 + } else {
  75 + //联想搜索
  76 + }
  77 + }
  78 + }.height('100%')
  79 + .width('100%')
  80 + }
  81 +
  82 + //搜索框
  83 + @Builder searchInputComponent() {
  84 + Row() {
  85 + //左
  86 + Stack({ alignContent: Alignment.Start }) {
  87 + if (this.searchTextData != null && this.searchTextData.length > 0 && !this.hasInputContent) {
  88 + Swiper(this.swiperController) {
  89 + ForEach(this.searchTextData, (item: string, index: number) => {
  90 + Text(item)
  91 + .fontWeight('400lpx')
  92 + .fontSize('25lpx')
  93 + .fontColor($r('app.color.color_666666'))
  94 + .lineHeight('35lpx')
  95 + .textAlign(TextAlign.Start)
  96 + .maxLines(1)
  97 + .textOverflow({ overflow: TextOverflow.Clip })
  98 + .margin({ left: '40lpx' })
  99 + })
  100 + }
  101 + .loop(true)
  102 + .autoPlay(true)
  103 + .interval(3000)
  104 + .indicator(false)
  105 + .vertical(true)
  106 + .enabled(false)
  107 + .focusable(false)
  108 + }
  109 + Row(){
  110 + TextInput({ text: this.searchText, placeholder: '', controller: this.controller })
  111 + .caretColor(Color.Pink)
  112 + .fontSize('27lpx')
  113 + .layoutWeight(1)
  114 + .fontColor(Color.Black)
  115 + .onChange((value: string) => {
  116 + this.searchText = value
  117 + if (this.searchText.length > 0) {
  118 + this.hasInputContent = true
  119 + } else {
  120 + this.hasInputContent = false
  121 + }
  122 + })
  123 + .backgroundColor($r('app.color.color_transparent'))
  124 + .defaultFocus(true)
  125 + if(this.hasInputContent){
  126 + Image($r('app.media.search_input_del_icon'))
  127 + .width('31lpx')
  128 + .height('31lpx')
  129 + .objectFit(ImageFit.Cover)
  130 + .interpolation(ImageInterpolation.High)
  131 + .onClick(()=>{
  132 + this.searchText = ""
  133 + this.hasInputContent = false
  134 + })
  135 + }
  136 + }.padding({right:'30lpx'})
  137 + .layoutWeight(1)
  138 + }
  139 + .backgroundImage($r('app.media.search_page_input_bg'))
  140 + .backgroundImageSize(ImageSize.Cover)
  141 + .layoutWeight(1)
  142 + .height('69lpx')
  143 +
  144 + //TODO 需要修改输入法 换行
  145 + //右
  146 + Text(this.hasInputContent?"搜索":"取消")
  147 + .textAlign(TextAlign.Center)
  148 + .fontWeight('400lpx')
  149 + .fontSize('31lpx')
  150 + .lineHeight('58lpx')
  151 + .fontColor($r('app.color.color_222222'))
  152 + .width('125lpx')
  153 + .height('58lpx')
  154 + .onClick(() => {
  155 + if(this.hasInputContent){
  156 + if(StringUtils.isNotEmpty(this.searchText)){
  157 + SearcherAboutDataModel.putSearchHistoryData(this.searchText)
  158 + this.getSearchHistoryData()
  159 + ToastUtils.shortToast("插入一条搜索记录")
  160 + }
  161 + }else{
  162 + router.back()
  163 + }
  164 + })
  165 + }
  166 + .height('85lpx')
  167 + .padding({ left: '31lpx' })
  168 + .alignItems(VerticalAlign.Center)
  169 + .margin({ bottom: '36lpx' })
  170 + }
  171 +}
  1 +import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
  2 +import { SearchHistoryItem } from '../../viewmodel/SearchHistoryItem'
  3 +import { MyCustomDialog } from '../reusable/MyCustomDialog'
  4 +
  5 +/**
  6 + * 搜索历史
  7 + */
  8 +@Component
  9 +export struct SearchHistoryComponent{
  10 + @Link searchHistoryData:SearchHistoryItem[]
  11 + onDelHistory?: () => void;
  12 + dialogController: CustomDialogController = new CustomDialogController({
  13 + builder: MyCustomDialog({
  14 + cancel: this.onCancel,
  15 + confirm: () => {
  16 + this.onAccept()
  17 + },
  18 + title: "确认清空历史记录",
  19 + tipShow:false
  20 + }),
  21 + autoCancel: true,
  22 + alignment: DialogAlignment.Center,
  23 + offset: { dx: 0, dy: -20 },
  24 + gridCount: 4,
  25 + customStyle: false
  26 + })
  27 +
  28 + onAccept(){
  29 + console.info('Callback when the second button is clicked')
  30 + //清空记录
  31 + this.searchHistoryData = []
  32 + SearcherAboutDataModel.delSearchHistoryData()
  33 + }
  34 +
  35 + onCancel() {
  36 + console.info('Callback when the first button is clicked')
  37 + }
  38 +
  39 + build(){
  40 + Column(){
  41 + Row(){
  42 + Text("搜索历史")
  43 + .textAlign(TextAlign.Center)
  44 + .fontWeight('400lpx')
  45 + .fontSize('27lpx')
  46 + .lineHeight('38lpx')
  47 + .fontColor($r('app.color.color_999999'))
  48 + .height('38lpx')
  49 +
  50 + Image($r('app.media.search_delete_icon'))
  51 + .height('31lpx')
  52 + .width('31lpx')
  53 + .interpolation(ImageInterpolation.High)
  54 + .objectFit(ImageFit.Cover)
  55 + .onClick(()=>{
  56 + //弹框提示
  57 + this.dialogController.open()
  58 + })
  59 + }.justifyContent(FlexAlign.SpaceBetween)
  60 + .margin({bottom:'17lpx'})
  61 + .width('100%')
  62 +
  63 + Grid(){
  64 + ForEach(this.searchHistoryData,(item:SearchHistoryItem,index:number)=>{
  65 + GridItem(){
  66 + Row(){
  67 + Text(`${item.searchContent}`)
  68 + .fontColor($r('app.color.color_222222'))
  69 + .fontSize('31lpx')
  70 + .fontWeight('400lpx')
  71 + .lineHeight('46lpx')
  72 + .maxLines(1)
  73 + .constraintSize({maxWidth:index%2 === 0?'270lpx':'250lpx'})
  74 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  75 + .textAlign(TextAlign.Start)
  76 +
  77 + Image($r('app.media.search_item_delete_icon'))
  78 + .width('46lpx')
  79 + .height('46lpx')
  80 + .margin({right:'31lpx',left:'4lpx'})
  81 + .interpolation(ImageInterpolation.High)
  82 + .objectFit(ImageFit.Cover)
  83 + .onClick(()=>{
  84 + SearcherAboutDataModel.delSearchSingleHistoryData(index)
  85 + if (this.onDelHistory !== undefined) {
  86 + this.onDelHistory()
  87 + }
  88 + })
  89 +
  90 + Blank()
  91 +
  92 + if(index%2 === 0 && index != this.searchHistoryData.length-1 ){
  93 + Divider()
  94 + .width('2lpx')
  95 + .height('23lpx')
  96 + .color($r('app.color.color_CCCCCC'))
  97 + .strokeWidth('2lpx')
  98 + .vertical(true)
  99 + }
  100 + }.height('100%')
  101 + .alignItems(VerticalAlign.Center)
  102 + .width('100%')
  103 + .margin({left:index%2 === 1?'23lpx':'0lpx'})
  104 + }.onClick(()=>{
  105 + })
  106 + .height('46lpx')
  107 + })
  108 + }
  109 + .height(this.getCategoryViewHeight())
  110 + .rowsTemplate(this.getCategoryRowTmpl())
  111 + .columnsTemplate('1fr 1fr')
  112 + .rowsGap('23lpx')
  113 + }
  114 + .margin({bottom:'46lpx'})
  115 + }
  116 +
  117 + getCategoryRowCount() {
  118 + return Math.ceil(this.searchHistoryData.length / 2);
  119 + }
  120 +
  121 + getCategoryRowTmpl() {
  122 + const count = this.getCategoryRowCount();
  123 + const arr: string[] = new Array(count || 1).fill('1fr');
  124 + console.log('tmpl ', arr.join(' '))
  125 + return arr.join(' ');
  126 + }
  127 +
  128 + getCategoryViewHeight() {
  129 + return `${46 * this.getCategoryRowCount()}lpx`;
  130 + }
  131 +}
  1 +import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
  2 +import { SearchHotContentItem } from '../../viewmodel/SearchHotContentItem'
  3 +
  4 +const TAG = "SearchHotsComponent"
  5 +
  6 +/**
  7 + * 热门搜索
  8 + */
  9 +@Component
  10 +export struct SearchHotsComponent{
  11 + @State searchHotsData:SearchHotContentItem[] = []
  12 +
  13 + aboutToAppear(){
  14 + //获取搜索热词
  15 + this.getSearchHotsData()
  16 + }
  17 +
  18 + getSearchHotsData(){
  19 + SearcherAboutDataModel.getSearchHotsData(getContext(this)).then((value)=>{
  20 + if(value!=null){
  21 + this.searchHotsData = value
  22 + }
  23 + }).catch((err:Error)=>{
  24 + console.log(TAG,JSON.stringify(err))
  25 + })
  26 + }
  27 +
  28 + build(){
  29 + Column(){
  30 + Row() {
  31 + Image($r('app.media.search_hot_icon'))
  32 + .width('46lpx')
  33 + .height('46lpx')
  34 + .objectFit(ImageFit.Auto)
  35 + .margin({right:'8lpx'})
  36 + .interpolation(ImageInterpolation.Medium)
  37 +
  38 + Text("热门搜索")
  39 + .textAlign(TextAlign.Center)
  40 + .fontWeight('600lpx')
  41 + .fontSize('33lpx')
  42 + .lineHeight('46lpx')
  43 + .fontColor($r('app.color.color_222222'))
  44 + .height('38lpx')
  45 + }
  46 + .width('100%')
  47 +
  48 + List(){
  49 + ForEach(this.searchHotsData,(item:SearchHotContentItem,index:number)=>{
  50 + ListItem(){
  51 + Column(){
  52 + Column(){
  53 + Row(){
  54 + Row(){
  55 + if(item.sequence <=3){
  56 + Image(item.sequence===1?$r('app.media.search_hot_num1'):item.sequence===2?$r('app.media.search_hot_num2'):$r('app.media.search_hot_num3'))
  57 + .width('27lpx')
  58 + .height('35lpx')
  59 + .objectFit(ImageFit.Auto)
  60 + .margin({right:'12lpx'})
  61 + .interpolation(ImageInterpolation.High)
  62 + }else {
  63 + Text(`${item.sequence}`)
  64 + .height('31lpx')
  65 + .fontColor($r('app.color.color_666666'))
  66 + .fontSize('27lpx')
  67 + .fontWeight('400lpx')
  68 + .lineHeight('31lpx')
  69 + .margin({right:'12lpx'})
  70 + }
  71 + Text(`${item.hotEntry}`)
  72 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  73 + .fontColor($r('app.color.color_222222'))
  74 + .fontSize('31lpx')
  75 + .maxLines(1)
  76 + .fontWeight('400lpx')
  77 + .lineHeight('42lpx')
  78 + }.layoutWeight(1)
  79 + if(item.mark!=0){
  80 + Image(item.mark===1?$r('app.media.search_hots_mark1'):$r('app.media.search_hots_mark2'))
  81 + .width('42lpx')
  82 + .height('31lpx')
  83 + .objectFit(ImageFit.Auto)
  84 + .interpolation(ImageInterpolation.High)
  85 + }
  86 + }.alignItems(VerticalAlign.Center)
  87 + .height('84lpx')
  88 + .justifyContent(FlexAlign.SpaceBetween)
  89 +
  90 + if(index != this.searchHotsData.length-1 ){
  91 + Divider()
  92 + .width('100%')
  93 + .height('1lpx')
  94 + .color($r('app.color.color_F5F5F5'))
  95 + .strokeWidth('1lpx')
  96 + }
  97 + }.height('108lpx')
  98 + .justifyContent(FlexAlign.Center)
  99 + .alignItems(HorizontalAlign.Start)
  100 + .padding({left:'27lpx'})
  101 + }
  102 + }
  103 + .onClick(()=>{
  104 + })
  105 + .height('117lpx')
  106 + })
  107 + }.onScrollFrameBegin((offset, state) => {
  108 + return { offsetRemain: 0 }
  109 + }).layoutWeight(1)
  110 + }.width('100%')
  111 + .height('100%')
  112 + .margin({top:'46lpx'})
  113 + }
  114 +}
1 1
2 -import { Logger, ResourcesUtils } from 'wdKit'; 2 +import { Logger, ResourcesUtils, SPHelper, UserDataLocal } from 'wdKit';
3 import { HttpUrlUtils, ResponseDTO, WDHttp } from 'wdNetwork'; 3 import { HttpUrlUtils, ResponseDTO, WDHttp } from 'wdNetwork';
4 import HashMap from '@ohos.util.HashMap'; 4 import HashMap from '@ohos.util.HashMap';
  5 +import { SearchHistoryItem } from '../viewmodel/SearchHistoryItem';
  6 +import { SearchHotContentItem } from '../viewmodel/SearchHotContentItem';
  7 +
5 const TAG = "SearcherAboutDataModel" 8 const TAG = "SearcherAboutDataModel"
6 9
7 /** 10 /**
@@ -9,6 +12,8 @@ const TAG = "SearcherAboutDataModel" @@ -9,6 +12,8 @@ const TAG = "SearcherAboutDataModel"
9 */ 12 */
10 class SearcherAboutDataModel{ 13 class SearcherAboutDataModel{
11 private static instance: SearcherAboutDataModel; 14 private static instance: SearcherAboutDataModel;
  15 + public searchHistoryData:SearchHistoryItem[] = []
  16 + public SEARCH_HISTORY_KEY:string = "SEARCH_HISTORY_KEY" + UserDataLocal.userId
12 17
13 private constructor() { } 18 private constructor() { }
14 19
@@ -24,6 +29,57 @@ class SearcherAboutDataModel{ @@ -24,6 +29,57 @@ class SearcherAboutDataModel{
24 } 29 }
25 30
26 /** 31 /**
  32 + * 插入搜索记录(单个)
  33 + */
  34 + public async putSearchHistoryData(content:string){
  35 + let history = SPHelper.default.getSync(this.SEARCH_HISTORY_KEY,"[]") as string
  36 + this.searchHistoryData = JSON.parse(history)
  37 + this.searchHistoryData.splice(0,0,new SearchHistoryItem(content))
  38 + await SPHelper.default.saveSync(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistoryData));
  39 + }
  40 +
  41 + /**
  42 + * 删除搜索记录(所有)
  43 + */
  44 + public async delSearchHistoryData(){
  45 + SPHelper.default.deleteSync(this.SEARCH_HISTORY_KEY)
  46 + this.searchHistoryData = []
  47 + }
  48 + /**
  49 + * 删除搜索记录(单个)
  50 + */
  51 + public async delSearchSingleHistoryData(index:number){
  52 + if(this.searchHistoryData!=null && this.searchHistoryData.length>0){
  53 + this.searchHistoryData.splice(index,1)
  54 + }else{
  55 + let history = SPHelper.default.getSync(this.SEARCH_HISTORY_KEY,"[]") as string
  56 + this.searchHistoryData = JSON.parse(history)
  57 + this.searchHistoryData.splice(index,1)
  58 + }
  59 + SPHelper.default.saveSync(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistoryData))
  60 + }
  61 +
  62 + /**
  63 + * 查询搜索记录(所有)
  64 + */
  65 + public getSearchHistoryData() : SearchHistoryItem[] {
  66 + if(this.searchHistoryData!=null && this.searchHistoryData.length>0){
  67 + if(this.searchHistoryData.length>10){
  68 + this.searchHistoryData.splice(10,this.searchHistoryData.length - 10)
  69 + }
  70 + return this.searchHistoryData
  71 + }
  72 + let history = SPHelper.default.getSync(this.SEARCH_HISTORY_KEY,"[]") as string
  73 +
  74 + this.searchHistoryData = JSON.parse(history)
  75 + if(this.searchHistoryData.length>10){
  76 + this.searchHistoryData.splice(10,this.searchHistoryData.length - 10)
  77 + }
  78 +
  79 + return this.searchHistoryData
  80 + }
  81 +
  82 + /**
27 * 首页 搜索提示滚动内容 83 * 首页 搜索提示滚动内容
28 */ 84 */
29 getSearchHintData(context: Context): Promise<string[]> { 85 getSearchHintData(context: Context): Promise<string[]> {
@@ -62,6 +118,43 @@ class SearcherAboutDataModel{ @@ -62,6 +118,43 @@ class SearcherAboutDataModel{
62 } 118 }
63 119
64 120
  121 + /**
  122 + * 搜索主页 热词
  123 + */
  124 + getSearchHotsData(context: Context): Promise<SearchHotContentItem[]> {
  125 + return new Promise<SearchHotContentItem[]>((success, error) => {
  126 + Logger.info(TAG, `getSearchHintData start`);
  127 + this.fetchSearchHotsData().then((navResDTO: ResponseDTO<SearchHotContentItem[]>) => {
  128 + if (!navResDTO || navResDTO.code != 0) {
  129 + success(this.getSearchHotsDataLocal(context))
  130 + return
  131 + }
  132 + Logger.info(TAG, "getSearchHotsData then,getSearchHotsData.timeStamp:" + navResDTO.timestamp);
  133 + let navigationBean = navResDTO.data as SearchHotContentItem[]
  134 + success(navigationBean);
  135 + }).catch((err: Error) => {
  136 + Logger.error(TAG, `getSearchHotsData catch, error.name : ${err.name}, error.message:${err.message}`);
  137 + success(this.getSearchHotsDataLocal(context))
  138 + })
  139 + })
  140 + }
  141 +
  142 + fetchSearchHotsData() {
  143 + let url = HttpUrlUtils.getSearchHotsDataUrl()
  144 + let headers: HashMap<string, string> = HttpUrlUtils.getCommonHeaders();
  145 + return WDHttp.get<ResponseDTO<SearchHotContentItem[]>>(url, headers)
  146 + };
  147 +
  148 + async getSearchHotsDataLocal(context: Context): Promise<SearchHotContentItem[]> {
  149 + Logger.info(TAG, `getSearchHotsDataLocal start`);
  150 + let compRes: ResponseDTO<SearchHotContentItem[]> | null = await ResourcesUtils.getResourcesJson<ResponseDTO<SearchHotContentItem[]>>(context,'search_hots_data.json' ,);
  151 + if (!compRes || !compRes.data) {
  152 + Logger.info(TAG, `getSearchHotsDataLocal compRes is empty`);
  153 + return []
  154 + }
  155 + Logger.info(TAG, `getSearchHotsDataLocal compRes : ${JSON.stringify(compRes)}`);
  156 + return compRes.data
  157 + }
65 158
66 } 159 }
67 160
  1 +import { SearchComponent } from '../components/search/SearchComponent'
  2 +
  3 +@Entry
  4 +@Component
  5 +struct SearchPage {
  6 + build() {
  7 + Column(){
  8 + SearchComponent()
  9 + }.height('100%')
  10 + .width('100%')
  11 + }
  12 +}
@@ -123,8 +123,9 @@ class EditInfoViewModel { @@ -123,8 +123,9 @@ class EditInfoViewModel {
123 } else if (item.editDataType == WDEditDataModelType.WDEditDataModelType_region) { 123 } else if (item.editDataType == WDEditDataModelType.WDEditDataModelType_region) {
124 this.params = {province:item.userExtend.province,city:item.userExtend.city, county: item.userExtend.county ,address:item.userExtend.address} 124 this.params = {province:item.userExtend.province,city:item.userExtend.city, county: item.userExtend.county ,address:item.userExtend.address}
125 } 125 }
  126 +
126 return new Promise((success, error) => { 127 return new Promise((success, error) => {
127 - this.BasePostRequest(HttpUrlUtils.APPOINTMENT_editUserDetail_PATH,this.params) 128 + this.BasePostRequest(item.editDataType == WDEditDataModelType.WDEditDataModelType_nickname?HttpUrlUtils.APPOINTMENT_editUserDetail1_PATH:HttpUrlUtils.APPOINTMENT_editUserDetail_PATH,this.params)
128 .then((navResDTO: ResponseDTO) => { 129 .then((navResDTO: ResponseDTO) => {
129 if (navResDTO.code == 0) { 130 if (navResDTO.code == 0) {
130 promptAction.showToast({ message: '修改成功' }) 131 promptAction.showToast({ message: '修改成功' })
  1 +export class SearchHistoryItem{
  2 + searchContent:string = ""
  3 +
  4 +
  5 + constructor(searchContent: string) {
  6 + this.searchContent = searchContent
  7 + }
  8 +}
  1 +export class SearchHotContentItem{
  2 + hotEntry:string = ""
  3 + mark:number = 0 //1 热 2 新
  4 + sequence:number = 0//序号
  5 +
  6 + constructor(hotEntry: string , mark: number , sequence: number ) {
  7 + this.hotEntry = hotEntry
  8 + this.mark = mark
  9 + this.sequence = sequence
  10 + }
  11 +}
@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 "components/page/EditUserIntroductionPage", 12 "components/page/EditUserIntroductionPage",
13 "components/page/BrowsingHistoryPage", 13 "components/page/BrowsingHistoryPage",
14 "components/page/MyCollectionListPage", 14 "components/page/MyCollectionListPage",
15 - "pages/OtherNormalUserHomePage" 15 + "pages/OtherNormalUserHomePage",
  16 + "pages/SearchPage"
16 ] 17 ]
17 } 18 }
@@ -6,5 +6,7 @@ @@ -6,5 +6,7 @@
6 "description": "Please describe the basic information.", 6 "description": "Please describe the basic information.",
7 "main": "Index.ets", 7 "main": "Index.ets",
8 "version": "1.0.0", 8 "version": "1.0.0",
9 - "dependencies": {} 9 + "dependencies": {
  10 + "wdComponent": "file:../../features/wdComponent"
  11 + }
10 } 12 }
  1 +import { BottomComponent } from '../widgets/details/BottomComponent';
  2 +import { TabComponent } from '../widgets/details/TabComponent';
  3 +import { TopPlayComponent } from '../widgets/details/TopPlayComponet';
  4 +
1 @Entry 5 @Entry
2 @Component 6 @Component
3 export struct DetailPlayLivePage { 7 export struct DetailPlayLivePage {
4 - @State message: string = 'Detail Play Live Page'; 8 + TAG: string = 'DetailPlayLivePage';
  9 +
  10 + aboutToAppear(): void {
  11 +
  12 + }
5 13
6 build() { 14 build() {
7 - Row() {  
8 Column() { 15 Column() {
9 - Text(this.message)  
10 - .fontSize(50)  
11 - .fontWeight(FontWeight.Bold) 16 + TopPlayComponent()
  17 + TabComponent()
  18 + BottomComponent()
12 } 19 }
  20 + .height('100%')
13 .width('100%') 21 .width('100%')
14 } 22 }
15 - .height('100%') 23 +
  24 + aboutToDisappear(): void {
16 } 25 }
17 } 26 }
  1 +@Component
  2 +export struct BottomComponent {
  3 + aboutToAppear(): void {
  4 + }
  5 +
  6 + build() {
  7 + Row() {
  8 +
  9 + }.backgroundColor(Color.Gray)
  10 + .height(56)
  11 + .width('100%')
  12 + }
  13 +
  14 + aboutToDisappear(): void {
  15 + }
  16 +}
  1 +import font from '@ohos.font'
  2 +
  3 +@Component
  4 +export struct LiveCountdownComponent {
  5 + textTimerController: TextTimerController = new TextTimerController()
  6 + @State format: string = 'HH:mm:ss'
  7 +
  8 + aboutToAppear(): void {
  9 + //注册字体
  10 + font.registerFont({
  11 + familyName: 'BebasNeue_Regular',
  12 + familySrc: $rawfile('font/BebasNeue_Regular.otf')
  13 + })
  14 + setTimeout(() => {
  15 + this.textTimerController.start()
  16 + }, 2000)
  17 + }
  18 +
  19 + build() {
  20 + Column() {
  21 + this.showTitle()
  22 + this.showCountDown()
  23 + this.showAppointment()
  24 + }.padding({
  25 + top: 20,
  26 + left: 20,
  27 + right: 20,
  28 + bottom: 24
  29 + })
  30 + .backgroundColor(Color.White)
  31 + .border({ radius: 6 })
  32 + .margin({ top: 16 })
  33 + }
  34 +
  35 + aboutToDisappear(): void {
  36 + this.textTimerController.pause()
  37 + }
  38 +
  39 + @Builder
  40 + showTitle() {
  41 + Text('距离直播开始还有')
  42 + .maxLines(2)
  43 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  44 + .fontSize('14fp')
  45 + .fontWeight(400)
  46 + .fontColor('#222222')
  47 + }
  48 +
  49 + @Builder
  50 + showCountDown() {
  51 + Row() {
  52 + this.showTimeStyle('10', true, 0)
  53 + this.showTimeStyle('月', false, 3)
  54 + this.showTimeStyle('8', true, 3)
  55 + this.showTimeStyle('日', false, 3)
  56 + this.showTimeStyle('16', true, 10)
  57 + this.showTimeStyle(':', true, 0)
  58 + this.showTimeStyle('05', true, 0)
  59 + }
  60 + .margin({ top: 10 })
  61 + .visibility(Visibility.None)
  62 +
  63 + TextTimer({ isCountDown: true, count: 24 * 60 * 60 * 1000 - 1000, controller: this.textTimerController })
  64 + .format(this.format)
  65 + .fontSize('40fp')
  66 + .fontWeight(FontWeight.Bold)
  67 + .fontColor('#222222')
  68 + .fontFamily('BebasNeue_Regular')
  69 + .onTimer((utc: number, elapsedTime: number) => {
  70 + console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime)
  71 + })
  72 + .margin({ top: 10 })
  73 + }
  74 +
  75 + @Builder
  76 + showAppointment() {
  77 + Text('我要预约')
  78 + .width('100%')
  79 + .height(42)
  80 + .textAlign(TextAlign.Center)
  81 + .fontSize('16fp')
  82 + .fontWeight(400)
  83 + .fontColor(Color.White)
  84 + .margin({
  85 + top: 16
  86 + })
  87 + .border({ radius: 4 })
  88 + .backgroundColor('#ED2800')
  89 + // .backgroundColor('#CCCCCC')
  90 + }
  91 +
  92 + @Builder
  93 + showTimeStyle(value: string, isBold: boolean, left: number) {
  94 + Text(value)
  95 + .fontSize(isBold ? '40fp' : '16fp')
  96 + .fontFamily(isBold ? 'BebasNeue_Regular' : '')
  97 + .fontWeight(isBold ? FontWeight.Bold : 500)
  98 + .fontColor('#222222')
  99 + .margin({ left: left })
  100 + }
  101 +}
  1 +import { ListHasNoMoreDataUI } from 'wdComponent/Index'
  2 +import { TabChatItemComponent } from './TabChatItemComponent'
  3 +
  4 +@Component
  5 +export struct TabChatComponent {
  6 + arr: string[] = []
  7 +
  8 + aboutToAppear(): void {
  9 + for (let index = 0; index < 12; index++) {
  10 + this.arr.push(index + '')
  11 + }
  12 + }
  13 +
  14 + build() {
  15 + Stack() {
  16 + if (this.arr.length == 0) {
  17 + ListHasNoMoreDataUI({ style: 2 })
  18 + } else {
  19 + List() {
  20 + ForEach(this.arr, (item: string) => {
  21 + ListItem() {
  22 + TabChatItemComponent()
  23 + }
  24 + })
  25 + ListItem() {
  26 + ListHasNoMoreDataUI()
  27 + }
  28 + }
  29 + }
  30 + }
  31 + .align(Alignment.Top)
  32 + .backgroundColor('#F5F5F5')
  33 + .height('100%')
  34 + .width('100%')
  35 + }
  36 +
  37 + aboutToDisappear(): void {
  38 + }
  39 +}
  1 +@Component
  2 +export struct TabChatItemComponent {
  3 + aboutToAppear(): void {
  4 + }
  5 +
  6 + build() {
  7 + Row() {
  8 + Image('https://img0.baidu.com/it/u=4105778329,1297102594&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500')
  9 + .borderRadius(90)
  10 + .width(24)
  11 + .height(24)
  12 + Text() {
  13 + Span('10999@qq.com: ')
  14 + .fontColor('#666666')
  15 + Span('少年强则国强:山中有精灵也不过如此了')
  16 + .fontColor('#222222')
  17 + }
  18 + .margin({ left: 8 })
  19 + .lineHeight(20)
  20 + .layoutWeight(1)
  21 + .fontSize('14fp')
  22 + .fontWeight(400)
  23 + }
  24 + .alignItems(VerticalAlign.Top)
  25 + .padding({
  26 + left: 15,
  27 + top: 15,
  28 + right: 15
  29 + })
  30 + }
  31 +
  32 + aboutToDisappear(): void {
  33 +
  34 + }
  35 +}
  1 +import { TabChatComponent } from './TabChatComponent'
  2 +import { TabInfoComponent } from './TabInfoComponent'
  3 +import { TabLiveComponent } from './TabLiveComponent'
  4 +
  5 +@Component
  6 +export struct TabComponent {
  7 + @State fontColor: string = '#999999'
  8 + @State selectedFontColor: string = '#222222'
  9 + @State currentIndex: number = 0
  10 + private controller: TabsController = new TabsController()
  11 + tabs: string[] = ['简介', '直播间', '大家聊']
  12 +
  13 + aboutToAppear(): void {
  14 +
  15 + }
  16 +
  17 + build() {
  18 + Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
  19 + ForEach(this.tabs, (item: string, index: number) => {
  20 + TabContent() {
  21 + if (0 == index) {
  22 + TabInfoComponent()
  23 + } else if (1 == index) {
  24 + TabLiveComponent()
  25 + } else {
  26 + TabChatComponent()
  27 + }
  28 + }.tabBar(this.tabBuilder(index, item))
  29 + .backgroundColor('#F5F5F5')
  30 + }, (item: string, index: number) => {
  31 + return item + index
  32 + })
  33 + }
  34 + .layoutWeight(1)
  35 + .vertical(false)
  36 + .barMode(BarMode.Fixed)
  37 + .barWidth(200)
  38 + .barHeight(48)
  39 + .animationDuration(100)
  40 + .onChange((index: number) => {
  41 + this.currentIndex = index
  42 + })
  43 + .backgroundColor(Color.White)
  44 + }
  45 +
  46 + @Builder
  47 + tabBuilder(index: number, name: string) {
  48 + Column() {
  49 + Text(name)
  50 + .margin({ top: 6 })
  51 + .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
  52 + .fontSize('18fp')
  53 + .fontWeight(this.currentIndex === index ? 600 : 400)
  54 + Divider()
  55 + .strokeWidth(2)
  56 + .margin({ top: 6 })
  57 + .width(15)
  58 + .color('#CB0000')
  59 + .visibility(this.currentIndex === index ? Visibility.Visible : Visibility.Hidden)
  60 + }.width('100%')
  61 + }
  62 +
  63 + aboutToDisappear(): void {
  64 + }
  65 +}
  1 +import { LiveCountdownComponent } from './LiveCountdownComponent'
  2 +
  3 +@Component
  4 +export struct TabInfoComponent {
  5 + aboutToAppear(): void {
  6 + }
  7 +
  8 + build() {
  9 + Column() {
  10 + this.showLiveTitle()
  11 + this.showLiveDetails()
  12 + LiveCountdownComponent()
  13 + }.margin({
  14 + top: 13,
  15 + left: 16,
  16 + right: 16
  17 + })
  18 + .height('100%')
  19 + }
  20 +
  21 + aboutToDisappear(): void {
  22 + }
  23 +
  24 + @Builder
  25 + showLiveTitle() {
  26 + Text('国新办发布会丨介绍防汛抗旱工国新办发布会丨介绍防汛抗旱工作情况国新办发布会丨介绍防汛抗旱工作情况作情况国新办发布会丨介绍防汛抗旱工作情况')
  27 + .maxLines(2)
  28 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  29 + .fontSize('18fp')
  30 + .fontWeight(500)
  31 + .fontColor('#222222')
  32 + }
  33 +
  34 + @Builder
  35 + showLiveDetails() {
  36 + Text('国务院新闻办公室将于7月25日上午10时举行国务院政策例行吹风会,请应急管理部副部长、水利部副部长王道席和自然资源部、水利部、应急管理部、中国气象局、国家消防救援局有关负责人介绍防汛抗旱工作情况,并答记者问。')
  37 + .maxLines(5)
  38 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  39 + .fontSize('14fp')
  40 + .fontWeight(400)
  41 + .fontColor('#666666')
  42 + .margin({ top: 8 })
  43 + }
  44 +}
  1 +import { ListHasNoMoreDataUI } from 'wdComponent/Index'
  2 +import { TabLiveItemComponent } from './TabLiveItemComponent'
  3 +
  4 +@Component
  5 +export struct TabLiveComponent {
  6 + arr: string[] = []
  7 +
  8 + aboutToAppear(): void {
  9 + for (let index = 0; index < 2; index++) {
  10 + this.arr.push(index + '')
  11 + }
  12 + }
  13 +
  14 + build() {
  15 + Stack() {
  16 + if (this.arr.length == 0) {
  17 + ListHasNoMoreDataUI({ style: 2 })
  18 + } else {
  19 + List() {
  20 + ForEach(this.arr, (item: string) => {
  21 + ListItem() {
  22 + TabLiveItemComponent({ item: item })
  23 + }
  24 + })
  25 + ListItem() {
  26 + ListHasNoMoreDataUI()
  27 + }
  28 + }
  29 + }
  30 + }
  31 + .alignContent(Alignment.Top)
  32 + .backgroundColor('#F5F5F5')
  33 + .height('100%')
  34 + .width('100%')
  35 +
  36 + }
  37 +
  38 + aboutToDisappear(): void {
  39 + }
  40 +}
  1 +@Component
  2 +export struct TabLiveItemComponent {
  3 + item: string = ''
  4 +
  5 + aboutToAppear(): void {
  6 +
  7 + }
  8 +
  9 + build() {
  10 + Column() {
  11 + Row() {
  12 + Image('https://img0.baidu.com/it/u=4105778329,1297102594&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500')
  13 + .borderRadius(90)
  14 + .width(24)
  15 + .height(24)
  16 + Text('人民日报直播频道')
  17 + .maxLines(1)
  18 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  19 + .fontSize('14fp')
  20 + .fontWeight(400)
  21 + .fontColor('#222222')
  22 + .margin({ left: 8 })
  23 + Text('嘉宾')
  24 + .maxLines(1)
  25 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  26 + .fontSize('11fp')
  27 + .fontWeight(400)
  28 + .fontColor('#968562')
  29 + .backgroundColor('#F1EFEB')
  30 + .padding({
  31 + left: 4,
  32 + top: 1,
  33 + right: 4,
  34 + bottom: 1
  35 + })
  36 + .borderRadius(2)
  37 + .margin({ left: 8 })
  38 + Text('1小时前')
  39 + .maxLines(1)
  40 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  41 + .fontSize('12fp')
  42 + .fontWeight(400)
  43 + .fontColor('#999999')
  44 + .margin({ left: 8 })
  45 + Blank()
  46 + Text('置顶')
  47 + .maxLines(1)
  48 + .textOverflow({ overflow: TextOverflow.Ellipsis })
  49 + .fontSize('11fp')
  50 + .fontWeight(400)
  51 + .fontColor('#ED2800')
  52 + .backgroundColor('#F1EFEB')
  53 + .padding({
  54 + left: 4,
  55 + top: 1,
  56 + right: 4,
  57 + bottom: 1
  58 + })
  59 + .borderRadius(2)
  60 + .margin({ left: 8 })
  61 + }
  62 + .width('100%')
  63 +
  64 + Text('国务院新闻办公室将于7月25日上午10时举行国务院政策例行吹风会,请应急管理部副部长、水利部副部长王道席和自然资源部、水利部、应急管理部、中国气象局、国家消防救援局有关负责人介绍防汛抗旱工作情况,并答记者问。')
  65 + .fontSize('14fp')
  66 + .fontWeight(400)
  67 + .fontColor('#222222')
  68 + .margin({
  69 + left: 32,
  70 + top: 6
  71 + })
  72 +
  73 + Image('https://t7.baidu.com/it/u=3690528415,706188365&fm=193&f=GIF')
  74 + .height(174)
  75 + .width(310)
  76 + .aspectRatio(310 / 174)
  77 + .objectFit(ImageFit.Auto)
  78 + .borderRadius(4)
  79 + .margin({
  80 + left: 32,
  81 + top: 8
  82 + })
  83 + }.margin({
  84 + left:15,
  85 + top:15,
  86 + right:15
  87 + })
  88 + }
  89 +
  90 + aboutToDisappear(): void {
  91 +
  92 + }
  93 +}
  1 +@Component
  2 +export struct TopPlayComponent {
  3 + aspectRatioPlayer: number = 375 / 211
  4 +
  5 + aboutToAppear(): void {
  6 +
  7 + }
  8 +
  9 + build() {
  10 + Stack()
  11 + .height(211)
  12 + .aspectRatio(this.aspectRatioPlayer)
  13 + .backgroundColor(Color.Black)
  14 + }
  15 +
  16 + aboutToDisappear(): void {
  17 + }
  18 +}
@@ -159,7 +159,10 @@ export struct DetailPlayShortVideoPage { @@ -159,7 +159,10 @@ export struct DetailPlayShortVideoPage {
159 onLoad: async () => { 159 onLoad: async () => {
160 // console.log('onload==', this.index) 160 // console.log('onload==', this.index)
161 // if (this.index === 0) { 161 // if (this.index === 0) {
162 - this.playerController.firstPlay(this.contentDetailData?.videoInfo[0]?.videoUrl); 162 +
  163 + if(this.contentDetailData!=null && this.contentDetailData?.videoInfo[0]!=null){
  164 + this.playerController.firstPlay(this.contentDetailData.videoInfo[0].videoUrl);
  165 + }
163 // } 166 // }
164 } 167 }
165 }) 168 })
  1 +{
  2 + "code": "0",
  3 + "data": [
  4 + {
  5 + "hotEntry": "习语",
  6 + "mark": 1,
  7 + "sequence": 1
  8 + },
  9 + {
  10 + "hotEntry": "党史学习教育工作",
  11 + "mark": 1,
  12 + "sequence": 2
  13 + },
  14 + {
  15 + "hotEntry": "2024年全国两会新闻中心启用",
  16 + "mark": 1,
  17 + "sequence": 3
  18 + },
  19 + {
  20 + "hotEntry": "第二艘国产大型邮轮后年底交付",
  21 + "mark": 0,
  22 + "sequence": 4
  23 + },
  24 + {
  25 + "hotEntry": "268名跨境电诈犯罪嫌疑人移交",
  26 + "mark": 0,
  27 + "sequence": 5
  28 + },
  29 + {
  30 + "hotEntry": "高考倒计时100天",
  31 + "mark": 2,
  32 + "sequence": 6
  33 + },
  34 + {
  35 + "hotEntry": "日本开始第四轮核污染水排放",
  36 + "mark": 0,
  37 + "sequence": 7
  38 + },
  39 + {
  40 + "hotEntry": "国台办点名管碧玲",
  41 + "mark": 0,
  42 + "sequence": 8
  43 + },
  44 + {
  45 + "hotEntry": "美方称不会向乌克兰派兵",
  46 + "mark": 2,
  47 + "sequence": 9
  48 + },
  49 + {
  50 + "hotEntry": "电动车乱停乱充电",
  51 + "mark": 2,
  52 + "sequence": 10
  53 + }
  54 + ],
  55 + "message": "Success",
  56 + "success": true,
  57 + "timestamp": 1712631086296
  58 +}