首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《仿盒马》app开发技术分享-- 商品搜索页(搜索记录&商品搜索)(38)

《仿盒马》app开发技术分享-- 商品搜索页(搜索记录&商品搜索)(38)

原创
作者头像
用户10696402
发布于 2025-06-27 16:57:15
发布于 2025-06-27 16:57:15
3410
举报

## 技术栈

Appgallery connect

## 开发准备

上一节我们新建了商品搜索页,实现了顶部的搜索bar以及下方的推荐搜索列表,这一节我们要新增一个商品搜索记录列表,以及输入内容之后搜索出对应商品的功能,我们还需要保证搜索内容的唯一性,以及搜索记录的本地数据持久化和搜索记录列表的删除

## 功能分析

商品搜索记录列表,我们可以通过保存输入的搜索内容到用户首选项中实现。商品搜索通过输入的名称与云数据库的商品名称匹配。搜索记录的唯一性是当我们搜索相同的内容,只会产生一条记录,只需要在添加数据的时候进行一次过滤,搜索记录的删除我们通过弹窗来实现,调用封装好的删除方法,根据key删除对应的存储记录

## 代码实现

首先实现存储搜索的内容,在点击搜索时添加

```c

Text("搜索")

.border({width:1,radius:10,color:Color.White})

.fontSize(14)

.fontColor("#ffffff")

.padding({left:13,right:13,top:5,bottom:5})

.borderRadius(10)

.onClick(async ()=>{

if (this.text.trim()==''&&this.text.length==0) {

this.isSearch=false

showToast("搜索内容不能为空")

}else {

this.isSearch=true

let home_product=new cloudDatabase.DatabaseQuery(home_product_list);

home_product.equalTo("name",this.text)

let list = await databaseZone.query(home_product);

let json = JSON.stringify(list)

let data:HomeProductList[]= JSON.parse(json)

this.goodsList=data

StorageUtils.set("history_list",this.addToStringList(this.searchHistoryList,this.text))

const history = await StorageUtils.getAll("history_list")

if (history!=null&&history!=undefined) {

this.searchHistoryList=JSON.parse(history)

}

}

})

```

展示存储的搜索列表

```c

//数据初始化

async aboutToAppear(): Promise<void> {

const history = await StorageUtils.getAll("history_list")

if (history!=''&&history!=undefined) {

this.searchHistoryList=JSON.parse(history)

}

let condition = new cloudDatabase.DatabaseQuery(search_hot_txt);

let listData = await databaseZone.query(condition);

let json = JSON.stringify(listData)

let data1:SearchHotTxt[]= JSON.parse(json)

this.searchTxt=data1

this.flag=true

}

List(){

ForEach(this.searchHistoryList,(item:string)=>{

ListItem(){

Column({space:5}){

Text(item)

.fontColor("#ffa09a9a")

.padding({left:15})

.margin({top:10})

Divider().width('100%').height(0.8)

.color("#e6e6e6")

}

.alignItems(HorizontalAlign.Start)

}

})

}

```

现在数据列表已经展示出来了,但是现在我们有相同的内容展示到列表中

```c

//添加的时候处理一下

addToStringList(list: string[], newItem: string): string {

if (!list.includes(newItem)) {

list.push(newItem);

}

return JSON.stringify(list);

}

```

根据搜索内容查询对应的商品

```c

let home_product=new cloudDatabase.DatabaseQuery(home_product_list);

home_product.equalTo("name",this.text)

let list = await databaseZone.query(home_product);

let json = JSON.stringify(list)

let data:HomeProductList[]= JSON.parse(json)

this.goodsList=data

```

添加商品展示的列表组件

```c

WaterFlow() {

ForEach(this.goodsList, (item:HomeProductList, index) => {

FlowItem() {

Column() {

Image(item.url)

.width('100%')

.aspectRatio(1)

.objectFit(ImageFit.Cover)

.borderRadius({topLeft:10,topRight:10})

Column() {

Text(item.name)

.fontSize(16)

.fontColor('#333')

.margin({ bottom: 4 })

Text(item.text_message)

.fontSize(12)

.fontColor('#666')

.margin({ bottom: 8 })

Text("最高立减"+item.promotion_spread_price)

.fontSize(12)

.fontColor('#ffffff')

.visibility(item.promotion_spread_price>0?Visibility.Visible:Visibility.None)

.margin({ bottom: 8 })

.padding({left:5,right:5,top:2,bottom:2})

.linearGradient({

angle:90,

colors: [[0xff0000, 0], [0xff6666, 0.2], [0xff6666, 1]]

})

Row(){

Text("限购")

.width(40)

.fontSize(12)

.borderRadius(20)

.backgroundColor("#FB424C")

.padding(3)

.textAlign(TextAlign.Center)

Text("每人限购"+item.max_loop_amount+"件")

.margin({left:5})

.fontSize(12)

.fontColor("#FB424C")

}

.borderRadius(20)

.padding({top:2,bottom:2,right:10})

.backgroundColor("#FEE3E3")

.visibility(item.amount>0?Visibility.Visible:Visibility.None)

Row() {

Text(){

Span("¥")

.fontColor(Color.Red)

.fontSize(14)

Span(String(item.price))

.fontSize(16)

.fontColor(Color.Red)

}

Text(String(item.original_price))

.fontSize(12)

.fontColor('#999')

.decoration({

type: TextDecorationType.LineThrough,

color: Color.Gray

})

.margin({left:10})

Blank()

Column() {

Image($r('app.media.cart'))

.width(20)

.height(20)

}

.justifyContent(FlexAlign.Center)

.width(36)

.height(36)

.backgroundColor("#ff2bd2fa")

.borderRadius(18)

}

.margin({top:10})

.width('100%')

.justifyContent(FlexAlign.SpaceBetween)

}

.alignItems(HorizontalAlign.Start)

.padding(12)

}

.backgroundColor(Color.White)

.borderRadius(12)

.onClick(() => {

let product: ProductDetailModel = {

id: item.id

};

router.pushUrl({

url: 'pages/component/ProductDetailsPage',

params: product

}, (err) => {

if (err) {

console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);

return;

}

console.info('Invoke pushUrl succeeded.');

});

})

}

.margin({ bottom: 12 })

})

}

.padding(10)

.columnsTemplate('1fr 1fr')

.columnsGap(12)

.onAreaChange((oldVal, newVal) => {

this.columns = newVal.width > 600 ? 2 : 1

})

.layoutWeight(1)

.visibility(this.isSearch?Visibility.Visible:Visibility.None)

```

