yangchenggong1_wd

desc:3.0 搜索主页(竖滚动,热门搜索,搜索历史)

Showing 22 changed files with 559 additions and 4 deletions
import { SearchComponent } from 'wdComponent'
@Entry
@Component
struct SearchPage {
build() {
Column(){
SearchComponent()
}.height('100%')
.width('100%')
}
}
\ No newline at end of file
... ...
... ... @@ -14,6 +14,7 @@
"pages/LaunchPage",
"pages/LaunchAdvertisingPage",
"pages/PrivacyPage",
"pages/OtherNormalUserHomePage"
"pages/OtherNormalUserHomePage",
"pages/SearchPage"
]
}
\ No newline at end of file
... ...
{
"code": "0",
"data": [
{
"hotEntry": "习语",
"mark": 1,
"sequence": 1
},
{
"hotEntry": "党史学习教育工作",
"mark": 1,
"sequence": 2
},
{
"hotEntry": "2024年全国两会新闻中心启用",
"mark": 1,
"sequence": 3
},
{
"hotEntry": "第二艘国产大型邮轮后年底交付",
"mark": 0,
"sequence": 4
},
{
"hotEntry": "268名跨境电诈犯罪嫌疑人移交",
"mark": 0,
"sequence": 5
},
{
"hotEntry": "高考倒计时100天",
"mark": 2,
"sequence": 6
},
{
"hotEntry": "日本开始第四轮核污染水排放",
"mark": 0,
"sequence": 7
},
{
"hotEntry": "国台办点名管碧玲",
"mark": 0,
"sequence": 8
},
{
"hotEntry": "美方称不会向乌克兰派兵",
"mark": 2,
"sequence": 9
},
{
"hotEntry": "电动车乱停乱充电",
"mark": 2,
"sequence": 10
}
],
"message": "Success",
"success": true,
"timestamp": 1712631086296
}
\ No newline at end of file
... ...
... ... @@ -45,3 +45,5 @@ export { FollowFirstTabsComponent } from "./components/page/mine/follow/FollowFi
export { MyHomeComponent } from "./components/page/mine/home/MyHomeComponent"
export { OtherUserHomeComponent } from "./components/page/mine/home/OtherUserHomeComponent"
export { SearchComponent } from "./components/page/search/SearchComponent"
\ No newline at end of file
... ...
import SearcherAboutDataModel from '../../model/SearcherAboutDataModel'
import RouteManager from '../../utils/RouteManager'
/**
* 首页顶部搜索导航栏
* 竖向滚动实现方案
... ... @@ -61,6 +62,9 @@ export struct FirstTabTopComponent{
.backgroundImage($r('app.media.top_search_bg'))
.backgroundImageSize(ImageSize.Cover)
.alignItems(VerticalAlign.Center)
.onClick(()=>{
RouteManager.jumpNewPage("pages/SearchPage")
})
Image($r('app.media.top_title_bg'))
.objectFit(ImageFit.Auto)
... ...
import router from '@ohos.router'
import { StringUtils } from 'wdKit/src/main/ets/utils/StringUtils'
import SearcherAboutDataModel from '../../../model/SearcherAboutDataModel'
import { SearchHistoryItem } from '../../../viewmodel/SearchHistoryItem'
import { SearchHistoryComponent } from './SearchHistoryComponent'
import { SearchHotsComponent } from './SearchHotsComponent'
const TAG = "SearchComponent"
@Component
export struct SearchComponent {
@State searchTextData: string[] = []
@State hasInputContent: boolean = false
@State hasChooseSearch: boolean = false
private swiperController: SwiperController = new SwiperController()
@State searchText: string = ''
controller: TextInputController = new TextInputController()
@State searchHistoryData: SearchHistoryItem[] = []
scroller: Scroller = new Scroller()
aboutToAppear() {
//获取提示滚动
this.getSearchHint()
//获取搜索历史
this.getSearchHistoryData()
}
getSearchHint() {
SearcherAboutDataModel.getSearchHintData(getContext(this)).then((value) => {
if (value != null) {
this.searchTextData = value
}
}).catch((err: Error) => {
console.log(TAG, JSON.stringify(err))
})
}
getSearchHistoryData() {
this.searchHistoryData = SearcherAboutDataModel.getSearchHistoryData()
}
build() {
Column() {
this.searchInputComponent()
if (!this.hasInputContent) {
Scroll(this.scroller) {
Column() {
SearchHistoryComponent({ searchHistoryData: $searchHistoryData })
//分隔符
Divider()
.width('100%')
.height('1lpx')
.color($r('app.color.color_EDEDED'))
.strokeWidth('1lpx')
SearchHotsComponent()
}
}
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Off)
.width('100%')
.height('100%')
.padding({ left: '31lpx', right: '31lpx' })
} else {
if (this.hasChooseSearch) {
//搜索结果
//搜索结果为null(空布局 + 为你推荐)
} else {
//联想搜索
}
}
}.height('100%')
.width('100%')
}
//搜索框
@Builder searchInputComponent() {
Row() {
//左
Stack({ alignContent: Alignment.Start }) {
if (this.searchTextData != null && this.searchTextData.length > 0 && !this.hasInputContent) {
Swiper(this.swiperController) {
ForEach(this.searchTextData, (item: string, index: number) => {
Text(item)
.fontWeight('400lpx')
.fontSize('25lpx')
.fontColor($r('app.color.color_666666'))
.lineHeight('35lpx')
.textAlign(TextAlign.Start)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Clip })
.margin({ left: '40lpx' })
})
}
.loop(true)
.autoPlay(true)
.interval(3000)
.indicator(false)
.vertical(true)
.enabled(false)
.focusable(false)
}
TextInput({ text: this.searchText, placeholder: '', controller: this.controller })
.caretColor(Color.Pink)
.fontSize('27lpx')
.fontColor(Color.Black)
.onChange((value: string) => {
this.searchText = value
if (this.searchText.length > 0) {
this.hasInputContent = false
} else {
this.hasInputContent = true
}
})
.backgroundColor($r('app.color.color_transparent'))
}
.backgroundImage($r('app.media.search_page_input_bg'))
.backgroundImageSize(ImageSize.Cover)
.layoutWeight(1)
.height('69lpx')
//右
Text("取消")
.textAlign(TextAlign.Center)
.fontWeight('400lpx')
.fontSize('31lpx')
.lineHeight('58lpx')
.fontColor($r('app.color.color_222222'))
.width('125lpx')
.height('58lpx')
.onClick(() => {
router.back()
// SearcherAboutDataModel.putSearchHistoryData(this.searchText)
// if(StringUtils.isNotEmpty(this.searchText)){
// if(this.searchHistoryData.length===10){
// this.searchHistoryData.pop()
// }
// this.searchHistoryData.push(new SearchHistoryItem(this.searchText))
// }
})
}
.height('85lpx')
.padding({ left: '31lpx' })
.alignItems(VerticalAlign.Center)
.margin({ bottom: '36lpx' })
}
}
\ No newline at end of file
... ...
import { SearchHistoryItem } from '../../../viewmodel/SearchHistoryItem'
/**
* 搜索历史
*/
@Component
export struct SearchHistoryComponent{
@Link searchHistoryData:SearchHistoryItem[]
build(){
Column(){
Row(){
Text("搜索历史")
.textAlign(TextAlign.Center)
.fontWeight('400lpx')
.fontSize('27lpx')
.lineHeight('38lpx')
.fontColor($r('app.color.color_999999'))
.height('38lpx')
Image($r('app.media.search_delete_icon'))
.height('31lpx')
.width('31lpx')
.objectFit(ImageFit.Auto)
.onClick(()=>{
//清空记录
})
}.justifyContent(FlexAlign.SpaceBetween)
.margin({bottom:'17lpx'})
.width('100%')
Grid(){
ForEach(this.searchHistoryData,(item:SearchHistoryItem,index:number)=>{
GridItem(){
Row(){
Text(`${item.searchContent}`)
.height('23lpx')
.fontColor($r('app.color.color_222222'))
.fontSize('23lpx')
.maxLines(1)
.constraintSize({maxWidth:index%2 === 0?'270lpx':'250lpx'})
.textOverflow({ overflow: TextOverflow.Ellipsis })
.textAlign(TextAlign.Start)
Image($r('app.media.search_item_delete_icon'))
.width('46lpx')
.height('46lpx')
.margin({right:'31lpx',left:'4lpx'})
.interpolation(ImageInterpolation.Medium)
.objectFit(ImageFit.Auto)
Blank()
if(index%2 === 0 && index != this.searchHistoryData.length-1 ){
Divider()
.width('2lpx')
.height('23lpx')
.color($r('app.color.color_CCCCCC'))
.strokeWidth('2lpx')
.vertical(true)
}
}.height('100%')
.alignItems(VerticalAlign.Center)
.width('100%')
.margin({left:index%2 === 1?'23lpx':'0lpx'})
}.onClick(()=>{
})
.height('46lpx')
})
}
.height(this.getCategoryViewHeight())
.rowsTemplate(this.getCategoryRowTmpl())
.columnsTemplate('1fr 1fr')
.rowsGap('23lpx')
}
.margin({bottom:'46lpx'})
}
getCategoryRowCount() {
return Math.ceil(this.searchHistoryData.length / 2);
}
getCategoryRowTmpl() {
const count = this.getCategoryRowCount();
const arr: string[] = new Array(count || 1).fill('1fr');
console.log('tmpl ', arr.join(' '))
return arr.join(' ');
}
getCategoryViewHeight() {
return `${46 * this.getCategoryRowCount()}lpx`;
}
}
\ No newline at end of file
... ...
import SearcherAboutDataModel from '../../../model/SearcherAboutDataModel'
import { SearchHotContentItem } from '../../../viewmodel/SearchHotContentItem'
const TAG = "SearchHotsComponent"
/**
* 热门搜索
*/
@Component
export struct SearchHotsComponent{
@State searchHotsData:SearchHotContentItem[] = []
aboutToAppear(){
//获取搜索热词
this.getSearchHotsData()
}
getSearchHotsData(){
SearcherAboutDataModel.getSearchHotsData(getContext(this)).then((value)=>{
if(value!=null){
this.searchHotsData = value
}
}).catch((err:Error)=>{
console.log(TAG,JSON.stringify(err))
})
}
build(){
Column(){
Row() {
Image($r('app.media.search_hot_icon'))
.width('46lpx')
.height('46lpx')
.objectFit(ImageFit.Auto)
.margin({right:'8lpx'})
.interpolation(ImageInterpolation.Medium)
Text("热门搜索")
.textAlign(TextAlign.Center)
.fontWeight('600lpx')
.fontSize('33lpx')
.lineHeight('46lpx')
.fontColor($r('app.color.color_222222'))
.height('38lpx')
}
.width('100%')
List(){
ForEach(this.searchHotsData,(item:SearchHotContentItem,index:number)=>{
ListItem(){
Column(){
Column(){
Row(){
Row(){
if(item.sequence <=3){
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'))
.width('27lpx')
.height('35lpx')
.objectFit(ImageFit.Auto)
.margin({right:'12lpx'})
}else {
Text(`${item.sequence}`)
.height('31lpx')
.fontColor($r('app.color.color_666666'))
.fontSize('27lpx')
.fontWeight('400lpx')
.lineHeight('31lpx')
.margin({right:'12lpx'})
}
Text(`${item.hotEntry}`)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontColor($r('app.color.color_222222'))
.fontSize('31lpx')
.maxLines(1)
.fontWeight('400lpx')
.lineHeight('42lpx')
}.layoutWeight(1)
if(item.mark!=0){
Image(item.mark===1?$r('app.media.search_hots_mark1'):$r('app.media.search_hots_mark2'))
.width('42lpx')
.height('31lpx')
.objectFit(ImageFit.Auto)
.interpolation(ImageInterpolation.Medium)
}
}.alignItems(VerticalAlign.Center)
.height('84lpx')
.justifyContent(FlexAlign.SpaceBetween)
if(index != this.searchHotsData.length-1 ){
Divider()
.width('100%')
.height('1lpx')
.color($r('app.color.color_F5F5F5'))
.strokeWidth('1lpx')
}
}.height('108lpx')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Start)
.padding({left:'27lpx'})
}
}
.onClick(()=>{
})
.height('117lpx')
})
}.onScrollFrameBegin((offset, state) => {
return { offsetRemain: 0 }
}).layoutWeight(1)
}.width('100%')
.height('100%')
.margin({top:'46lpx'})
}
}
\ No newline at end of file
... ...
... ... @@ -3,6 +3,8 @@ import { Logger, ResourcesUtils } from 'wdKit';
import { ResponseDTO, WDHttp } from 'wdNetwork';
import HashMap from '@ohos.util.HashMap';
import { HttpUrlUtils } from '../network/HttpUrlUtils';
import { SearchHistoryItem } from '../viewmodel/SearchHistoryItem';
import { SearchHotContentItem } from '../viewmodel/SearchHotContentItem';
const TAG = "SearcherAboutDataModel"
/**
... ... @@ -10,6 +12,7 @@ const TAG = "SearcherAboutDataModel"
*/
class SearcherAboutDataModel{
private static instance: SearcherAboutDataModel;
searchHistoryData:SearchHistoryItem[] = []
private constructor() { }
... ... @@ -25,6 +28,53 @@ class SearcherAboutDataModel{
}
/**
* 静态搜索历史记录
*/
getSearchHistoryData():SearchHistoryItem[]{
if(this.searchHistoryData.length > 0){
return this.searchHistoryData
}
this.searchHistoryData.push(new SearchHistoryItem("评论"))
this.searchHistoryData.push(new SearchHistoryItem("关注关注"))
this.searchHistoryData.push(new SearchHistoryItem("收藏收藏收藏"))
this.searchHistoryData.push(new SearchHistoryItem("历史"))
this.searchHistoryData.push(new SearchHistoryItem("消息消息消息消息消息"))
this.searchHistoryData.push(new SearchHistoryItem("留言板留言板留言板留言板留言板"))
this.searchHistoryData.push(new SearchHistoryItem("预约"))
return this.searchHistoryData
}
// async putSearchHistoryData(content:string){
// let history = await SPHelper.default.get(SearcherAboutDataModel.SEARCH_HISTORY_KEY, '[]') as string;
// this.searchHistoryData = JSON.parse(history)
// this.searchHistoryData.push(new SearchHistoryItem(content))
// await SPHelper.default.save(SearcherAboutDataModel.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistoryData));
// }
// getSearchHistoryData():Promise<SearchHistoryItem[]>{
// return new Promise<SearchHistoryItem[]>(async (success, error) => {
// if(this.searchHistoryData!=null && this.searchHistoryData.length>0){
// success(this.searchHistoryData)
// return
// }
// try {
// let history = await SPHelper.default.get(SearcherAboutDataModel.SEARCH_HISTORY_KEY, '') as string;
// console.log('ycg',history);
// }catch (error){
// console.log('ycg',"1111");
// }
//
// this.searchHistoryData = JSON.parse(history)
// this.searchHistoryData.push(new SearchHistoryItem("评论"))
// this.searchHistoryData.push(new SearchHistoryItem("关注关注"))
// this.searchHistoryData.push(new SearchHistoryItem("收藏收藏收藏"))
//
// success(this.searchHistoryData)
// })
// }
/**
* 首页 搜索提示滚动内容
*/
getSearchHintData(context: Context): Promise<string[]> {
... ... @@ -62,6 +112,45 @@ class SearcherAboutDataModel{
return compRes.data
}
/**
* 搜索主页 热词
*/
getSearchHotsData(context: Context): Promise<SearchHotContentItem[]> {
return new Promise<SearchHotContentItem[]>((success, error) => {
Logger.info(TAG, `getSearchHintData start`);
this.fetchSearchHotsData().then((navResDTO: ResponseDTO<SearchHotContentItem[]>) => {
if (!navResDTO || navResDTO.code != 0) {
success(this.getSearchHotsDataLocal(context))
return
}
Logger.info(TAG, "getSearchHotsData then,getSearchHotsData.timeStamp:" + navResDTO.timestamp);
let navigationBean = navResDTO.data as SearchHotContentItem[]
success(navigationBean);
}).catch((err: Error) => {
Logger.error(TAG, `getSearchHotsData catch, error.name : ${err.name}, error.message:${err.message}`);
success(this.getSearchHotsDataLocal(context))
})
})
}
fetchSearchHotsData() {
let url = HttpUrlUtils.getSearchHotsDataUrl()
let headers: HashMap<string, string> = HttpUrlUtils.getYcgCommonHeaders();
return WDHttp.get<ResponseDTO<SearchHotContentItem[]>>(url, headers)
};
async getSearchHotsDataLocal(context: Context): Promise<SearchHotContentItem[]> {
Logger.info(TAG, `getSearchHotsDataLocal start`);
let compRes: ResponseDTO<SearchHotContentItem[]> | null = await ResourcesUtils.getResourcesJson<ResponseDTO<SearchHotContentItem[]>>('search_hots_data.json' ,context);
if (!compRes || !compRes.data) {
Logger.info(TAG, `getSearchHotsDataLocal compRes is empty`);
return []
}
Logger.info(TAG, `getSearchHotsDataLocal compRes : ${JSON.stringify(compRes)}`);
return compRes.data
}
}
... ...
... ... @@ -121,6 +121,11 @@ export class HttpUrlUtils {
*/
static readonly SEARCH_HINT_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hints";
/**
* 搜索主页 热词
*/
static readonly SEARCH_HOTS_DATA_PATH: string = "/api/rmrb-search-api/zh/c/hots";
private static hostUrl: string = HttpUrlUtils.HOST_UAT;
static getCommonHeaders(): HashMap<string, string> {
... ... @@ -308,6 +313,10 @@ export class HttpUrlUtils {
return url
}
static getSearchHotsDataUrl() {
let url = HttpUrlUtils.HOST_SIT + HttpUrlUtils.SEARCH_HOTS_DATA_PATH
return url
}
/**
* 点赞操作
... ...
... ... @@ -40,7 +40,7 @@ export class PageViewModel extends BaseViewModel {
if (mock_switch) {
return this.getBottomNavDataMock(context);
}
return this.getNavData();
return this.getNavData(context);
}
async getBottomNavDataMock(context?: Context): Promise<NavigationBodyDTO> {
... ... @@ -54,12 +54,13 @@ export class PageViewModel extends BaseViewModel {
return compRes.data
}
private getNavData(): Promise<NavigationBodyDTO> {
private getNavData(context?: Context): Promise<NavigationBodyDTO> {
return new Promise<NavigationBodyDTO>((success, error) => {
Logger.info(TAG, `getNavData start`);
PageRepository.fetchNavigationDataApi().then((navResDTO: ResponseDTO<NavigationBodyDTO>) => {
if (this.isRespondsInvalid(navResDTO, 'getNavData')) {
error("page data invalid");
// error("page data invalid");
success(this.getBottomNavDataMock(context))
return
}
Logger.info(TAG, "getNavData then,navResDTO.timeStamp:" + navResDTO.timestamp);
... ...
export class SearchHistoryItem{
searchContent:string = ""
searchUserId:string = ""
searchTime:string = ""
constructor(searchContent: string,searchUserId?:string,searchTime?:string) {
this.searchContent = searchContent
this.searchUserId = searchUserId
this.searchTime = searchTime
}
}
\ No newline at end of file
... ...
export class SearchHotContentItem{
hotEntry:string = ""
mark:number = 0 //1 热 2 新
sequence:number = 0//序号
constructor(hotEntry: string , mark: number , sequence: number ) {
this.hotEntry = hotEntry
this.mark = mark
this.sequence = sequence
}
}
\ No newline at end of file
... ...