小程序-购物车-基于SKU电商规格组件实现

SKU 概念: 存货单位( Stock Keeping Unit ), 库存 管理的最小可用单元,通常称为“单品”。
SKU 常见于电商领域,对于前端工程师而言,更多关注 SKU 算法 ,基于后端的 SKU 数据 渲染页面 实现交互

SKU 模块 - 下载 SKU 插件

DCloud 插件市场  是 uni-app 官方插件生态集中地,有数千款插件

使用SKU插件:

组件安装到自己的项目

注意事项:项目进行 git 提交时会校验文件,可添加 /* eslint-disable */  禁用检查

<script>
/* eslint-disable */
// 省略组件源代码
</script>

打开购物车弹框,渲染商品信息   goods.vue

<!-- SKU 弹窗组件 -->

  <vk-data-goods-sku-popup v-model="isShowSku" :localdata="localdata" />

// 是否显示 SKU 组件

const isShowSku = ref(false)

// 商品信息

const localdata = ref({} as SkuPopupLocaldata)

//  渲染商品信息

// 获取商品详情信息

const goods = ref<GoodsResult>()

const getGoodsByIdData = async () => {

  const res = await getGoodsByIdAPI(query.id)

  goods.value = res.result

  // SKU 组件所需格式

  localdata.value = {

    _id: res.result.id,

    name: res.result.name,

    goods_thumb: res.result.mainPictures[0],

    spec_list: res.result.specs.map((v) => {

      return {

        name: v.name,

        list: v.values,

      }

    }),

    sku_list: res.result.skus.map((v) => {

      return {

        _id: v.id,

        goods_id: res.result.id,

        goods_name: res.result.name,

        image: v.picture,

        price: v.price * 100, // 注意:需要乘以 100

        stock: v.inventory,

        sku_name_arr: v.specs.map((vv) => vv.valueName),

      }

    }),

  }

}

打开sku 弹窗       渲染商品

打开SKU弹窗  =》 设置按钮模式    =》 微调组件样式

<!-- SKU 弹窗组件 -->

  <vk-data-goods-sku-popup

    v-model="isShowSku"

    :localdata="localdata"

    :mode="mode"

    add-cart-background-color="#ffa868"

    buy-now-background-color="#27ba98"

    :active-style="{

      color: '#27ba9b',

      borderColor: '#27ba9b',

      backgroundCColor: '#e9f8f5',

    }"

  />

// mode  设置按钮模式

// add-cart-background-color   设置即入购物车按钮背景色

// buy-now-background-color   设置立即购买按钮背景色

// :active-style  选择商品规格时的激活样式

// 按钮模式       枚举

enum SkuMode {

  Both = 1, // 购物车和立即购买都显示

  Cart = 2, // 只显示购物车

  Buy = 3, // 只显示立即购买

}

const mode = ref<SkuMode>(SkuMode.Both)

// 打开sku 弹窗 修改按钮模式

const openSkuPopup = (val: SkuMode) => {

  // 显示sku组件

  isShowSku.value = true

  // 修改按钮模式

  mode.value = val

}

<view class="item arrow" @tap="openSkuPopup(SkuMode.Both)">

          <text class="label">选择</text>

          <text class="text ellipsis"> 请选择商品规格 </text>

</view>

<view class="buttons">

      <view class="addcart" @tap="openSkuPopup(SkuMode.Cart)"> 加入购物车 </view>

      <view class="buynow" @tap="openSkuPopup(SkuMode.Buy)"> 立即购买 </view>

</view>

加入购物车事件        加入购物车在商品详情页面       goods.vue

<!-- SKU 弹窗组件 -->

  <vk-data-goods-sku-popup

    v-model="isShowSku"

    :localdata="localdata"

    :mode="mode"

    add-cart-background-color="#ffa868"

    buy-now-background-color="#27ba9b"

    ref="skuPopupRef"

    :actived-style="{

      color: '#27BA9B',

      borderColor: '#27BA9B',

      backgroundColor: '#E9F8F5',

    }"

    @add-cart="onAddCart"

  />

// 加入购物车事件

