HarmonyOS鸿蒙Next中购物车页面真机调试,动态删除页面元素后,底部导航栏组件无法点击了

HarmonyOS鸿蒙Next中购物车页面真机调试,动态删除页面元素后,底部导航栏组件无法点击了 按照京东购物车样式写的静态页面。

删除购物车所有商品后,底部导航栏组件无法点击,并且发现日志报错

E 03900/Ace: [<private> (<private>)] the node is not in the map
E 03900/Ace: [<private> (<private>)] Node[1634] can’t be reached.

// cart.js
import prompt from '@system.prompt';
import { setSystemBarProperties } from '../../utils/index';
import cartList from "../../utils/cartList"

export default {
  data: {
    isEdit: false,
    dataLoading: false,
    cartList: [],
    checkedAll: false,
    dialogType: 1, // 1:收藏;2:删除
    dialogMessage: ""
  },
  computed: {
    totalCount: {
      set() { return 0 },
      get() { 
        return this.cartList.reduce((pre, cur) => { 
          return pre + cur.sorted.length 
        }, 0)
      }
    },
    totalPrice: {
      set() { return "0.00" },
      get() { 
        if (!this.cartList.length) { 
          return "0.00" 
        }
        const checkedGoodsList = this.cartList.reduce((pre, cur) => { 
          const checkedList = cur.sorted.filter(item => item.checked)
          return pre.concat(checkedList)
        }, [])
        const totalPrice = checkedGoodsList.reduce((pre, cur) => { 
          return pre + ((cur.Price * cur.Num) - cur.Discount)
        }, 0)
        return totalPrice.toFixed(2)
      }
    },
    discountPrice: {
      set() { return "0.00" },
      get() { 
        if (!this.cartList.length) { 
          return "0.00" 
        }
        const checkedGoodsList = this.cartList.reduce((pre, cur) => { 
          const checkedList = cur.sorted.filter(item => item.checked)
          return pre.concat(checkedList)
        }, [])
        const discountPrice = checkedGoodsList.reduce((pre, cur) => { 
          return pre + cur.Discount
        }, 0)
        return discountPrice.toFixed(2)
      }
    },
    buyTotal: {
      set() { return 0 },
      get() { 
        if (!this.cartList.length) { 
          return 0 
        }
        const checkedGoodsList = this.cartList.reduce((pre, cur) => { 
          const checkedList = cur.sorted.filter(item => item.checked)
          return pre.concat(checkedList)
        }, [])
        let buyTotal = 0
        buyTotal = checkedGoodsList.reduce((pre, cur) => { 
          return pre + cur.Num
        }, 0)
        return buyTotal 
      }
    }
  },
  onInit() {
    console.log("cartOnInit");
    this.initCartList()
  },
  onShow() {
    this.initBarStatus()
    this.initCheckedAll()
  },
  handleChangeEdit() {
    this.isEdit = !this.isEdit
  },
  initCartList() {
    if (this.cartList.length === 0) {
      this.dataLoading = true
      setTimeout(() => {
        cartList.forEach(item => {
          item.checked = false
          item.sorted.forEach(sItem => {
            sItem.checked = false
          })
        })
        this.cartList = cartList
        this.initCheckedAll()
        this.dataLoading = false
      }, 1000)
    }
  },
  initCheckedAll() {
    if (this.cartList.length) {
      const shopCheckedAll = this.cartList.every(c => c.checked)
      this.checkedAll = shopCheckedAll
    } else {
      this.checkedAll = false
    }
  },
  initBarStatus() {
    setSystemBarProperties({
      statusBarColor: "#f6f6f6"
    })
  },
  handleShopSelect(item) {
    item.checked = !item.checked
    item.sorted.forEach(s => {
      s.checked = item.checked
    })
    if (item.checked) {
      const shopCheckedAll = this.cartList.every(c => c.checked)
      this.checkedAll = shopCheckedAll
    } else {
      this.checkedAll = false
    }
  },
  handleGoodsSelect(value, shop) {
    value.checked = !value.checked
    const goodsCheckedAll = shop.sorted.every(s => s.checked)
    if (goodsCheckedAll) {
      shop.checked = true
      const shopCheckedAll = this.cartList.every(c => c.checked)
      this.checkedAll = shopCheckedAll
    } else {
      shop.checked = false
      this.checkedAll = false
    }
  },
  handleCheckedAll() {
    this.checkedAll = !this.checkedAll
    this.cartList.forEach(item => {
      item.checked = this.checkedAll
      item.sorted.forEach(s => {
        s.checked = this.checkedAll
      })
    })
  },
  handleGoodsReduce(value) {
    if (value.Num > 1) {
      value.Num--
    }
  },
  handleGoodsIncrease(value) {
    value.Num++
  },
  toOrder() {
    if (!this.buyTotal) {
      prompt.showToast({
        message: '您还没有选择商品哦',
        duration: 1500,
      })
      return false
    }
  },
  getCheckedCount() {
    return this.cartList.reduce((pre, cur) => {
      return pre + cur.sorted.filter(s => s.checked).length
    }, 0)
  },
  handleCollect() {
    const checkedCount = this.getCheckedCount()
    if (!checkedCount) {
      prompt.showToast({
        message: '您还没有选择商品哦',
        duration: 1500,
      })
      return false
    }
    this.dialogType = 1
    this.dialogMessage = `确定要将这${checkedCount}种商品移入我的收藏?`
    this.$element('confirm-dialog').show()
  },
  handleDelete() {
    const checkedCount = this.getCheckedCount()
    if (!checkedCount) {
      prompt.showToast({
        message: '您还没有选择商品哦',
        duration: 1500,
      })
      return false
    }
    this.dialogType = 2
    this.dialogMessage = `确认要删除这${checkedCount}种商品吗?`
    this.$element('confirm-dialog').show()
  },
  handleDilaogConfirm() {
    try {
      const checkedCount = this.getCheckedCount()
      if (checkedCount === this.totalCount) {
        this.cartList = []
        this.isEdit = false
        return false
      }
      const cartList = []
      this.cartList.forEach(item => {
        if (!item.checked) {
          item.sorted = item.sorted.filter(s => !s.checked)
          cartList.push(item)
        }
      })
      this.cartList = cartList
      this.handleDialogCancel()
    } catch (e) {
      console.log(e);
    }
  },
  handleDialogCancel() {
    this.$element('confirm-dialog').close()
  },
  selectService(goods) {
    console.log(JSON.stringify(goods));
    this.showPanel()
  },
  showPanel() {
    this.$element('simplepanel').show()
  },
  closePanel() {
    this.$element('simplepanel').close()
  },
  handleClick(message) {
    console.log(message);
  }
}
// cart.hml
<element name="tabbar" src="../../components/tabbar/tabbar.hml"></element>
<div class="page" style="padding-top: {{ cartList.length > 0 ? '86px' : '40px' }};  padding-bottom: {{ cartList.length > 0 ? '106px' : '56px'}};">
  <div class="container">
    <div class="header">
      <div class="headedr-title">
        <text class="header-title-text">购物车</text>
      </div>
      <div class="header-address">
        <image class="address-icon" src="../../common/images/icon_location.png"></image>
        <text class="header-address-text">配送至:金水路与燕黑路交叉口西北角兴业大厦10楼1010室</text>
      </div>
      <div if="{{ cartList.length > 0 }}" class="header-edit-content" onclick="handleChangeEdit">
        <text if="{{ ! isEdit }}" class="header-edit-text">编辑</text>
        <text if="{{ isEdit }}" class="header-edit-text">完成</text>
      </div>
    </div>
    <div if="{{ cartList.length > 0 }}" class="count-statistc">
      <text class="count-total">全部 {{ totalCount }}</text>
    </div>
    <div if="{{ cartList.length > 0 && ! dataLoading }}" class="cart-conent">
      <div for="{{ cartList }}" class="cart-module">
        <div class="module-shop-name">
          <div class="checkbox-content">
            <div class="checkbox-image-content" onclick="handleShopSelect($item)">
              <image if="{{ $item.checked }}" class="checkbox-image-item" src="../../common/images/icon_selected.png"></image>
              <image if="{{ !$item.checked }}" class="checkbox-image-item" src="../../common/images/icon_unselected.png"></image>
            </div>
          </div>
          <text class="shop-name">{{ $item.shopName }}</text>
          <image src="../../common/images/icon_arrow.png" class="jump-icon"></image>
        </div>
        <div class="goods-list">
          <div for="{{ value in $item.sorted }}" class="goods-item">
            <div class="checkbox-content">
              <div class="checkbox-image-content" onclick="handleGoodsSelect(value, $item)">
                <image if="{{ value.checked }}" class="checkbox-image-item" src="../../common/images/icon_selected.png"></image>
                <image if="{{ !value.checked }}" class="checkbox-image-item" src="../../common/images/icon_unselected.png"></image>
              </div>
            </div>
            <div class="goods-content">
              <div class="goods-image-content">
                <image class="goods-image" src="https://img10.360buyimg.com/mobilecms/{{ value.ImageUrlRN }}"></image>
              </div>
              <div class="goods-info-content">
                <div class="goods-name">
                  <text class="goods-name-text">{{ value.Name }}</text>
                </div>
                <div class="goods-specification" onclick="selectService(value)">
                  <text class="goods-specification-text goods-specification-text-ellipse">{{ value.unitedText }}</text>
                  <text class="goods-specification-text goods-specification-text-service">,选服务</text>
                </div>
                <div class="goods-show-price">
                  <text class="goods-show-price-text">¥{{ value.Price }}</text>
                  <div class="goods-num-content">
                    <text class="num-option num-reduce {{ value.Num === 1 ? 'num-option-disabled' : '' }}" onclick="handleGoodsReduce(value)">-</text>
                    <text class="goods-num-text">{{ value.Num }}</text>
                    <text class="num-option num-increase" onclick="handleGoodsIncrease(value)">+</text>
                  </div>
                </div>
                <div class="goods-estimate-price">
                  <text class="goods-estimate-price-text">预估到手价 ¥{{ ((value.Price * value.Num - value.Discount) / value.Num).toFixed(2) }}</text>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div show="{{ dataLoading }}" class="data-loading">
      <image class="loading-gif" src="../../common/images/loaddata.gif"></image>
    </div>
    <div if="{{ cartList.length === 0 && ! dataLoading }}" class="cart-null">
      <image class="cart-null-image" src="../../common/images/5a93c51cN3bb5e37b.png"></image>
      <text class="cart-null-message">购物车空空如也,去逛逛吧</text>
    </div>
    <div if="{{ cartList.length > 0 }}" class="price-statistics">
      <div class="price-statistics-left">
        <div class="checked-all-content">
          <div class="checkbox-content">
            <div class="checkbox-image-content" onclick="handleCheckedAll">
              <image if="{{ checkedAll }}" class="checkbox-image-item" src="../../common/images/icon_selected.png"></image>
              <image if="{{ !checkedAll }}" class="checkbox-image-item" src="../../common/images/icon_unselected.png"></image>
            </div>
          </div>
          <text class="checked-all-text">全选</text>
        </div>
        <div show="{{ ! isEdit }}" class="price-content">
          <text class="total-price-text">合计:¥{{ totalPrice }}</text>
          <text class="discount-amount">优惠减:¥{{ discountPrice }}</text>
        </div>
      </div>
      <button if="{{ ! isEdit }}" type="capsule" class="buy-button" onclick="toOrder">去结算({{ buyTotal }})</button>
      <div if="{{ isEdit }}" class="edit-button-content">
        <button class="edit-button-content-item" onclick="handleCollect">移入收藏</button>
        <button class="edit-button-content-item" onclick="handleDelete">删除</button>
      </div>
    </div>
    <tabbar></tabbar>
    <dialog id="confirm-dialog">
      <div class="dialog-container">
        <div class="dialog-message">
          <text class="dialog-message-text">{{ dialogMessage }}</text>
        </div>
        <div class="dialog-button-content">
          <button if="{{ dialogType === 1 }}" class="button-item cancel-button" onclick="handleDialogCancel">我再想想</button>
          <button if="{{ dialogType === 1 }}" class="button-item confirm-button" onclick="handleDilaogConfirm">移入收藏</button>
          <button if="{{ dialogType === 2 }}" class="button-item cancel-button" onclick="handleDialogCancel">取消</button>
          <button if="{{ dialogType === 2 }}" class="button-item confirm-button" onclick="handleDilaogConfirm">确定</button>
        </div>
      </div>
    </dialog>
    <panel id="simplepanel" type="foldable" mode="half" dragbar="false" miniheight="200px">
      <div class="panel-div">
        <div class="inner-txt">
          <text class="txt">服务选择</text>
        </div>
        <div class="inner-btn">
          <button type="capsule" value="Close" onclick="closePanel"></button>
        </div>
      </div>
    </panel>
  </div>
