《仿盒马》app开发技术分享-- 商品搜索页(搜索记录&商品搜索)(38)
2025-06-25 11:47:40
109次阅读
0个评论
技术栈
Appgallery connect
开发准备
上一节我们新建了商品搜索页,实现了顶部的搜索bar以及下方的推荐搜索列表,这一节我们要新增一个商品搜索记录列表,以及输入内容之后搜索出对应商品的功能,我们还需要保证搜索内容的唯一性,以及搜索记录的本地数据持久化和搜索记录列表的删除
功能分析
商品搜索记录列表,我们可以通过保存输入的搜索内容到用户首选项中实现。商品搜索通过输入的名称与云数据库的商品名称匹配。搜索记录的唯一性是当我们搜索相同的内容,只会产生一条记录,只需要在添加数据的时候进行一次过滤,搜索记录的删除我们通过弹窗来实现,调用封装好的删除方法,根据key删除对应的存储记录
代码实现
首先实现存储搜索的内容,在点击搜索时添加
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)
}
}
})
展示存储的搜索列表
//数据初始化
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)
}
})
}
现在数据列表已经展示出来了,但是现在我们有相同的内容展示到列表中
//添加的时候处理一下
addToStringList(list: string[], newItem: string): string {
if (!list.includes(newItem)) {
list.push(newItem);
}
return JSON.stringify(list);
}
根据搜索内容查询对应的商品
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
添加商品展示的列表组件
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)
现在我们的效果已经实现
接下来是列表的删除
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);
})
})
到这里我们就实现了商品搜索页的内容
全部代码如下
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);
}
}
00
- 0回答
- 0粉丝
- 0关注
相关话题
- 《仿盒马》app开发技术分享-- 商品搜索页(顶部搜索bar&热门搜索)(37)
- 《仿盒马》app开发技术分享-- 回收记录页(47)
- 《仿盒马》app开发技术分享-- 商品详情页(10)
- 《仿盒马》app开发技术分享-- 商品规格弹窗(11)
- 《仿盒马》app开发技术分享-- 首页商品流(7)
- 《仿盒马》app开发技术分享-- 分类右侧商品列表(18)
- 《仿盒马》app开发技术分享-- 地址管理页(24)
- 《仿盒马》app开发技术分享-- 个人信息页(23)
- 《仿盒马》app开发技术分享-- 订单列表页(33)
- 《仿盒马》app开发技术分享-- 订单详情页(32)
- 《仿盒马》app开发技术分享-- 旧物回收页(静态)(40)
- 《仿盒马》app开发技术分享-- 用户登录页(业务逻辑)(21)
- 《仿盒马》app开发技术分享-- 确认订单页(数据展示)(29)
- 《仿盒马》app开发技术分享-- 确认订单页(业务逻辑)(30)
- 《仿盒马》app开发技术分享-- 旧物回收页(业务逻辑)(41)