const onAddCart = (e: SkuPopupEvent) => {

  console.log(e)

}

控制台打印数据

封装购物车接口:cart.ts

1、加入购物车接口封装

import { http } from "@/utils/http"

/**

 * 加入购物车

 * @param data 请求体参数

 * @returns

 */

export const postMemberCartAPI = (data: { skuId: string; count: number}) => {

  return http({

    method: 'POST',

    url: '/member/cart',

    data,

  })

}

完善商品详情页面的加入购物车功能

// 加入购物车事件

const onAddCart = async (e: SkuPopupEvent) => {

  console.log(e)

  await postMemberCartAPI({ skuId: e._id, count: e.buy_num })

  uni.showToast({ icon: 'success', title: '已加入购物车' })

  // 关闭弹窗

  isShowSku.value = false

}

完整的商品详情页面代码:goods.vue

<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref, computed } from 'vue'
import { getGoodsByIdAPI } from '@/services/goods'
import { postMemberCartAPI } from '@/services/cart'
import type { GoodsResult } from '@/types/goods'
import AddressPanel from './components/AddressPanel.vue'
import ServicePanel from './components/ServicePanel.vue'
import PageSkeleton from './components/PageSkeleton.vue'
import type {
  SkuPopupEvent,
  SkuPopupInstanceType,
  SkuPopupLocaldata,
} from '@/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup'

// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()

// 接收页面参数
const query = defineProps<{
  id: string
}>()

// 获取商品详情信息
const goods = ref<GoodsResult>()
const getGoodsByIdData = async () => {
  const res = await getGoodsByIdAPI(query.id)
  goods.value = res.result
  // SKU 组件所需格式
  localdata.value = {
    _id: res.result.id,
    name: res.result.name,
    goods_thumb: res.result.mainPictures[0],
    spec_list: res.result.specs.map((v) => {
      return {
        name: v.name,
        list: v.values,
      }
    }),
    sku_list: res.result.skus.map((v) => {
      return {
        _id: v.id,
        goods_id: res.result.id,
        goods_name: res.result.name,
        image: v.picture,
        price: v.price * 100, // 注意:需要乘以 100
        stock: v.inventory,
        sku_name_arr: v.specs.map((vv) => vv.valueName),
      }
    }),
  }
}

// 是否数据加载完成
const isFinish = ref(false)

// 页面加载
onLoad(async () => {
  await getGoodsByIdData()
  isFinish.value = true
})

// 轮播图变化时
const currentIndex = ref(0)
const onChange: UniHelper.SwiperOnChange = (e) => {
  currentIndex.value = e.detail!.current
}

// 点击图片时
const onTapImage = (url: string) => {
  // 大图预览
  uni.previewImage({
    current: url, // 当前显示图片的链接
    urls: goods.value!.mainPictures, // 需要预览的图片链接列表  数组
  })
}
// uni-ui  弹出层组件 ref
const popup = ref<{
  open: (type?: UniHelper.UniPopupType) => void
  close: (type?: UniHelper.UniPopupType) => void
}>()

// 弹出层渲染
const popupName = ref<'address' | 'service'>()
const openPopup = (name: typeof popupName.value) => {
  // 修改弹出层名称
  popupName.value = name
  popup.value?.open()
}

// 是否显示 SKU 组件
const isShowSku = ref(false)
// 商品信息
const localdata = ref({} as SkuPopupLocaldata)
// 按钮模式
enum SkuMode {
  Both = 1, // 购物车和立即购买都显示
  Cart = 2, // 只显示购物车
  Buy = 3, // 只显示立即购买
}
const mode = ref<SkuMode>(SkuMode.Both)
// 打开sku 弹窗 修改按钮模式
const openSkuPopup = (val: SkuMode) => {
  // 显示sku组件
  isShowSku.value = true
  // 修改按钮模式
  mode.value = val
}
// SKU组件实例
const skuPopupRef = ref<SkuPopupInstanceType>()