</div>
// cart.css
.page {
  width: 100%;
  height: 100%;
  display: flex;
}
.container {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 100%;
  background-color: #f6f6f6;
  overflow: scroll;
}
.data-loading {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.loading-gif {
  width: 100px;
  height: 100px;
}
.header {
  position: fixed;
  left: 0px;
  top: 0px;
  right: 0px;
  padding: 0px 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 40px;
  background-color: #f6f6f6;
}
.headedr-title {
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 60px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
.headedr-title .header-title-text {
  font-size: 18px;
  line-height: 18px;
  color: #000;
  text-align: left;
}
.header-address {
  flex-shrink: 1;
  flex-grow: 1;
  padding: 0px 8px;
  display: flex;
  align-items: center;
  width: 100%;
  height: 18px;
  background-color: #f0f0f0;
  border-radius: 9px;
}
.address-icon {
  width: 12px;
  height: 12px;
}
.header-address-text {
  flex: 1;
  flex-grow: 1;
  padding-left: 5px;
  font-size: 12px;
  line-height: 12px;
  color: #999;
  max-lines: 1;
  text-overflow: ellipsis;
}
.header-edit-content {
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 40px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.header-edit-content .header-edit-text {
  font-size: 14px;
  line-height: 14px;
  color: #000;
}
.count-statistc {
  position: fixed;
  left: 0px;
  top: 40px;
  right: 0px;
  padding: 0px 15px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  width: 100%;
  height: 46px;
  background-color: #f6f6f6;
}
.count-total {
  font-size: 16px;
  color: #a2353a;
  font-weight: bolder;
}
.cart-conent {
  padding: 0px 15px;
  display: flex;
  flex-direction: column;
}
.cart-module {
  margin-bottom: 10px;
  padding: 0px 10px 20px;
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
  border-radius: 15px;
  width: 100%;
}
.module-shop-name {
  padding: 15px 0px 0px;
  display: flex;
  align-items: center;
  width: 100%;
}
.checkbox-content {
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 26px;
  width: 26px;
  display: flex;
  align-items: center;
}
.checkbox-image-content {
  width: 26px;
  height: 26px;
}
.checkbox-image-item {
  width: 26px;
  height: 26px;
}
.shop-name {
  padding-right: 5px;
  font-size: 14px;
  line-height: 14px;
  font-weight: bolder;
  color: #000;
  max-lines: 1;
  text-overflow: ellipsis;
}
.jump-icon {
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 8px;
  width: 8px;
  height: 8px;
}
.goods-list {
  display: flex;
  flex-direction: column;
  width: 100%;
}
.goods-item {
  position: relative;
  padding: 10px 0px;
  display: flex;
  align-items: flex-start;
}
.goods-item .checkbox-content {
  position: relative;
  top: 31px;
}
.goods-content {
  flex-shrink: 1;
  flex-grow: 1;
  display: flex;
}
.goods-image-content {
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 88px;
  width: 88px;
  height: 88px;
  border-radius: 8px;
  background-color: #f9f9f9;
}
.goods-image {
  width: 88px;
  height: 88px;
  border-radius: 8px;
  background-color: #f9f9f9;
}
.goods-info-content {
  flex-grow: 1;
  flex-shrink: 1;
  display: flex;
  flex-direction: column;
  padding-left: 12px;
}
.goods-name-text {
  font-size: 14px;
  line-height: 18px;
  color: #000;
  max-lines: 2;
  text-overflow: ellipsis;
  word-break: break-all;
}
.goods-specification {
  margin-top: 5px;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  border-radius: 11px;
}
.goods-specification-text {
  padding: 5px 11px;
  height: 22px;
  font-size: 12px;
  line-height: 12px;
  color: #575757;
  background-color: #f1f1f1;
}
.goods-specification-text-ellipse {
  padding-right: 0px;
  max-lines: 1;
  text-overflow: ellipsis;
  border-top-left-radius: 11px;
  border-bottom-left-radius: 11px;
  word-break: break-all;
}
.goods-specification-text-service {
  padding-left: 0px;
  flex-shrink: 0;
  flex-grow: 0;
  border-top-right-radius: 11px;
  border-bottom-right-radius: 11px;
}
.goods-show-price {
  margin-top: 6px;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.goods-show-price-text {
  font-size: 14px;
  color: #000;
  font-weight: bolder;
}
.goods-num-content {
  display: flex;
  border-radius: 13px;
  border: 1px solid #d4d4d4;
}
.num-option {
  width: 24px;
  height: 24px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  color: #000;
  text-align: center;
}
.num-option-disabled {
  color: #d4d4d4;
}
.goods-num-text {
  width: 26px;
  height: 24px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  color: #000;
  text-align: center;
  border-left: 1px solid #d4d4d4;
  border-right: 1px solid #d4d4d4;
}
.goods-estimate-price {
  margin-top: 6px;
}
.goods-estimate-price-text {
  font-size: 14px;
  color: #ba130a;
}
.price-statistics {
  position: fixed;
  left: 0px;
  bottom: 56px;
  right: 0px;
  padding: 0px 10px;
  width: 100%;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: #ffffff;
  border-top: 1px solid #dddddd;
}
.price-statistics-left {
  display: flex;
  align-items: center;
}
.checked-all-content {
  display: flex;
  align-items: center;
}
.checked-all-text {
  font-size: 12px;
  color: #000;
}
.price-content {
  padding-left: 8px;
  display: flex;
  flex-direction: column;
}
.total-price-text {
  font-size: 14px;
  color: #000;
}
.discount-amount {
  margin-top: 5px;
  font-size: 10px;
  color: #727272;
}
.buy-button {
  display: flex;
  align-items: center;
  padding: 0px 18px;
  height: 36px;
  font-size: 14px;
  text-color: #fff;
  background-color: #fc3d45;
}
.edit-button-content {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.edit-button-content-item {
  display: flex;
  align-items: center;
  margin-left: 14px;
  padding: 0px 17px;
  height: 34px;
  font-size: 14px;
  text-color: #000000;
  border-radius: 17px;
  border: 1px solid #919191;
  background-color: #ffffff;
}
#confirm-dialog .dialog-container {
  display: flex;
  flex-direction: column;
  padding: 40px 20px 15px;
  width: 295px;
}
#confirm-dialog .dialog-message {
  width: 100%;
}
#confirm-dialog .dialog-message .dialog-message-text {
  width: 100%;
  font-size: 16px;
  color: #000;
  text-align: center;
}
#confirm-dialog .dialog-button-content {
  margin-top: 20px;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
#confirm-dialog .dialog-button-content .button-item {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 116px;
  height: 28px;
  font-size: 14px;
  line-height: 14px;
  border-radius: 14px;
}
#confirm-dialog .dialog-button-content .cancel-button {
  background-color: #ffffff;
  text-color: #fe3c45;
  border: 1px solid #fe3c45;
}
#confirm-dialog .dialog-button-content .confirm-button {
  background-color: #fe3c45;
  text-color: #fff;
  border: 1px solid #fe3c45;
}
.cart-null {
  padding: 70px 0px 65px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  background-color: #ffffff;
}
.cart-null-image {
  width: 90px;
  height: 90px;
}
.cart-null-message {
  margin-top: 15px;
  font-size: 16px;
  line-height: 16px;
  color: #333;
}
// tabbar.hml
<element name="tabbar" src="../../components/tabbar/tabbar.hml"></element>
<toolbar class="toolbarContain" style="position: fixed; bottom: 0px;background-color: #ffffff;">
  <toolbar-item
    for="{{tabbarList}}"
    icon="{{$idx === active ? $item.activeIcon : $item.inactiveIcon}}"
    value='{{$item.text}}'
    style="color: {{ $idx === active ? '#f2190c' : '#8b8b8b'}}"
    onclick="itemClick($item)"
  ></toolbar-item>