现在我们的效果已经实现

接下来是列表的删除

```c

Text("清空")

.fontSize(14)

.fontColor("#ff989b9b")

.onClick(()=>{

promptAction.showDialog({

title: '提示',

message: '是否清空搜索历史?',

buttons: [

{

text: '确认',

color: '#ffffff'

},

{

text: '关闭',

color: '#ffffff'

}

],

})

.then(data => {

this.searchHistoryList.length=0

StorageUtils.remove("history_list")

console.info('showDialog success, click button: ' + data.index);

})

.catch((err: Error) => {

console.info('showDialog error: ' + err);

})

})

```

到这里我们就实现了商品搜索页的内容

全部代码如下

```c

import { promptAction, router } from '@kit.ArkUI'

import showToast from '../utils/ToastUtils'

import { cloudDatabase } from '@kit.CloudFoundationKit'

import { search_hot_txt } from '../clouddb/search_hot_txt'

import { SearchHotTxt } from '../entity/SearchHotTxt'

import { StorageUtils } from '../utils/StorageUtils'

import { HomeProductList } from '../entity/HomeProductList'

import { ProductDetailModel } from '../model/ProductDetailModel'

import { home_product_list } from '../clouddb/home_product_list'

let databaseZone = cloudDatabase.zone('default');

@Entry

@Component

struct ProductSearchPage {

@State text: string = ''

controller: TextInputController = new TextInputController()

@State searchTxt:SearchHotTxt[]=[]

@State searchHistoryList:string[]=[]

@State flag:boolean=false

@State isSearch:boolean=false

@State goodsList: HomeProductList[]=[]

@State columns: number = 2

async aboutToAppear(): Promise<void> {

const history = await StorageUtils.getAll("history_list")

if (history!=''&&history!=undefined) {

this.searchHistoryList=JSON.parse(history)

}

let condition = new cloudDatabase.DatabaseQuery(search_hot_txt);

let listData = await databaseZone.query(condition);

let json = JSON.stringify(listData)

let data1:SearchHotTxt[]= JSON.parse(json)

this.searchTxt=data1

this.flag=true

}

build() {

Column(){

Row(){

Image($r('app.media.left_back'))

.height(20)

.width(20)

.onClick(()=>{

router.back()

})

TextInput({ text: this.text, placeholder: '输入商品名搜索', controller: this.controller })

.placeholderColor(Color.White)

.placeholderFont({ size: 16, weight: 400 })

.caretColor(Color.White)

.width(200)

.fontSize(16)

.fontColor(Color.White)

.onChange((value: string) => {

this.text = value

if (value.length==0) {

this.isSearch=false

}

})

Text("搜索")

.border({width:1,radius:10,color:Color.White})

.fontSize(14)

.fontColor("#ffffff")

.padding({left:13,right:13,top:5,bottom:5})

.borderRadius(10)

.onClick(async ()=>{

if (this.text.trim()==''&&this.text.length==0) {

this.isSearch=false

showToast("搜索内容不能为空")

}else {

this.isSearch=true

let home_product=new cloudDatabase.DatabaseQuery(home_product_list);

home_product.equalTo("name",this.text)

let list = await databaseZone.query(home_product);

let json = JSON.stringify(list)

let data:HomeProductList[]= JSON.parse(json)

this.goodsList=data

StorageUtils.set("history_list",this.addToStringList(this.searchHistoryList,this.text))

const history = await StorageUtils.getAll("history_list")

if (history!=null&&history!=undefined) {

this.searchHistoryList=JSON.parse(history)

}

}

})

}

.justifyContent(FlexAlign.SpaceBetween)

.width('100%')

.padding({top:10,bottom:10,left:15,right:15})

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

.backgroundColor("#ff0000")

Column(){

Text("热门搜索")

.width('100%')

.fontSize(16)

.fontColor("#000000")

.fontWeight(FontWeight.Bold)

.padding({left:15,top:15})

if (this.flag){

Flex({wrap:FlexWrap.Wrap}){

ForEach(this.searchTxt,(item:SearchHotTxt,index:number)=>{

Text(item.txt)

.backgroundColor("#ffe7e5e5")

.fontColor("#000000")

.fontWeight(FontWeight.Bold)

.fontSize(16)

.padding(10)

.margin({top:10,left:10})

.borderRadius(5)

.onClick(()=>{

this.text=item.txt

})

})

}

Row(){

Text("搜索历史")

.fontSize(16)

.fontColor("#000000")

.fontWeight(FontWeight.Bold)

Text("清空")

.fontSize(14)

.fontColor("#ff989b9b")

.onClick(()=>{

promptAction.showDialog({

title: '提示',

message: '是否清空搜索历史?',

buttons: [

{

text: '确认',

color: '#ffffff'

},

{

text: '关闭',

color: '#ffffff'

}

],

})

.then(data => {

this.searchHistoryList.length=0

StorageUtils.remove("history_list")

console.info('showDialog success, click button: ' + data.index);

})

.catch((err: Error) => {

console.info('showDialog error: ' + err);

})

})

}

.width('100%')

.justifyContent(FlexAlign.SpaceBetween)

.padding({left:15,right:15})

.margin({top:30})

List(){

ForEach(this.searchHistoryList,(item:string)=>{

ListItem(){

Column({space:5}){

Text(item)

.fontColor("#ffa09a9a")

.padding({left:15})

.margin({top:10})

Divider().width('100%').height(0.8)

.color("#e6e6e6")

}

.alignItems(HorizontalAlign.Start)

}

})

}

}

}

.layoutWeight(1)

.width('100%')

.backgroundColor(Color.White)

.visibility(this.isSearch?Visibility.None:Visibility.Visible)

WaterFlow() {

ForEach(this.goodsList, (item:HomeProductList, index) => {

FlowItem() {

Column() {

Image(item.url)

.width('100%')

.aspectRatio(1)

.objectFit(ImageFit.Cover)

.borderRadius({topLeft:10,topRight:10})

Column() {

Text(item.name)

.fontSize(16)

.fontColor('#333')

.margin({ bottom: 4 })

Text(item.text_message)

.fontSize(12)

.fontColor('#666')

.margin({ bottom: 8 })

Text("最高立减"+item.promotion_spread_price)

.fontSize(12)

.fontColor('#ffffff')

.visibility(item.promotion_spread_price>0?Visibility.Visible:Visibility.None)

.margin({ bottom: 8 })

.padding({left:5,right:5,top:2,bottom:2})

.linearGradient({

angle:90,

colors: [[0xff0000, 0], [0xff6666, 0.2], [0xff6666, 1]]

})

Row(){

Text("限购")

.width(40)

.fontSize(12)

.borderRadius(20)

.backgroundColor("#FB424C")

.padding(3)

.textAlign(TextAlign.Center)

Text("每人限购"+item.max_loop_amount+"件")

.margin({left:5})

.fontSize(12)

.fontColor("#FB424C")

}

.borderRadius(20)

.padding({top:2,bottom:2,right:10})

.backgroundColor("#FEE3E3")

.visibility(item.amount>0?Visibility.Visible:Visibility.None)

Row() {

Text(){

Span("¥")

.fontColor(Color.Red)

.fontSize(14)

Span(String(item.price))

.fontSize(16)

.fontColor(Color.Red)

}

Text(String(item.original_price))

.fontSize(12)

.fontColor('#999')

.decoration({

type: TextDecorationType.LineThrough,

color: Color.Gray

})

.margin({left:10})

Blank()

Column() {

Image($r('app.media.cart'))

.width(20)

.height(20)

}

.justifyContent(FlexAlign.Center)

.width(36)

.height(36)

.backgroundColor("#ff2bd2fa")

.borderRadius(18)

}

.margin({top:10})

.width('100%')

.justifyContent(FlexAlign.SpaceBetween)

}

.alignItems(HorizontalAlign.Start)

.padding(12)

}

.backgroundColor(Color.White)

.borderRadius(12)

.onClick(() => {

let product: ProductDetailModel = {

id: item.id

};

router.pushUrl({

url: 'pages/component/ProductDetailsPage',

params: product

}, (err) => {

if (err) {

console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);

return;

}

console.info('Invoke pushUrl succeeded.');

});

})

}

.margin({ bottom: 12 })

})

}

.padding(10)

.columnsTemplate('1fr 1fr')

.columnsGap(12)

.onAreaChange((oldVal, newVal) => {

this.columns = newVal.width > 600 ? 2 : 1

})

.layoutWeight(1)

.visibility(this.isSearch?Visibility.Visible:Visibility.None)

}

.height('100%')

.width('100%')

.backgroundColor(Color.White)

}

addToStringList(list: string[], newItem: string): string {

if (!list.includes(newItem)) {

list.push(newItem);

}

return JSON.stringify(list);

}

}

```

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档