// 计算被选中的值
const selectArrText = computed(() => {
  return skuPopupRef.value?.selectArr?.join(' ').trim() || '请选择商品规格'
})
// 加入购物车事件
const onAddCart = async (e: SkuPopupEvent) => {
  console.log(e)
  await postMemberCartAPI({ skuId: e._id, count: e.buy_num })
  uni.showToast({ icon: 'success', title: '已加入购物车' })
  // 关闭弹窗
  isShowSku.value = false
}
</script>

<template>
  <!-- SKU 弹窗组件 -->
  <vk-data-goods-sku-popup
    v-model="isShowSku"
    :localdata="localdata"
    :mode="mode"
    add-cart-background-color="#ffa868"
    buy-now-background-color="#27ba9b"
    ref="skuPopupRef"
    :actived-style="{
      color: '#27BA9B',
      borderColor: '#27BA9B',
      backgroundColor: '#E9F8F5',
    }"
    @add-cart="onAddCart"
  />
  <scroll-view scroll-y class="viewport" v-if="isFinish">
    <!-- 基本信息 -->
    <view class="goods">
      <!-- 商品主图 -->
      <view class="preview">
        <swiper circular @change="onChange">
          <swiper-item v-for="item in goods?.mainPictures" :key="item">
            <image @tap="onTapImage(item)" mode="aspectFill" :src="item" />
          </swiper-item>
        </swiper>
        <view class="indicator">
          <text class="current">{{ currentIndex + 1 }}</text>
          <text class="split">/</text>
          <text class="total">{{ goods?.mainPictures.length }}</text>
        </view>
      </view>

      <!-- 商品简介 -->
      <view class="meta">
        <view class="price">
          <text class="symbol">¥</text>
          <text class="number">{{ goods?.price }}</text>
        </view>
        <view class="name ellipsis">{{ goods?.name }} </view>
        <view class="desc"> {{ goods?.desc }} </view>
      </view>

      <!-- 操作面板 -->
      <view class="action">
        <view class="item arrow" @tap="openSkuPopup(SkuMode.Both)">
          <text class="label">选择</text>
          <text class="text ellipsis"> {{ selectArrText }} </text>
        </view>
        <view class="item arrow" @tap="openPopup('address')">
          <text class="label">送至</text>
          <text class="text ellipsis"> 请选择收获地址 </text>
        </view>
        <view class="item arrow" @tap="openPopup('service')">
          <text class="label">服务</text>
          <text class="text ellipsis"> 无忧退 快速退款 免费包邮 </text>
        </view>
      </view>
    </view>

    <!-- 商品详情 -->
    <view class="detail panel">
      <view class="title">
        <text>详情</text>
      </view>
      <view class="content">
        <view class="properties">
          <!-- 属性详情 -->
          <view class="item" v-for="item in goods?.details.properties" :key="item.name">
            <text class="label">{{ item.name }}</text>
            <text class="value">{{ item.value }}</text>
          </view>
        </view>
        <!-- 图片详情 -->
        <image
          v-for="item in goods?.details.pictures"
          :key="item"
          mode="widthFix"
          :src="item"
        ></image>
      </view>
    </view>

    <!-- 同类推荐 -->
    <view class="similar panel">
      <view class="title">
        <text>同类推荐</text>
      </view>
      <view class="content">
        <navigator
          v-for="item in goods?.similarProducts"
          :key="item"
          class="goods"
          hover-class="none"
          :url="`/pages/goods/goods?id=${item.id}`"
        >
          <image class="image" mode="aspectFill" :src="item.picture"></image>
          <view class="name ellipsis">{{ item.name }}</view>
          <view class="price">
            <text class="symbol">¥</text>
            <text class="number">{{ item.price }}</text>
          </view>
        </navigator>
      </view>
    </view>
  </scroll-view>
  <PageSkeleton v-else />

  <!-- 用户操作 -->
  <view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
    <view class="icons">
      <button class="icons-button"><text class="icon-heart"></text>收藏</button>
      <button class="icons-button" open-type="contact">
        <text class="icon-handset"></text>客服
      </button>
      <navigator class="icons-button" url="/pages/cart/cart" open-type="switchTab">
        <text class="icon-cart"></text>购物车
      </navigator>
    </view>
    <view class="buttons">
      <view class="addcart" @tap="openSkuPopup(SkuMode.Cart)"> 加入购物车 </view>
      <view class="buynow" @tap="openSkuPopup(SkuMode.Buy)"> 立即购买 </view>
    </view>
  </view>

  <!-- uni-ui 弹出层 -->
  <uni-popup ref="popup" type="bottom">
    <AddressPanel v-if="popupName === 'address'" @close="popup?.close()" />
    <ServicePanel v-if="popupName === 'service'" @close="popup?.close()" />
  </uni-popup>