</toolbar>
// tabbar.js
import maRouter from "../../maRouter"
export default {
  data: {
    active: -1,
    tabbarList: [
      {
        inactiveIcon: "common/images/tabbar/home-normal.png",
        activeIcon: "common/images/tabbar/home-active.png",
        text: "首页",
        path: "pages/index/index"
      },
      {
        inactiveIcon: "common/images/tabbar/category-normal.png",
        activeIcon: "common/images/tabbar/category-active.png",
        text: "分类",
        path: "pages/category/category"
      },
      {
        inactiveIcon: "common/images/tabbar/cart-normal.png",
        activeIcon: "common/images/tabbar/cart-active.png",
        text: "购物车",
        path: "pages/cart/cart"
      },
      {
        inactiveIcon: "common/images/tabbar/my-normal.png",
        activeIcon: "common/images/tabbar/my-active.png",
        text: "我的",
        path: "pages/userCenter/userCenter"
      }
    ]
  },
  onPageShow() {
    const routerState = maRouter.getState()
    const index = this.tabbarList.findIndex(item => item.path === (routerState.path + routerState.name))
    if (index !== -1) {
      this.active = index
    }
  },
  itemClick(item) {
    const routerState = maRouter.getState()
    const currentRoute = routerState.path + routerState.name
    if (currentRoute !== item.path) {
      maRouter.push({ url: item.path })
    }
  }
}

上面是一个页面的代码,下面有整个项目的代码GitHub地址。

操作步骤就是真机打开项目后,在购物车页面右上角编辑,全选购物车商品后,右下角删除,确认后,底部导航栏无法点击。只有点击手机物理返回键才有效果。

项目GitHub地址


更多关于HarmonyOS鸿蒙Next中购物车页面真机调试,动态删除页面元素后,底部导航栏组件无法点击了的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中购物车页面真机调试,动态删除页面元素后,底部导航栏组件无法点击了的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,动态删除页面元素后,底部导航栏组件无法点击,可能是由于布局更新未正确触发或事件绑定丢失。建议检查以下几点:

  1. 布局更新:确保删除元素后,调用invalidate()requestLayout()方法强制更新布局。
  2. 事件绑定:确认底部导航栏的事件监听器未被意外移除,重新绑定事件处理函数。
  3. 焦点问题:检查是否有其他元素获取了焦点,导致导航栏无法响应点击事件。
  4. 调试日志:通过日志输出,确认删除操作是否影响了导航栏的可见性或交互状态。

如果问题依旧,建议逐步排查代码逻辑,确保删除操作不会影响其他组件的正常功能。

回到顶部