当前位置: 首页 > wzjs >正文

自适应网站建设价格wordpress 热门标签

自适应网站建设价格,wordpress 热门标签,wordpress 文章缩略图,网站数据维护1. 购物车 咱们购物车基于 V2 装饰器进行开发,底气来源于 自定义组件混用场景指导 1.1. 素材整合 observedv2和Trace 数据模型和页面 // 其他略 // 购物车 export interface CartGoods {count: number;id: string;name: string;picture: string;price: number;…

1. 购物车

咱们购物车基于 V2 装饰器进行开发,底气来源于

自定义组件混用场景指导

1.1. 素材整合

observedv2和Trace

  1. 数据模型和页面

// 其他略
// 购物车
export interface CartGoods {count: number;id: string;name: string;picture: string;price: number;selected: boolean;skuId: string;stock: number;attrsText: string;
}@ObservedV2
export class CartGoodsModel implements CartGoods {@Tracecount: number = 0id: string = ''name: string = ''picture: string = ''price: number = 0@Traceselected: boolean = falseskuId: string = ''stock: number = 0attrsText: string = ''constructor(model: CartGoods) {this.count = model.countthis.id = model.idthis.name = model.namethis.picture = model.picturethis.price = model.pricethis.selected = model.selectedthis.skuId = model.skuIdthis.stock = model.stockthis.attrsText = model.attrsText}
}
  1. 静态页面
import { auth, MkGuess, MkNavbar, MkUser, CartGoodsModel, CartGoods } from 'basic'@Component
export struct CartView {// 用户信息@StorageProp(auth.KEY) user: MkUser = {} as MkUser// 顶部安全区域@StorageProp('safeTop') safeTop: number = 0// ObserveV2 无法直接和@State 使用 会报错 但是包到数组中是可以使用的// @State cartData: CartGoodsModel = new CartGoodsModel({} as CartGoods)// 商品信息@State cartList: CartGoodsModel[] = []aboutToAppear(): void {}onCheckOrder() {AlertDialog.show({message: '去结算'})}@BuilderDeleteBuilder(onDelete: () => void) {Text('删除').fontSize(14).width(60).height(100).backgroundColor($r('[basic].color.red')).fontColor($r('[basic].color.white')).textAlign(TextAlign.Center).onClick(() => {onDelete()})}build() {Column() {MkNavbar({ title: '购物袋', showLeftIcon: false, showRightIcon: true }).border({width: { bottom: 0.5 },color: '#e4e4e4'})List() {if (this.user.token) {if (this.cartList.length) {ForEach(this.cartList, (cart: CartGoodsModel) => {ListItem() {CartItemComp({cart})}.backgroundColor($r('[basic].color.under')).padding({ left: 8, right: 8 }).transition({ type: TransitionType.Delete, opacity: 0 }).swipeAction({end: this.DeleteBuilder(async () => {AlertDialog.show({message: 'clickDel'})})})})} else {ListItem() {Text('无商品')}}} else {// 未登录ListItem() {Text('未登录')}}ListItem() {MkGuess().margin({ top: 8, bottom: 8 })}}.contentStartOffset(8).width('100%').layoutWeight(1).scrollBar(BarState.Off)if (this.cartList.length) {Row() {// TODO 添加自定义 CheckBoxText('全选').fontSize(14).fontColor($r('[basic].color.black')).margin({ right: 20 })Text('合计:').fontSize(14).fontColor($r('[basic].color.black')).margin({ right: 2 })Text('998').fontSize(16).fontWeight(500).fontColor($r('[basic].color.red')).layoutWeight(1)Button('去结算').fontSize(14).height(36).backgroundColor($r('[basic].color.red')).onClick(() => {})}.height(50).width('100%').backgroundColor($r('[basic].color.white')).border({width: { top: 0.5, bottom: 0.5 },color: '#e4e4e4'}).padding({ left: 16, right: 16 })}}.height('100%').width('100%').backgroundColor($r('[basic].color.under')).padding({ top: this.safeTop })}
}@ComponentV2
struct CartItemComp {@Param // v1 Prop 自己的状态 @Localcart: CartGoodsModel = new CartGoodsModel({} as CartGoods)build() {Row({ space: 10 }) {// TODO 添加自定义 CheckBoxImage(this.cart.picture).width(80).height(80)Column({ space: 8 }) {Text(this.cart.name).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontSize(14).fontColor($r('[basic].color.black')).width('100%')Row() {Text(this.cart.attrsText).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontSize(12).fontColor($r('[basic].color.text')).layoutWeight(1)Image($r('sys.media.ohos_ic_public_arrow_down')).fillColor($r('[basic].color.gray')).width(16).height(16)}.padding({ left: 6, right: 4 }).width(100).height(24).backgroundColor($r('[basic].color.under')).borderRadius(2)Row() {Text(`¥${this.cart.price}`).fontSize(14).fontWeight(500)Counter()}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.layoutWeight(1).alignItems(HorizontalAlign.Start)}.width('100%').height(100).padding(10).border({ width: { bottom: 0.5 }, color: '#e4e4e4' }).backgroundColor($r('[basic].color.white'))}
}
  1. 在 oh-package.json5 文件中导入 common/basic
{"name": "cart","version": "1.0.0","description": "Please describe the basic information.","main": "Index.ets","author": "","license": "Apache-2.0","packageType": "InterfaceHar","dependencies": {"basic": "file:../../commons/basic"}
}