</template>

购物车列表页面:cart.vue

获取登录的用户信息  -->  条件渲染(是否登录)  -->  初始化调用  -->  列表渲染

 

封装购物车列表类型数据:cart.d.ts

/** 购物车类型 */

export type CartItem = {

  /** 商品 ID */

  id: string

  /** SKU ID */

  skuId: string

  /** 商品名称 */

  name: string

  /** 图片 */

  picture: string

  /** 数量 */

  count: number

  /** 加入时价格 */

  price: number

  /** 当前的价格 */

  nowPrice: number

  /** 库存 */

  stock: number

  /** 是否选中 */

  selected: boolean

  /** 属性文字 */

  attrsText: string

  /** 是否为有效商品 */

  isEffective: boolean

}

封装购物车列表接口:cart.ts

import type { CartItem } from '@/types/cart';

import { http } from '@/utils/http'

/**

 * 获取购物车列表数据

 * @returns

 */

export const getMemberCartAPI = () => {

  return http<CartItem[]>({

    method: 'GET',

    url: '/member/cart',

  })

}

初始化调用:cart.vue

// 获取购物车列表数据

const cartList = ref<CartItem>([])

const getMemberCartData = async () => {

  const res = await getMemberCartAPI()

  cartList.value = res.result

}

// onShow:页面显示就触发     页面初始化调用  因为加入购物车不是在这个页面的,所以用onShow调用更合适

onShow(() => {

  // 判断用户是否已经登录了

  if (memberStore.profile) {

    getMemberCartData()

  }

})

删除购物车列表中的商品:封装API、按钮绑定事件、弹窗二次确认、调用API、重新获取列表

封装购物车删除API 接口:

/**

 * 删除/清空购物车单品

 * @param data 请求体参数 ids SKUID 集合

 */

export const deleteMemberCartAPI = (data: { ids: string[] }) => {

  return http({

    method: 'DELETE',

    url: '/member/cart',

    data,

  })

}

点击删除按钮 - 删除购物车商品   cart.vue

// 点击删除按钮 - 删除购物车

const onDeleteCart = (skuId: string) => {

  // 弹窗二次确认

  uni.showModal({

    content: '是否确定删除?',

    success: async (res) => {

      if (res.confirm) {

        await deleteMemberCartAPI({ ids: [skuId] })

        // 更新购物车列表

        getMemberCartData()

      }

    },

  })

}

删除成功

修改商品数量:步进器组件

<view class="count">

                <!-- <text class="text">-</text>

                <input class="input" type="number" :value="item.count.toString()" />

                <text class="text">+</text> -->

                <vk-data-input-number-box

                  v-model="item.count"

                  :min="1"

                  :max="item.stock"

                  :index="item.skuId"

                  @change="onChangeCount"

                />

</view>

封装修改API

/**

 * 修改购物车单品

 * @param skuId SKUID

 * @param data selected 选中状态 count 商品数量

 */

export const putMemberCartBySkuIdAPI = ( skuId: string, data: { selected?: boolean; count?: number }) => {

  return http({

    method: 'PUT',

    url: `/member/cart/${skuId}`,

    data,

  })

}

修改方法:

// 修改商品数量

const onChangeCount = (e) => {

  console.log(e)

  putMemberCartBySkuIdAPI(e.index, { count: e.value })

}

修改商品的选中状态,即单选和全选功能实现

            <!-- 选中状态 -->

              <text

                @tap="onChangeSelected(item)"

                class="checkbox"

                :class="{ checked: item.selected }"

              ></text>

封装全选 / 取消全选API

/**

 * 购物车全选/取消全选

 * @param data selected 是否选中

 */

export const putMemberCartSelectedAPI = (data: { selected: boolean }) => {

  return http({

    method: 'PUT',

    url: '/member/cart/selected',

    data,

  })

}

// 修改选中状态  - 单选修改

const onChangeSelected = (good: CartItem) => {

  console.log(good)

// 前端数据更新  - 是否选中 取反

  good.selected = !good.selected

  // 后端数据更新    与修改数量接口是同一条接口  传递的参数不同

  putMemberCartBySkuIdAPI(good.skuId, { selected: good.selected })

}

// 计算全选状态

const isSelectedAll = computed(() => {

  return cartList.value.length && cartList.value.every((v) => v.selected)

})

// 修改选中状态-全选修改

const onChangeSelectedAll = () => {

  // 全选状态取法

  const _isSelectedAll = !isSelectedAll.value

  // 前端数据更新

  cartList.value.forEach((item) => {

    item.selected = _isSelectedAll

  })

  // 后端更新

  putMemberCartSelectedAPI({ selected: _isSelectedAll })

}

购物车页面 - 底部结算信息

<!-- 底部结算 -->

      <view class="toolbar">

        <text class="all" @tap="onChangeSelectedAll" :class="{ checked: isSelectedAll }">全选</text>

        <text class="text">合计:</text>

        <text class="amount">{{ selectedCartListMoney }}</text>

        <view class="button-grounp">

          <view

            @tap="gotoPayment"

            class="button payment-button"

            :class="{ disabled: selectedCartListCount === 0 }"

          >

            去结算({{ selectedCartListCount }})

          </view>

        </view>

      </view>

逻辑实现:

// 计算选中的商品列表

const selectedCartList = computed(() => {

  return cartList.value.filter((v) => v.selected)

})

// 计算选中商品的总件数

const selectedCartListCount = computed(() => {

  return selectedCartList.value.reduce((sum, item) => sum + item.count, 0)

})

// 计算选中商品的总金额

const selectedCartListMoney = computed(() => {

  return selectedCartList.value

    .reduce((sum, item) => sum + item.count * item.nowPrice, 0)

    .toFixed(2)

})

// 去结算按钮

const gotoPayment = () => {

  // 判断用户是否选择了商品    即商品数量不能为 0

  if (selectedCartListCount.value === 0) {

    return uni.showToast({ icon: 'none', title: '请选择商品' })

  }

  // 跳转到计算页面

  uni.showToast({ title: '此功能还未写' })

}

完整的购物车列表页面组件代码:cart.vue

<script setup lang="ts">
import { onShow } from '@dcloudio/uni-app'
import { ref, computed } from 'vue'
import {
  deleteMemberCartAPI,
  getMemberCartAPI,
  putMemberCartBySkuIdAPI,
  putMemberCartSelectedAPI,
} from '@/services/cart'
import { useMemberStore } from '@/stores/index'
import type { CartItem } from '@/types/cart'
import type { InputNumberBoxEvent } from '@/components/vk-data-input-number-box/vk-data-input-number-box'

// 获取会员 Store
const memberStore = useMemberStore()

// 获取购物车列表数据
const cartList = ref<CartItem>([])
const getMemberCartData = async () => {
  const res = await getMemberCartAPI()
  cartList.value = res.result
}

// onShow:页面显示就触发      页面初始化调用  因为加入购物车不是在这个页面的,所以用onShow调用更合适
onShow(() => {
  // 判断用户是否已经登录了
  if (memberStore.profile) {
    getMemberCartData()
  }
})

// 点击删除按钮 - 删除购物车
const onDeleteCart = (skuId: string) => {
  // 弹窗二次确认
  uni.showModal({
    content: '是否确定删除?',
    success: async (res) => {
      if (res.confirm) {
        await deleteMemberCartAPI({ ids: [skuId] })
        // 更新购物车列表
        getMemberCartData()
      }
    },
  })
}

// 修改商品数量
const onChangeCount = (e: InputNumberBoxEvent) => {
  console.log(e)
  putMemberCartBySkuIdAPI(e.index, { count: e.value })
}