git 记录

Cart-素材整合

1.2. MkEmpty 组件封装(V2实现)

V2装饰器

先来搞定购物车中间部分的显示效果,通过组件来完成

核心步骤:

  1. 创建组件并暴露
  2. 根据文档实现功能
  3. 页面中导入并测试使用
    1. 已登录,无商品,点击去第一个 Tab(下一节做)
    2. 未登录,点击去登录页

基础模版

@ComponentV2export struct MkEmpty {build() {Column({ space: 20 }) {Image($r('app.media.ic_public_empty')).width(120).aspectRatio(1)Text('信息').fontSize(12).fontColor($r('app.color.gray'))Button('去逛逛').fontSize(12).height(26).padding({ left: 20, right: 20 }).linearGradient({angle: 90,colors: [['#FD3F8F', 0], ['#FF773C', 1]]}).margin({ bottom: 20 })}.padding(40).width('100%')}}

属性名

类型

说明

默认值

tip

string

提示信息

空字符串

buttonText

string

按钮文本

空字符串

icon

ResourceStr

中间图标

ic_public_empty

onClickButton

()=>void

点击按钮的逻辑

空方法

参考代码

@ComponentV2
export struct MkEmpty {@Paramtip: string = '空空如也'@ParambuttonText: string = '去逛逛'@Paramicon: ResourceStr = $r('app.media.ic_public_empty')@EventonClickButton: () => void = () => {}build() {Column({ space: 20 }) {Image(this.icon).width(120).aspectRatio(1)Text(this.tip).fontSize(12).fontColor($r('app.color.gray'))Button(this.buttonText).fontSize(12).height(26).padding({ left: 20, right: 20 }).linearGradient({angle: 90,colors: [['#FD3F8F', 0], ['#FF773C', 1]]}).margin({ bottom: 20 }).onClick(() => {this.onClickButton()})}.padding(40).width('100%')}
}
List() {if (this.user.token) {if (this.cartList.length) {// 略} else {ListItem() {MkEmpty({tip: '购物袋是空的~',buttonText: '去逛逛',onClickButton: () => {// 去第一个 Tab}})}}} else {// 未登录ListItem() {MkEmpty({tip: '您还未登陆~',buttonText: '请先登录',onClickButton: () => {// 去登录页}})}}ListItem() {MkGuess().margin({ top: 8, bottom: 8 })}
}

git记录

Cart-MkEmpty组件封装

1.3. 去首页

使用Emitter进行线程间通信

需求及分析:

  1. 已登录,购物车没有商品的时候,点击切换 tab 到第一个【首页】
  2. feature/cart -> products/phone 可以通过??? 来实现通讯
  3. products/phone 如何控制 tab 切换

核心步骤:

  1. 增加 emmit 的事件 id 常量,并导出 (方便管理)
  2. phone/index 注册事件,接收参数
  3. feature/cart 触发事件,传递数据
  4. phone/index 控制 tab 切换
// 其他略
export class EmitterKey {// 切换到指定的 tabstatic CHANGE_TAB: emitter.InnerEvent = { eventId: 10001 }
}
aboutToAppear(): void {this.registerEvent()
}// 注册 emmiter
registerEvent() {// 订阅事件emitter.on(EmitterKey.CHANGE_TAB, (eventData) => {this.activeIndex = eventData.data?.index || 0  // 不传值也能回首页})
}Tabs({ barPosition: BarPosition.End, index: this.activeIndex }){// 其他略
}
MkEmpty({tip: '购物袋是空的',buttonText: '去逛逛',onClickButton: () => {// 发送事件emitter.emit(EmitterKey.CHANGE_TAB, {data: {index: 0}})}
})

git 记录

Cart-去首页

1.4. 加入购物车

接口文档

需求:

  1. 详情页点击添加到购物车
  2. 更新服务器数据

核心步骤:

  1. 抽取 api,丢到 common
  2. 页面实现交互逻辑
    1. token 校验-》sku 校验-》提交-》api调用-》
  1. 通过一个类管理所有购物车接口,因为有一整套的 CRUD
import { RequestAxios } from '../utils/Request'interface AddParams {skuId: stringcount: number
}class Cart {// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}
}export const cart = new Cart()
  1. 导出
// 导出购物车封装购物车请求 API 的类,这个类只需导出一次即可
export { cart } from './src/main/ets/apis/cart'
  1. 整合业务,在商品详情页调用 加入购物车 接口
  // 加入购物车async addToCart() {// 1. 判断用户是否登录,未登录需要登录const token = auth.getUser().tokenif (!token) {promptAction.showToast({ message: '请登录后下单' })this.pageStack.pushPath({ name: 'LoginView' })return}// 2. 判断用户是否有选择商品规则(是否完整)if (!this.sku.id) {promptAction.showToast({ message: '请选择要购买的商品规格' })return}// 3. 加入购物车this.loading = true// await 等待请求成功await cart.add({ skuId: this.sku.id, count: this.count })this.loading = falsepromptAction.showToast({ message: '加入购物袋成功' })// 4. 关闭半模态弹窗this.showSheet = false}// 其他略
Button(this.loading ? '加入中...' : '加入购物袋').buyButton($r('[basic].color.black'), true).onClick(async () => {this.addToCart()})

git 记录

Cart-加入购物车

1.5. 个数及页面跳转

多个地方需要用到个数,并且在很多时候都需要刷新

1.5.1. 详情页面个数更新

需求:

  1. 获取最新个数(通过接口)
  2. 更新购物车个数(考虑其他界面获取)

核心步骤:

  1. 抽取 api:
    1. 有 Token
      1. 获取数据,具体的值
    1. 没 Token
      1. 格式为 0
    1. 更新到AppStorage
  1. 组件中获取并渲染
import { RequestAxios } from '../utils/Request'interface AddParams {skuId: stringcount: number
}interface CountResult {count: number
}export class Cart {// AppStorage 的 KeyCartKey: string = 'cartCount'// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}// 获取购物车数量async count() {let count = 0// 用户已登录才发请求获取const userInfo = auth.getUser()if (userInfo.token) {// 获取购物车数量const res = await RequestAxios.get<CountResult>('/member/cart/count')// 更新变量,用于全局存储count = res.count}// 全局存储购物车数量AppStorage.setOrCreate(this.CartKey, count)}
}export const cart = new Cart()
// 购物车商品数量
@StorageProp(cart.CartKey) cartCount: number = 0Badge({count: this.cartCount,style: {},position: { x: 30, y: 4 }
}) /*** 加入购物车* */
async addToCart() {// 登录判断if (!auth.getUser().token) {promptAction.showToast({message: '请登录后下单'})return pathStack.pushPathByName('LoginView', null)}// 是否选择商品if (!this.sku.id) {return promptAction.showToast({message: '请选择商品规格'})}this.loading = true// 加入购物车await cart.add({skuId: this.sku.id,count: this.count})promptAction.showToast({message: '加入购物袋成功'})// 关闭半模态this.showSheet = falsethis.loading = false// 更新数量cart.count()
}Button(this.loading ? '加入中...' : '加入购物袋').buyButton($r('app.color.black'), true).onClick(async () => {this.addToCart()})

git 记录

Cart-详情页个数更新

1.5.2. 首页更新数量

需求:

  1. 首页增加角标
  2. 获取个数并渲染
  3. 如果是首次打开页面,获取个数(已登录的情况)

核心步骤:

  1. 增加 Badge 组件
  2. 通过 AppStorage 获取数据并渲染
  3. 生命周期钩子中获取数据
  1. 更新首页购物车数量
@StorageProp(cart.CartKey) cartCount: number = 0aboutToAppear(): void {this.breakpointSystem.register()// 获取购物车数量cart.count()
}@BuilderTabItemBuilder(item: TabItem, index: number) {Badge({count: index === 2 ? this.cartCount : 0,style: {},}) {Column() {Image(this.activeIndex === index ? item.active : item.normal).width(24).aspectRatio(1)Text(item.text).fontColor($r('app.color.black')).fontSize(12)}.justifyContent(FlexAlign.SpaceEvenly).height(50)}
}

git记录

Cart-首页个数更新

1.5.3. 登录和登出更新数量

登录和登出:

  1. 登录更新:
    1. 账号密码登录,华为登录之后,保存用户信息同时更新购物车数量
  1. 登出更新:
    1. 设置页,退出登录时,删除用户信息,同时更新购物车数量
    2. cart.count() --> 0
  1. 保存用户信息、删除用户信息时,都要更新购物车数量
import { cart } from "../apis/cart"export interface MkUser {token: stringnickname: stringavatar: stringaccount: string
}class Auth {KEY: string = 'user'initUser() {// 把 AppStorage 的用户信息进行持久化 UI 状态存储,状态改变自动同步到本地磁盘PersistentStorage.persistProp(this.KEY, {})}getUser() {return AppStorage.get<MkUser>(this.KEY) || {} as MkUser}saveUser(user: MkUser) {AppStorage.setOrCreate<MkUser>(this.KEY, user)// 登录时,更新购物车数量cart.count()}removeUser() {AppStorage.setOrCreate<MkUser>(this.KEY, {} as MkUser)// 登出时,更新购物车数量cart.count()}
}// 导出实例化对象,单例
export const auth = new Auth()

git记录

Cart-登录登出更新购物车数量

1.6. 跳转购物车页

需求:

  1. 点击购物车图标,打开购物袋页面
  2. tabs打开时,没有返回按钮
  3. 跳转打开时,显示返回按钮

注意:

  1. 之前购物袋只是 Tabs 渲染的组件,并不是页面,没有注册页面路由。

核心步骤

  1. feature/CartView
    1. 增加 Builder 入口函数 和 NavDestination 组件
    2. 配置路由表 route_map.json
    3. 配置 module.json5
  1. 详情页,点击跳转即可
    1. 优化:详情页跳转的时候【显示返回按钮】,并可以点击【返回上一页】
    2. 通过携带参数实现效果
  1. 增加 Builder 入口函数 和 NavDestination 组件
// 1.1 路由入口函数
@Builder
function CartViewBuilder() {// 1.2 子路由组件NavDestination() {CartView()}.hideTitleBar(true)
}@Component
export struct CartView {}
  1. 新建路由表文件,并配置路由表
{"routerMap": [{"name": "CartView","pageSourceFile": "src/main/ets/views/CartView.ets","buildFunction": "CartViewBuilder"}]
}
  1. 配置 module.json5,让模块的路由表生效
{"module": {"name": "cart","type": "shared","description": "$string:shared_desc","deviceTypes": ["phone","tablet","2in1"],"deliveryWithInstall": true,"pages": "$profile:main_pages","routerMap": "$profile:route_map"}
}
  1. 页面跳转,并传参
// 购物袋角标
Badge({count: this.cartCount,style: {},position: { x: 30, y: 4 }
}) {Image($r('[basic].media.ic_public_cart')).iconButton().onClick(() => {pathStack.pushPathByName('CartView', true)})
}
  1. 获取参数
@State isShowLeftIcon: boolean = falseaboutToAppear(): void {const params = pathStack.getParamByName('CartView') as boolean[]if (params.length > 0) {this.isShowLeftIcon = params.pop() as boolean}
}// 其他内容略
MkNavbar({title: '购物袋',showRightIcon: true,showLeftIcon: this.isShowLeftIcon,leftClickHandler: () => {pathStack.pop()}
})

git 记录

Cart-去购物车页

1.7. 购物车列表渲染

接口文档

需求:

  1. 获取购物车数据并渲染

核心步骤:

  1. 抽取接口
  2. 获取数据之后通过 new 转对象(嵌套数据更新)
  3. 这个过程需要在 2 种情况下执行(分两步完成)
    1. 首次进入,有 token 就获取(生命周期钩子)
    2. 添加商品到购物车中(详情页--》购物车页)

1.7.1. 默认渲染

import { RequestAxios } from '../utils/Request'
import { CartGoods } from '../viewmodel'interface AddParams {skuId: stringcount: number
}interface CountResult {count: number
}class Cart {CartKey: string = 'cartCount'// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}// 获取个数async count() {const res = await RequestAxios.get<CountResult>('/member/cart/count')AppStorage.setOrCreate(this.CartKey, res.data.result.count)return res.data.result.count}// 获取列表async list() {return RequestAxios.get<CartGoods[]>('/member/cart')}}export const cart = new Cart()
aboutToAppear(): void {const params = pathStack.getParamByName('CartView') as boolean[]if (params.length > 0) {this.isShowLeftIcon = params.pop() as boolean}this.getData()
}async getData() {if (!auth.getUser().token) {return}const res = await cart.list()this.cartList = res.map((item) => new CartGoodsModel(item))
}

git 记录

Cart-默认渲染

1.7.2. 更新购物车列表

组件可见区域变化事件

问题分析:

  1. 购物车,每次打开重新获取列表数据
    1. 不是页面,只有 aboutToAppear 钩子
    2. 第一次打开之后就不会被销毁,不会再次触发
  1. 详情页
    1. 跳转去购物车是新页面,会触发 aboutToAppear 可以刷新
  1. tab 页
    1. 切换到 购物车 tab 时需要获取列表(不会再次触发aboutToAppear)

思考:如何实现 购物车页面数据刷新?

  1. 通过 emitter 触发刷新
  2. 组件可视区域改变时刷新 onVisibleAreaChange

@Component
export struct CartView {// ...省略aboutToAppear(): void {const params = pathStack.getParamByName('CartView') as boolean[]if (params.length > 0) {this.isShowLeftIcon = params.pop() as boolean}// this.getData() 拿掉}// 获取购物袋列表async getData() {// 用户已登录的情况下,才获取购物袋列表const userInfo = auth.getUser()if (userInfo.token) {// 显示加载框this.dialog.open()const res = await cart.list()// map 把每个普通对象转换成 CartGoodsModel 对象,更新对象时图片不会闪动this.cartList = res.data.result.map(item => new CartGoodsModel(item))// 隐藏加载框this.dialog.close()}}// ...省略build() {Column() {// ...}.height('100%').width('100%').backgroundColor($r('[basic].color.under')).onVisibleAreaChange([0, 1], (isVisible) => {if (isVisible) {this.getData()}})}
}

git 记录

Cart-更新购物车列表

1.8. 个数更新

接口文档

需求:

  1. 修改【个数】时
  2. 更新服务器,更新本地
  3. 更新购物车个数

核心步骤:

  1. 实现Count组件的个数渲染及禁用外观
  2. 累加及递减逻辑:
    1. 修改服务器数据(*)
    2. 更新本地数据
    3. 更新个数
    4. 伴随 loading 效果
  1. 选中:
    1. 修改服务器数据(*)
    2. 更新本地数据
import { auth } from '../../../../Index'
import { RequestAxios } from '../utils/Request'
import { CartGoods } from '../viewmodel'// 导出 页面中 会用到
export interface UpdateParams {selected?: booleancount?: number
}interface AddParams {skuId: stringcount: number
}interface CountResult {count: number
}export const CartKey: string = 'cartCount'export class Cart {// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}// 获取个数async count() {let count = 0if (auth.getUser().token) {const res = await RequestAxios.get<CountResult>('/member/cart/count')count = res.data.result.count}AppStorage.setOrCreate(CartKey, count)}// 获取列表async list() {return RequestAxios.get<CartGoods[]>('/member/cart')}// 更新商品async update(skuId: string, data: UpdateParams) {return RequestAxios.put<CartGoods>(`/member/cart/${skuId}`, data)}
}export const cart = new Cart()
// 其他略
async updateCart(params: UpdateParams) {await cart.update(this.cart.skuId, params)// 如果是更新个数 不传递时为 undefinedif (params.count) {this.cart.count = params.countcart.count()}
}// 计数器
Counter() {Text(this.cart.count.toString())
}
.enableDec(this.cart.count > 1) // 控制减号是否可用
.enableInc(this.cart.count < this.cart.stock) // 控制加号是否可用
.onInc(() => {this.updateCart({ count: this.cart.count + 1 })
})
.onDec(() => {this.updateCart({ count: this.cart.count - 1 })
})

git 记录

Cart-个数及选中状态更新

1.9. MkCheckBox组件

checkBox

原生的 CheckBox 使用起来有一些不便,咱们来:

  1. 试试这个不便之处
  2. 然后自己实现个组件,替换到 购物车页面的 【列表项】和【全选】

如果不用双向绑定那么 选中状态 和 绑定的状态变量就没有关联

@Entry
@Component
struct Page06 {@State isChecked: boolean = falsebuild() {Column() {Text(JSON.stringify(this.isChecked))Checkbox().select(this.isChecked).onClick(() => {this.isChecked = true})}.height('100%').width('100%').backgroundColor(Color.Orange).padding(40)}
}

基础模版

@ComponentV2
export struct MkCheckBox {build() {// $r('app.media.ic_public_check_filled')选中  $r('app.media.ic_public_check') 未选中Image($r('app.media.ic_public_check_filled')).width(18)// $r('app.color.red') 选中 $r('app.color.gray') 默认.fillColor($r('app.color.red')).aspectRatio(1).margin(2)}
}
// 其他略 记得导出
export { MkCheckBox } from './src/main/ets/components/MkCheckBox'
// 添加全选 其他略
MkCheckBox({
})
Text('全选').fontSize(14).fontColor($r('[basic].color.black')).margin({ right: 20 })@ComponentV2
struct CartItemComp {@Paramcart: CartGoodsModel = new CartGoodsModel({} as CartGoods)async updateCart(params: UpdateParams) {await cart.update(this.cart.skuId, params)if (params.selected !== undefined) {this.cart.selected = params.selected}if (params.count) {this.cart.count = params.countcart.count()}}build() {Row({ space: 10 }) {// 添加单选 其他略MkCheckBox({})Image(this.cart.picture).width(80).height(80)}.width('100%').height(100).padding(10).border({ width: { bottom: 0.5 }, color: '#e4e4e4' }).backgroundColor($r('[basic].color.white'))}
}

属性名

类型

说明

默认值

checked

boolean

是否选中

false

boxSize

number

大小

18

onClickBox

()=>void

点击组件

()=>{}

@ComponentV2
export struct MkCheckBox {@Param checked: boolean = false@Param boxSize: number = 18@EventonClickBox: () => void = () => {}build() {Image(this.checked ? $r('app.media.ic_public_check_filled') : $r('app.media.ic_public_check')).width(this.boxSize).fillColor(this.checked ? $r('app.color.red') : $r('app.color.gray')).aspectRatio(1).margin(2).onClick(() => this.onClickBox())}
}

git记录

Cart-MkCheckBox组件封装

1.10. 选中状态更新

接口文档

需求:

  1. 修改【选中状态】时
  2. 更新服务器,更新本地

核心步骤:

  1. 点击选中:
    1. 修改服务器数据(*)
    2. 更新本地数据
@ComponentV2
struct CartItemComp {@Paramcart: CartGoodsModel = new CartGoodsModel({} as CartGoods)async updateCart(params: UpdateParams) {await cart.update(this.cart.skuId, params)if (params.selected !== undefined) {this.cart.selected = params.selected}if (params.count) {this.cart.count = params.countcart.count()}}build() {Row({ space: 10 }) {MkCheckBox({checked: this.cart.selected, onClickBox: () => {this.updateCart({ selected: !this.cart.selected })}})Image(this.cart.picture).width(80).height(80)Column({ space: 8 }) {Text(this.cart.name).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontSize(14).fontColor($r('[basic].color.black')).width('100%')Row() {Text(this.cart.attrsText).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontSize(12).fontColor($r('[basic].color.text')).layoutWeight(1)Image($r('sys.media.ohos_ic_public_arrow_down')).fillColor($r('[basic].color.gray')).width(16).height(16)}.padding({ left: 6, right: 4 }).width(100).height(24).backgroundColor($r('[basic].color.under')).borderRadius(2)Row() {Text(`¥${this.cart.price}`).fontSize(14).fontWeight(500)Counter() {Text(this.cart.count.toString())}.enableDec(this.cart.count > 0).onInc(() => {this.updateCart({ count: this.cart.count + 1 })}).onDec(() => {this.updateCart({ count: this.cart.count - 1 })})}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.layoutWeight(1).alignItems(HorizontalAlign.Start)}.width('100%').height(100).padding(10).border({ width: { bottom: 0.5 }, color: '#e4e4e4' }).backgroundColor($r('[basic].color.white'))}
}

git记录
Cart-选中状态更新

1.11. 全选

接口文档

Array.prototype.every() - JavaScript | MDN

需求:

  1. 点击列表项目,同步更新全选状态
  2. 点击全选,批量设置每一项的选中状态

核心步骤:

  1. 计算全选状态并绑定(方法):
    1. 返回每一项是否被选中(selected==true)(every)
    2. 点击子组件(CheckBox)通过回调函数通知父组件(强制刷新页面)
  1. 抽取 api
  2. 点击全选:
    1. 获取本地选中状态
    2. 调用接口更新服务器
    3. 设置列表项的选中状态
import { auth } from '../../../../Index'
import { RequestAxios } from '../utils/Request'
import { CartGoods } from '../viewmodel'interface UpdateParams {selected?: booleancount?: number
}interface AddParams {skuId: stringcount: number
}interface CountResult {count: number
}interface CheckAllParams {selected: boolean
}export const CartKey: string = 'cartCount'export class Cart {// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}// 获取个数async count() {let count = 0if (auth.getUser().token) {const res = await RequestAxios.get<CountResult>('/member/cart/count')count = res.data.result.count}AppStorage.setOrCreate(CartKey, count)}// 获取列表list() {return RequestAxios.get<CartGoods[]>('/member/cart')}// 更新商品update(skuid: string, data: UpdateParams) {return RequestAxios.put<CartGoods>(`/member/cart/${skuid}`, data)}// 全选checkAll(data: CheckAllParams) {return RequestAxios.put<null>('/member/cart/selected', data)}
}export const cart = new Cart()

// 全选
MkCheckBox({checked: this.cartList.every(v => v.selected),onClickBox: async () => {const isCheckedAll = this.cartList.every(v => v.selected)await cart.checkAll({ selected: !isCheckedAll })this.cartList.forEach(v => v.selected = !isCheckedAll)}})

git 记录

Cart-全选

1.12. 总价格

链接

需求:

  1. 根据选中状态,个数,单价计算总价格

核心步骤:

  1. 实现方法:
    1. 筛选-》累加-》小数点 2 位-》转字符串
  1. 组件中使用该方法
// 定义计算总价的函数
/*** 总价格* */totalCount() {return this.cartList.filter(v => v.selected).reduce((acc, cur) => acc + cur.price * cur.count, 0).toFixed().toString()}Text('合计:').fontSize(14).fontColor($r('[basic].color.black')).margin({ right: 2 })
Text(this.totalCount()).fontSize(16).fontWeight(500).fontColor($r('[basic].color.red')).layoutWeight(1)

git 记录

Cart-总价格

1.13. 删除

接口文档

需求:

  1. 点击删除,删除购物车数据

核心步骤:

  1. 抽取接口(坑,delete 请求方法第二个参数和之前的不同
  2. 点击删除调用接口,删除服务器数据
  3. 更新个数
  4. 删除本地
  5. 伴随 loading 效果
export class RequestAxios {// get -> params -> { params: {} }// T 响应的内容 的类型!!static get<T>(url: string, params?: object): Promise<T> {return axiosInstance.get<null, T>(url, { params })}static getPlus<T>(url: string, config?: AxiosRequestConfig): Promise<T> {return axiosInstance.get<null, T>(url, config)}// post -> data -> { data: {} }static post<T>(url: string, data?: object): Promise<T> {return axiosInstance.post<null, T>(url, data)}static delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {return axiosInstance.delete<null, T>(url, config)}static put<T>(url: string, data?: object): Promise<T> {return axiosInstance.put<null, T>(url, data)}
}
import { Log } from '@abner/log'
import { auth, Logger } from '../../../../Index'
import { RequestAxios } from '../utils/Request'
import { CartGoods } from '../viewmodel'interface UpdateParams {selected?: booleancount?: number
}interface AddParams {skuId: stringcount: number
}interface CountResult {count: number
}interface CheckAllParams {selected: boolean
}// 删除的参数
interface DeleteParams {ids: string[]
}export const CartKey: string = 'cartCount'export class Cart {// 添加add(data: AddParams) {return RequestAxios.post<null>('/member/cart', data)}// 获取个数async count() {let count = 0if (auth.getUser().token) {const res = await RequestAxios.get<CountResult>('/member/cart/count')count = res.data.result.count}AppStorage.setOrCreate(CartKey, count)}// 获取列表async list() {return RequestAxios.get<CartGoods[]>('/member/cart')}// 更新商品async update(skuid: string, data: UpdateParams) {return RequestAxios.put<CartGoods>(`/member/cart/${skuid}`, data)}// 全选async checkAll(data: CheckAllParams) {return RequestAxios.put<null>('/member/cart/selected', data)}// 删除async remove(data: DeleteParams) {return RequestAxios.delete<null>('/member/cart', { data: data })}
}export const cart = new Cart()
// 其他略
ForEach(this.cartList,(item: CartGoodsModel, index: number) => {ListItem() {CartItemComp({cart: item})}.backgroundColor($r('[basic].color.under')).padding({ left: 8, right: 8 }).transition({ type: TransitionType.Delete, opacity: 0 }).swipeAction({end: this.DeleteBuilder(async () => {AlertDialog.show({title: '温馨提示',message: '确定删除该商品吗?',buttons: [{value: '取消', fontColor: $r('[basic].color.gray'), action: () => {}},{value: '确定', fontColor: $r('[basic].color.black'), action: async () => {// 服务器端删除,根据 skuId 删除商品await cart.remove({ ids: [item.skuId] })// 本地删除this.cartList.splice(index, 1)// 更新购物袋数量await cart.count()}},]})})})})

git 记录

Cart-删除购物袋商品

2. bug-ForEach 的keyGenerator参数

ForEach 键值规则

ForEach的第三个参数如果不给,那么默认值

bug 复现

  1. 保证购物车有数据,测试交互效果
  2. 切换到其他tab,再切换购物车,测试交互效果
  3. 修改选中状态,修改个数,无法触发【全选】 和 【价格】的重新计算
import { promptAction } from '@kit.ArkUI'
import { JSON } from '@kit.ArkTS'@ObservedV2
class Dog {id: number@Tracename: string@Tracechecked: booleanconstructor(id: number, name: string) {this.id = idthis.name = namethis.checked = false}
}@Entry
@Component
struct Parent {@StatesimpleList: Array<Dog> = [new Dog(1, '1狗'),new Dog(2, '2狗'),new Dog(3, '3狗'),];isCheckedAll() {console.log('计算是否全部选中')return this.simpleList.every(v => v.checked)}build() {Row() {Column() {Button('打印数据').margin(10).onClick(() => {promptAction.showToast({message: JSON.stringify(this.simpleList)})})Text('点击替换成相同的数据').fontSize(24).fontColor(Color.Red).onClick(() => {this.simpleList = this.simpleList.map(v => {return new Dog(v.id, v.name)})})Text(this.isCheckedAll() ? '全部选中' : '未全选').fontSize(24).fontColor(Color.Blue).margin(10)ForEach(this.simpleList, (item: Dog, index: number) => {ChildItem({ item: item }).margin({ top: 20 })}, (item: Dog, index: number) => {return JSON.stringify(item) + ' ' + index})}.justifyContent(FlexAlign.Center).width('100%').height('100%')}.height('100%').backgroundColor(0xF1F3F5)}
}@ComponentV2
struct ChildItem {@Param item: Dog = new Dog(0, '')aboutToAppear(): void {console.log('aboutToAppear' + this.item.id)}build() {Row() {Text(this.item.checked ? '√' : 'x').fontSize(20).margin({ right: 10 }).onClick(() => {this.item.checked = !this.item.checked})Text(this.item.id + '|' + this.item.name).fontSize(20).onClick(() => {this.item.name += '1'})}}
}

结论:

  1. 默认的键值生成逻辑,在使用相同数据覆盖的时候不会触发子组件的重新生成
  2. 子组件交互时修改的依旧是上一份数据,但是数据源已经更改所以无法触发 UI 更新

解决方案:

  1. 自定义 keyGenerator 函数,希望子组件全部重新创建时,使生成结果和上一次不同
  2. 可以使用时间戳,随机数,累加的数。。核心就是保证 循环的 key 发生改变

优化代码

import { promptAction } from '@kit.ArkUI'
import { JSON } from '@kit.ArkTS'
import { data } from '@kit.TelephonyKit'@ObservedV2
class Dog {id: number@Tracename: string@Tracechecked: booleanconstructor(id: number, name: string) {this.id = idthis.name = namethis.checked = false}
}@Entry
@Component
struct Parent {@StatesimpleList: Array<Dog> = [new Dog(1, '1狗'),new Dog(2, '2狗'),new Dog(3, '3狗'),];num: number = 0isCheckedAll() {console.log('计算是否全部选中')return this.simpleList.every(v => v.checked)}build() {Row() {Column() {Button('打印数据').margin(10).onClick(() => {promptAction.showToast({message: JSON.stringify(this.simpleList)})})Text('点击替换成相同的数据').fontSize(24).fontColor(Color.Red).onClick(() => {this.num = Date.now()// 基于原始数据 生成新数据this.simpleList = this.simpleList.map(v => {return new Dog(v.id, v.name)})})Text(this.isCheckedAll() ? '全部选中' : '未全选').fontSize(24).fontColor(Color.Blue).margin(10)ForEach(this.simpleList, (item: Dog, index: number) => {ChildItem({ item: item }).margin({ top: 20 })}, (item: Dog, index: number) => {console.log('ForEach' + item.id)return JSON.stringify(item)+this.num})}.justifyContent(FlexAlign.Center).width('100%').height('100%')}.height('100%').backgroundColor(0xF1F3F5)}
}@ComponentV2
struct ChildItem {@Param item: Dog = new Dog(0, '')aboutToAppear(): void {console.log('aboutToAppear' + this.item.id)}build() {Row() {Text(this.item.checked ? '√' : 'x').fontSize(20).margin({ right: 10 }).onClick(() => {this.item.checked = !this.item.checked})Text(this.item.id + '|' + this.item.name).fontSize(20).onClick(() => {this.item.name += '1'})}}
}
// 额外添加到随机数中
refreshNum: number = 0/*** 获取数据* */
async getData() {if (!auth.getUser().token) {return}const res = await cart.list()this.cartList = res.map((item) => new CartGoodsModel(item))this.refreshNum = Date.now()
}ForEach(this.cartList,(item: CartGoodsModel, index: number) => {// 略},(item: CartGoodsModel, index: number) => {return JSON.stringify(item) + this.refreshNum})

3. 自定义弹窗优化

3.1. 弹窗错误优化

错误信息
目前使用的弹窗会有错误提示,错误原因是:

  • 103302: 内容节点对应自定义弹窗已存在
  • 103303:无法找到内容节点对应的自定义弹窗

咱们来优化一下弹框的逻辑

  1. 多次请求只有第一次弹框
  2. 多次请求最后一次结束的时候关闭弹框

// 其他略let count =0// 添加请求拦截器
axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {// 如果用户信息中有 token 就在请求头中携带 tokenconst user = auth.getUser()if (user.token) {config.headers.Authorization = `Bearer ${user.token}`}// 对请求数据做点什么if (count === 0) {PromptActionClass.openDialog()}count++return config;
}, (error: AxiosError) => {// 对请求错误做些什么return Promise.reject(error);
});interface ErrorType {message: stringmsg: stringcode: string
}// 添加响应拦截器
axiosInstance.interceptors.response.use((response: AxiosResponse) => {// 对响应数据做点什么count--if (count <= 0) {PromptActionClass.closeDialog()}return response.data.result
}, (error: AxiosError<ErrorType>) => {// 只要 http 状态码 不在 200-299 以内 就会进到这个异常// 400 参数错误 (用户名密码登录 输入错误密码)// Logger.info(error.response?.status)if (error.response?.status === 400) {// Logger.info( error.response.data as object)promptAction.showToast({message: error.response.data.message})} else if (error.response?.status === 401) {// 删除 tokenauth.removeUser()// 提示用户promptAction.showToast({message: error.response.data.message})// 去登陆pathStack.pushPathByName('LoginView', null)}count--if (count <= 0) {PromptActionClass.closeDialog()}// 401 登录过期 (给错误的 token)// 对响应错误做点什么return Promise.reject(error);
});

git 记录

fix-优化全局弹框错误提示

http://www.dtcms.com/wzjs/555503.html

相关文章:

  • 硅云网站建设视频上海html5网站制作公司
  • 网页制作与网站建设文档天津建设工程信息网公布
  • 做网站有哪些费用广州高端网站定制开发价格
  • 怎么搞免费的网站招商网站建设大概多少钱
  • 网页制作基础教程第二版葛艳玲嘉定网站设计制作优化排名
  • 手机优化大师为什么扣钱深圳企业股权优化
  • 供需网站开发温州网站建设对比
  • 山东网站推广公司线上推广专员岗位职责
  • 多人视频网站开发公司阳江今天刚刚发生的重大新闻
  • gta5房产网站在建设做网站为什么需要花钱
  • 做淘宝类网站的步骤杭州做百度推广的公司
  • 千网图片网站优化排名公司哪家好
  • 企业网站建设相关书籍在线阅读备案期间需要关闭网站
  • 广州微信网站建设公司哪家好象山县建设管理局网站
  • 企业设计个网站做网站中的镜像是什么
  • 网站建设济宁苏州保洁公司招聘保洁区域经理
  • 购物网站建设策划做网站 插件
  • 网站开发工程师面试试题wordpress安装后台
  • 网站是用什么程序做的wordpress 点击文章图片
  • 个人建站 wordpress精品一卡2卡三卡4卡分类
  • 站长网东莞物流网站设计公司
  • 上市公司专利查询网站十堰网络科技有限公司
  • 长沙公司建设网站温州网站建设制作
  • 分销网站方案wordpress 多个页面
  • 电子商务网站建设课程的心得百度地图实时街景怎么移动
  • wordpress默认设置页面烟台网站排名优化报价
  • c#网站开发 pdfoa协同办公系统平台
  • 推荐一下做年会视频的网站企业网站是企业在互联网上进行网络营销
  • 动画形式的h5在哪个网站做英语网站online
  • 忘记网站后台密码建设厅官方网站新资质标准