// 修改选中状态 - 单品修改
const onChangeSelected = (good: CartItem) => {
  console.log(good)
  // 前端数据更新  - 是否选中 取反
  good.selected = !good.selected
  // 后端数据更新
  putMemberCartBySkuIdAPI(good.skuId, { selected: good.selected })
}

// 计算全选状态
const isSelectedAll = computed(() => {
  return cartList.value.length && cartList.value.every((v) => v.selected)
})

// 修改选中状态-全选修改
const onChangeSelectedAll = () => {
  // 全选状态取法
  const _isSelectedAll = !isSelectedAll.value
  // 前端数据更新
  cartList.value.forEach((item) => {
    item.selected = _isSelectedAll
  })
  // 后端更新
  putMemberCartSelectedAPI({ selected: _isSelectedAll })
}

// 计算选中的商品列表
const selectedCartList = computed(() => {
  return cartList.value.filter((v) => v.selected)
})

// 计算选中商品的总件数
const selectedCartListCount = computed(() => {
  return selectedCartList.value.reduce((sum, item) => sum + item.count, 0)
})

// 计算选中商品的总金额
const selectedCartListMoney = computed(() => {
  return selectedCartList.value
    .reduce((sum, item) => sum + item.count * item.nowPrice, 0)
    .toFixed(2)
})

// 去结算按钮
const gotoPayment = () => {
  // 判断用户是否选择了商品    即商品数量不能为 0
  if (selectedCartListCount.value === 0) {
    return uni.showToast({ icon: 'none', title: '请选择商品' })
  }
  // 跳转到计算页面
  uni.showToast({ title: '此功能还未写' })
}
</script>

<template>
  <scroll-view scroll-y class="scroll-view">
    <!-- 已登录: 显示购物车 -->
    <template v-if="memberStore.profile.token">
      <!-- 购物车列表 -->
      <view class="cart-list" v-if="cartList.length">
        <!-- 优惠提示 -->
        <view class="tips">
          <text class="label">满减</text>
          <text class="desc">满1件, 即可享受9折优惠</text>
        </view>
        <!-- 滑动操作分区 -->
        <uni-swipe-action>
          <!-- 滑动操作项 -->
          <uni-swipe-action-item v-for="item in cartList" :key="item.skuId" class="cart-swipe">
            <!-- 商品信息 -->
            <view class="goods">
              <!-- 选中状态 -->
              <text
                @tap="onChangeSelected(item)"
                class="checkbox"
                :class="{ checked: item.selected }"
              ></text>
              <navigator
                :url="`/pages/goods/goods?id=${item.id}`"
                hover-class="none"
                class="navigator"
              >
                <image mode="aspectFill" class="picture" :src="item.picture"></image>
                <view class="meta">
                  <view class="name ellipsis">{{ item.name }}</view>
                  <view class="attrsText ellipsis">{{ item.attrsText }}</view>
                  <view class="price">{{ item.nowPrice }}</view>
                </view>
              </navigator>
              <!-- 商品数量 -->
              <view class="count">
                <!-- <text class="text">-</text>
                <input class="input" type="number" :value="item.count.toString()" />
                <text class="text">+</text> -->
                <vk-data-input-number-box
                  v-model="item.count"
                  :min="1"
                  :max="item.stock"
                  :index="item.skuId"
                  @change="onChangeCount"
                />
              </view>
            </view>
            <!-- 右侧删除按钮 -->
            <template #right>
              <view class="cart-swipe-right">
                <button @tap="onDeleteCart(item.skuId)" class="button delete-button">删除</button>
              </view>
            </template>
          </uni-swipe-action-item>
        </uni-swipe-action>
      </view>
      <!-- 购物车空状态 -->
      <view class="cart-blank" v-else>
        <image src="/static/images/blank_cart.png" class="image" />
        <text class="text">购物车还是空的,快来挑选好货吧</text>
        <navigator open-type="switchTab" url="/pages/index/index" hover-class="none">
          <button class="button">去首页看看</button>
        </navigator>
      </view>
      <!-- 吸底工具栏 -->
      <view class="toolbar">
        <text class="all" @tap="onChangeSelectedAll" :class="{ checked: isSelectedAll }">全选</text>
        <text class="text">合计:</text>
        <text class="amount">{{ selectedCartListMoney }}</text>
        <view class="button-grounp">
          <view
            @tap="gotoPayment"
            class="button payment-button"
            :class="{ disabled: selectedCartListCount === 0 }"
          >
            去结算({{ selectedCartListCount }})
          </view>
        </view>
      </view>
    </template>
    <!-- 未登录: 提示登录 -->
    <view class="login-blank" v-else>
      <text class="text">登录后可查看购物车中的商品</text>
      <navigator url="/pages/login/login" hover-class="none">
        <button class="button">去登录</button>
      </navigator>
    </view>
    <!-- 猜你喜欢 -->
    <Guess ref="guessRef"></XtxGuess>
    <!-- 底部占位空盒子 -->
    <view class="toolbar-height"></view>
  </scroll-view>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/641866.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

总结 HTTPS 的加密流程

一、前言 http是为了解决http存在的问题而在http基础上加入了SSL/TSL&#xff0c;在HTTP/2中TCP三次握手后会进入SSL/TSL握手&#xff0c;当SSL/TSL建立链接后&#xff0c;才会进行报文的传输。 二、HTTPS的混合加密 我们先来认识密钥&#xff1a; 密钥是用于加密和解密数据…

VMware安装保姆教程、Docker安装/依赖安装缓慢等问题

常见问题前置: 1、docker依赖安装缓慢,没有走设置的资源库:解决安装docker-ce过慢 Operation too slow. Less than 1000 bytes/sec transferred the last 30 seconds‘) 在添加阿里云镜像后安装依旧慢: yum-config-manager --add-repo http://mirrors.aliyun.com/docker…

JavaSE--基础语法(第一期)

Java是一种优秀的程序设计语言&#xff0c;它具有令人赏心悦目的语法和易于理解的语义。不仅如此&#xff0c;Java还是一个有一系列计算机软件和规范形成的技术体系&#xff0c;这个技术体系提供了完整的用于软件开发和 跨平台部署的支持环境&#xff0c;并广泛应用于嵌入式系统…

鸿蒙HarmonyOS开发:tabs结合tabContent实现底部tabBar导航栏页面布局

文章目录 一、组件介绍1、Tabs参数属性事件TabsController 2、子组件属性说明 二、基础示例1、基础顶部导航2、效果3、可以滚动导航栏2、效果 三、扩展示例自定义导航栏1、代码2、效果 一、组件介绍 Tabs组件的页面组成包含两个部分&#xff0c;分别是TabContent和TabBar。Tab…

宝塔面板修改端口后无法登入

今天通过宝塔面板登录腾讯云主机&#xff0c;看到下面的提醒&#xff0c;顺便点进去随便改了个端口 本以为改端口是很简单事情&#xff0c;结果我改完之后面板立马登不上了&#xff0c;接下来我改了登录地址和端口也不行&#xff0c;我以为是防火墙的问题&#xff0c;增加了防火…

告别登录烦恼,WPS免登录修改器体验!(如何实现不登录使用WPS)

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 演示环境 &#x1f3e1;&#x1f4d2; 解决方案 &#x1f4d2;&#x1f388; 获取方式 &#x1f388;⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 想象一下&#xff0c;如果你能够绕过繁琐的登录流程&#x…

微信小程序--微信开发者工具使用小技巧(3)

一、微信开发者工具使用小技巧 1、快速创建小程序页面 在app.json中的pages配置项&#xff0c;把需要创建的页面填写上去 2、快捷键使用 进入方式 1&#xff1a; 文件–>首选项–> keyboard shortcuts 进入快捷键查看与设置 进入方式 2&#xff1a; 设置–>快捷键…

Tower在深度学习中的概念,tower没有确切定义

在论文UniTS中&#xff0c;来自Havard的工作。 tower更像是针对一个task的组件 tower这个概念貌似在REC&#xff08;recommendation&#xff09;推荐系统中使用较多 deep learning - What is a tower? - Data Science Stack Exchange https://developers.google.com/machin…

lvgl无法显示中文

环境&#xff1a; VS2019、LVGL8.3 问题&#xff1a; VS2019默认编码为GB2312&#xff0c; 解决&#xff1a; VS2022设置编码方式为utf-8的三种方式_vs utf8-CSDN博客 我用的方法2&#xff0c;设置为 utf-8无签名就行。

基于transformers框架实践Bert系列6-完形填空

本系列用于Bert模型实践实际场景&#xff0c;分别包括分类器、命名实体识别、选择题、文本摘要等等。&#xff08;关于Bert的结构和详细这里就不做讲解&#xff0c;但了解Bert的基本结构是做实践的基础&#xff0c;因此看本系列之前&#xff0c;最好了解一下transformers和Bert…

c++入门的基础知识

c入门 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff1a; 补充C语言语法的不足&#xff0c;以及C是如何对C语言设计…

ClickHouse vs. Elasticsearch: 计数聚合的工作原理

本文字数&#xff1a;7875&#xff1b;估计阅读时间&#xff1a;20 分钟 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 介绍 在另一篇博客文章中&#xff0c;我们对 ClickHouse 和 Elasticsearch 在大规模数据分析和可观测性用例中的性能进行了比较&#xff0c;特别是对…

k8s-helloword部署一个应用

k8s-helloword部署一个应用 快速部署一个pod命令 部署一个名为 test-nginx Pod 方式一&#xff1a;使用 kubectl run kubectl run test-nginx --imagenginx然后使用 kubectl get pod 查看&#xff0c;kubectl get pod 是查看默认名称空间下的Pod 如果想要跟详细的查看这个…

HTML静态网页成品作业(HTML+CSS)——宠物狗介绍网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有3个页面。 二、作品演示 三、代…

如何禁止U盘拷贝文件|禁止U盘使用的软件有哪些

禁止U盘拷贝文件的方法有很多&#xff0c;比如使用注册表、组策略编辑器等&#xff0c;但这些方法都适合个人&#xff0c;不适合企业&#xff0c;因为企业需要对下属多台电脑进行远程管控&#xff0c;需要方便、省时、省力的方法。目前来说&#xff0c;最好的方法就是使用第三方…

RDP方式连接服务器上传文件方法

随笔 目录 1. RDP 连接服务器 2. 为避免rdp 访问界面文字不清晰 3. 本地上传文件到服务器 1. RDP 连接服务器 # mstsc 连接服务器step1: 输入mstscstep2: 输入 IP, username, passwd 2. 为避免rdp 访问界面文字不清晰 解决方法&#xff1a; 3. 本地上传文件到服务器 step…

Java进阶学习笔记13——抽象类

认识抽象类&#xff1a; 当我们在做子类共性功能抽取的时候&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了。在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff0c;而类中如果有抽象方法&#xff0c;该类就定义为抽象类…

ASP+ACCESS基于WEB网上留言板

摘要 本文概述了ACCESS数据库及其相关的一些知识&#xff0c;着重论述ACCESS数据库和ASP的中间技术&#xff0c;构建一个简单的留言板。具体的实现是构造一个留言板系统&#xff0c;能很方便的和同学沟通和交流。留言板具有功能强大、使用方便的特点。用户以个人的身份进入&am…

jenkins+sonarqube部署与配置过程

1、部署jenkins&#xff08;本文不做说明&#xff09; 2、部署sonarqube(docker-compose) version: "2.1"services:sonarqube:image: sonarqube:9.9.4-communitycontainer_name: sonarqubedepends_on:- dbports:- 9000:9000networks:- sonarnetenvironment:SONARQU…

集合、Collection接口特点和常用方法

1、集合介绍 对于保存多个数据使用的是数组&#xff0c;那么数组有不足的地方。比如&#xff0c; 长度开始时必须指定&#xff0c;而且一旦制定&#xff0c;不能更改。 保存的必须为同一类型的元素。 使用数组进行增加/删除元素的示意代码&#xff0c;也就是比较麻烦。 为…