Files
integral-shop/single_uniapp22miao/pages/integral/cart.vue
panchengyong 786bf78282 更新项目配置和添加小程序模块
- 修改 ArticleController.java
- 更新 application.yml 配置
- 更新 frontend/.env.production 环境配置
- 添加 single_uniapp22miao 小程序模块
- 添加 logs 目录
2026-03-13 13:27:13 +08:00

1193 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="cart-page">
<!-- 顶部导航栏 -->
<view class="custom-navbar">
<view class="nav-back" @click="goToIndex">
<text class="iconfont icon-fanhui"></text>
</view>
<text class="nav-title">购物车</text>
<!-- <text class="nav-count" v-if="cartCount > 0">({{ cartCount }})</text> -->
</view>
<!-- 购物车列表 -->
<view class="cart-content" v-if="cartList.valid.length > 0 || cartList.invalid.length > 0">
<!-- 有效商品列表 -->
<view class="cart-list" v-if="cartList.valid.length > 0">
<view
v-for="(item, index) in cartList.valid"
:key="item.id"
class="cart-item"
>
<!-- 复选框 -->
<view
class="checkbox"
:class="{ checked: item.checked, disabled: !item.attrStatus && footerswitch }"
@click="toggleSelect(index)"
>
<text v-if="item.checked" class="check-icon"></text>
</view>
<!-- 商品图片 -->
<image
v-if="item.image"
:src="item.image"
class="goods-image"
mode="aspectFill"
@click="goToDetail(item)"
@error="handleImageError(index)"
/>
<view v-else class="goods-image placeholder">
<text class="placeholder-text">暂无图片</text>
</view>
<!-- 商品信息 -->
<view class="goods-info">
<text
class="goods-name"
:class="{ 'invalid-text': !item.attrStatus }"
@click="goToDetail(item)"
>{{ item.storeName }}</text>
<!-- 规格信息 -->
<text class="goods-spec" v-if="item.suk">属性{{ item.suk }}</text>
<!-- 价格/积分 -->
<text class="goods-points" v-if="item.attrStatus">
{{ formatPoints(item.vipPrice || item.price) }} 积分
</text>
<!-- 规格失效提示 -->
<view class="invalid-tip" v-if="!item.attrStatus">
<text class="tip-text">请重新选择商品规格</text>
</view>
<!-- 数量控制区域 -->
<view class="quantity-section" v-if="item.attrStatus">
<view class="quantity-control">
<view
class="btn-minus"
:class="{ disabled: item.numSub }"
@click.stop="subCart(index)"
>
<text></text>
</view>
<text class="quantity-num">{{ item.cartNum }}</text>
<view
class="btn-plus"
:class="{ disabled: item.numAdd }"
@click.stop="addCart(index)"
>
<text></text>
</view>
</view>
<!-- 删除按钮 -->
<view class="btn-delete" @click.stop="deleteItem(index)">
<text class="delete-icon">🗑</text>
</view>
</view>
</view>
</view>
</view>
<!-- 失效商品列表 -->
<view class="invalid-section" v-if="cartList.invalid.length > 0">
<view class="invalid-header">
<text class="invalid-title">失效商品 ({{ cartList.invalid.length }})</text>
</view>
<view class="invalid-list">
<view
v-for="(item, index) in cartList.invalid"
:key="'invalid-' + item.id"
class="cart-item invalid"
>
<view class="invalid-badge">
<text>失效</text>
</view>
<image
v-if="item.image"
:src="item.image"
class="goods-image"
mode="aspectFill"
/>
<view class="goods-info">
<text class="goods-name invalid-text">{{ item.storeName }}</text>
<text class="goods-spec" v-if="item.suk">属性{{ item.suk }}</text>
<text class="invalid-reason">该商品已失效</text>
</view>
</view>
</view>
</view>
</view>
<!-- 空购物车 -->
<view class="empty-cart" v-else>
<view class="empty-icon">🛒</view>
<text class="empty-text">购物车是空的</text>
<view class="btn-go-shop" @click="goToIndex">
<text>去逛逛</text>
</view>
</view>
<!-- 底部结算栏 -->
<view class="footer-bar" v-if="cartList.valid.length > 0">
<view class="footer-top">
<view class="select-all" @click="toggleSelectAll">
<view class="checkbox" :class="{ checked: isAllSelected }">
<text v-if="isAllSelected" class="check-icon"></text>
</view>
<text class="select-text">全选({{ selectValue.length }})</text>
</view>
<text class="selected-count">已选 {{ selectedCount }} 件商品</text>
</view>
<view class="footer-bottom">
<view class="points-info">
<text class="my-points">我的积分: {{ formatPoints(userPoints) }}</text>
<view class="total-points">
<text class="label">合计:</text>
<text class="value">{{ formatPoints(totalPoints) }}</text>
<text class="unit">积分</text>
</view>
</view>
<view
class="btn-checkout"
:class="{ disabled: selectedCount === 0 || totalPoints > userPoints }"
@click="onCheckout"
>
<text> </text>
</view>
</view>
</view>
<!-- 底部导航栏 -->
<view class="bottom-tabbar">
<view class="tab-item" @click="goToIndex">
<!-- <view class="tab-icon-wrap">
<text class="tab-icon-text">🏠</text>
</view> -->
<image class="tab-icon" src="/static/tabBar/home.png" mode="aspectFit"></image>
<text class="tab-text">积分兑换</text>
</view>
<view class="tab-item active">
<view class="tab-icon-wrap">
<view class="cart-badge" v-if="cartTotalCount > 0">
<text>{{ cartTotalCount > 99 ? '99+' : cartTotalCount }}</text>
</view>
<text class="tab-icon-text">
<image class="tab-icon" src="/static/tabBar/cartd.png" mode="aspectFit"></image></text>
</view>
<text class="tab-text active">购物车</text>
</view>
<view class="tab-item" @click="goToOrders">
<!-- <view class="tab-icon-wrap">
<text class="tab-icon-text">📋</text>
</view> -->
<image class="tab-icon" src="/static/tabBar/order.png" mode="aspectFit"></image>
<text class="tab-text">订单</text>
</view>
</view>
</view>
</template>
<script>
import { getIntegralUserByAccount } from '@/api/user.js'
import { getCartList, getCartCounts, changeCartNum, cartDel } from '@/api/order.js'
import { mapGetters } from "vuex"
import { toLogin } from '@/libs/login.js'
import { Debounce } from '@/utils/validate.js'
export default {
computed: {
...mapGetters(['isLogin', 'userInfo']),
// 是否全选(只计算有效商品)
isAllSelected() {
const validList = this.cartList.valid || []
if (validList.length === 0) return false
const attrStatusList = validList.filter(item => item.attrStatus)
return attrStatusList.length > 0 && attrStatusList.every(item => item.checked)
},
// 已选商品总件数(含数量)
selectedCount() {
return this.selectValue.length
},
// 已选商品种类数(用于结算按钮显示)
selectedItemCount() {
return this.selectValue.length
},
// 购物车总商品数用于底部badge
cartTotalCount() {
return this.cartCount
},
// 已选商品总积分/价格
totalPoints() {
return this.selectCountPrice
},
// 获取用户账号
userAccount() {
if (this.userInfo) {
return this.userInfo.account || this.userInfo.phone || this.userInfo.phoneNumber || ''
}
return ''
}
},
data() {
return {
account: '', // 默认账号,用于获取积分
userPoints: 0,
cartCount: 0,
cartList: {
valid: [],
invalid: []
},
selectValue: [], // 选中的商品ID数组
selectCountPrice: 0, // 选中商品的总价格/积分
loading: false,
page: 1,
limit: 20,
loadend: false,
footerswitch: true // true=正常模式 false=管理模式
}
},
onLoad() {
if (!this.isLogin) {
toLogin()
return
}
this.init()
},
onShow() {
// 每次显示时刷新购物车和积分
if (this.isLogin) {
this.getIndex()
this.getUserPoints()
}
},
onPullDownRefresh() {
this.getIndex()
this.getUserPoints()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
},
methods: {
// 初始化
async init() {
await Promise.all([
this.getUserPoints(),
this.getIndex()
])
},
// 首次进入加载的接口
getIndex() {
this.page = 1
this.cartList.valid = []
this.cartList.invalid = []
this.selectValue = []
this.selectCountPrice = 0
this.loadend = false
this.getCartList()
this.getInvalidList()
},
// 格式化积分数字(添加千分位逗号)
formatPoints(points) {
if (!points && points !== 0) return '0'
return Number(points).toFixed(0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
},
// 获取用户积分
async getUserPoints() {
try {
// 尝试从用户信息获取
if (this.userInfo && this.userInfo.integral !== undefined) {
this.userPoints = this.userInfo.integral || 0
return
}
// 尝试通过账号获取
const account = this.userAccount || this.account
if (account) {
const res = await getIntegralUserByAccount(account)
if (res.data) {
this.userPoints = res.data.integral || 0
}
}
} catch (error) {
console.error('获取积分失败:', error)
if (this.userInfo && this.userInfo.integral !== undefined) {
this.userPoints = this.userInfo.integral || 0
}
}
},
// 获取购物车数量
getCartNum() {
getCartCounts(true, 'total').then(res => {
this.cartCount = res.data.count || 0
})
},
// 获取购物车列表(有效商品)
async getCartList() {
if (this.loading) return
this.loading = true
uni.showLoading({
title: '加载中',
mask: true
})
try {
// 先获取购物车数量
const countRes = await getCartCounts(true, 'total')
this.cartCount = countRes.data.count || 0
// 获取有效商品列表
const res = await getCartList({
page: this.page,
limit: this.limit,
isValid: true
})
if (res.data && res.data.list) {
let validList = res.data.list || []
let selectValue = []
console.log('购物车原始数据:', validList)
// 处理每个商品的状态
validList = validList.map(item => {
console.log('购物车商品:', { id: item.id, cartNum: item.cartNum, storeName: item.storeName })
// 数量控制状态
item.numSub = item.cartNum <= 1
item.numAdd = item.cartNum >= (item.stock || 0)
// 默认选中有效商品
if (item.attrStatus) {
item.checked = true
selectValue.push(item.id)
} else {
item.checked = false
}
return item
})
this.cartList.valid = validList
this.selectValue = selectValue
this.switchSelect()
}
} catch (error) {
console.error('获取购物车失败:', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.loading = false
uni.hideLoading()
}
},
// 获取失效商品列表
async getInvalidList() {
try {
const res = await getCartList({
page: 1,
limit: 20,
isValid: false
})
if (res.data && res.data.list) {
this.cartList.invalid = res.data.list || []
}
} catch (error) {
console.error('获取失效商品失败:', error)
}
},
// 切换单个商品选中状态
toggleSelect(index) {
const item = this.cartList.valid[index]
if (!item) return
// 如果是正常模式且商品规格无效,不允许选中
if (this.footerswitch && !item.attrStatus) {
return
}
item.checked = !item.checked
// 更新选中列表
if (item.checked) {
if (!this.selectValue.includes(item.id)) {
this.selectValue.push(item.id)
}
} else {
const idx = this.selectValue.indexOf(item.id)
if (idx > -1) {
this.selectValue.splice(idx, 1)
}
}
this.switchSelect()
},
// 切换全选
toggleSelectAll() {
const newSelected = !this.isAllSelected
let selectValue = []
this.cartList.valid = this.cartList.valid.map(item => {
if (newSelected) {
if (this.footerswitch) {
// 正常模式:只选中有效商品
if (item.attrStatus) {
item.checked = true
selectValue.push(item.id)
} else {
item.checked = false
}
} else {
// 管理模式:全部选中
item.checked = true
selectValue.push(item.id)
}
} else {
item.checked = false
}
return item
})
this.selectValue = selectValue
this.switchSelect()
},
// 计算选中商品总价
switchSelect() {
let selectCountPrice = 0
const validList = this.cartList.valid || []
validList.forEach(item => {
if (this.selectValue.includes(item.id)) {
// 使用 vipPrice 或 price 作为积分/价格
const price = item.vipPrice || item.price || 0
selectCountPrice += price * item.cartNum
}
})
this.selectCountPrice = selectCountPrice
},
// 减少数量
subCart: Debounce(function(index) {
const item = this.cartList.valid[index]
console.log('subCart:', { index, id: item?.id, cartNum: item?.cartNum })
if (!item || !item.attrStatus) return
let newNum = Number(item.cartNum) - 1
if (newNum < 1) {
newNum = 1
item.numSub = true
return
}
item.cartNum = newNum
item.numSub = newNum <= 1
item.numAdd = false
this.setCartNum(item.id, newNum, () => {
this.switchSelect()
this.getCartNum()
})
}),
// 增加数量
addCart: Debounce(function(index) {
const item = this.cartList.valid[index]
console.log('addCart:', { index, id: item?.id, cartNum: item?.cartNum })
if (!item || !item.attrStatus) return
let newNum = Number(item.cartNum) + 1
const stock = item.stock || 0
if (newNum > stock) {
newNum = stock
item.numAdd = true
uni.showToast({
title: '库存不足',
icon: 'none'
})
return
}
item.cartNum = newNum
item.numSub = false
item.numAdd = newNum >= stock
this.setCartNum(item.id, newNum, () => {
this.switchSelect()
this.getCartNum()
})
}),
// 修改数量(兼容旧方法名)
changeQuantity(index, delta) {
if (delta > 0) {
this.addCart(index)
} else {
this.subCart(index)
}
},
// 调用API修改购物车数量
setCartNum(cartId, cartNum, successCallback) {
console.log('=== setCartNum 开始 ===')
console.log('参数:', { cartId, cartNum, cartIdType: typeof cartId })
if (!cartId && cartId !== 0) {
console.error('购物车ID无效:', cartId)
uni.showToast({
title: '购物车ID无效',
icon: 'none'
})
return
}
// 使用封装的 changeCartNum 方法
changeCartNum(cartId, cartNum, { noVerify: true }).then(res => {
// 后端成功码可能是 0 或 200
if (res && (res.code === 0 || res.code === 200)) {
console.log('修改数量成功')
successCallback && successCallback(res.data)
} else {
console.error('业务错误:', res)
this.getIndex()
uni.showToast({
title: res?.message || res?.msg || '修改失败',
icon: 'none'
})
}
}).catch(err => {
console.error('修改数量请求失败:', err)
this.getIndex()
uni.showToast({
title: typeof err === 'string' ? err : (err?.msg || err?.message || '修改失败'),
icon: 'none'
})
})
},
// 删除商品
deleteItem(index) {
const item = this.cartList.valid[index]
if (!item) return
uni.showModal({
title: '提示',
content: '确定要删除该商品吗?',
success: (res) => {
if (res.confirm) {
this.delCartItem([item.id], index)
}
}
})
},
// 批量删除商品
delCartItem(ids, singleIndex) {
cartDel(ids, { noVerify: true }).then(res => {
// 后端成功码可能是 0 或 200
if (res && (res.code === 0 || res.code === 200)) {
if (singleIndex !== undefined) {
// 单个删除
this.cartList.valid.splice(singleIndex, 1)
} else {
// 批量删除(管理模式)
this.cartList.valid = this.cartList.valid.filter(item => !ids.includes(item.id))
}
// 更新选中列表
this.selectValue = this.selectValue.filter(id => !ids.includes(id))
this.switchSelect()
this.getCartNum()
uni.showToast({
title: '删除成功',
icon: 'success'
})
} else {
console.error('删除失败:', res)
uni.showToast({
title: res?.message || res?.msg || '删除失败',
icon: 'none'
})
}
}).catch(err => {
console.error('删除请求失败:', err)
uni.showToast({
title: typeof err === 'string' ? err : '删除失败',
icon: 'none'
})
})
},
// 去结算
onCheckout() {
if (this.selectValue.length === 0) {
uni.showToast({
title: '请选择商品',
icon: 'none'
})
return
}
// 检查积分是否足够
if (this.selectCountPrice > this.userPoints) {
uni.showToast({
title: '积分不足',
icon: 'none'
})
return
}
// 跳转到积分商城订单确认页面
const cartIds = this.selectValue.join(',')
uni.navigateTo({
url: `/pages/integral/confirm?cartIds=${cartIds}&from=cart`
})
},
// 跳转商品详情
goToDetail(item) {
const goodsId = item.productId || item.goods_id || item.id
if (!goodsId) {
uni.showToast({
title: '商品ID无效',
icon: 'none'
})
return
}
uni.navigateTo({
url: `/pages/integral/detail?id=${goodsId}`
})
},
// 跳转首页
goToIndex() {
uni.navigateTo({
url: '/pages/integral/index'
})
},
// 跳转订单列表
goToOrders() {
uni.navigateTo({
url: '/pages/integral/orders'
})
},
// 处理图片加载错误
handleImageError(index) {
const item = this.cartList.valid[index]
if (item) {
item.image = ''
this.$forceUpdate()
}
},
// 检查商品ID是否在选中列表中
inArray(search, array) {
return array.includes(search)
}
}
}
</script>
<style lang="scss" scoped>
.cart-page {
min-height: 100vh;
background-color: #F8F8F8;
padding-bottom: 280rpx;
}
// 顶部导航栏
.custom-navbar {
background-color: #FFFFFF;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1rpx solid #F0F0F0;
position: relative;
padding: 0 30rpx;
}
.nav-back {
position: absolute;
left: 30rpx;
font-size: 36rpx;
color: #333333;
}
.nav-title {
font-size: 36rpx;
color: #333333;
font-weight: 500;
}
.nav-count {
font-size: 28rpx;
color: #999999;
margin-left: 8rpx;
}
// 购物车内容
.cart-content {
padding: 20rpx;
}
.cart-list {
background-color: transparent;
}
.cart-item {
background-color: #FFFFFF;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
display: flex;
align-items: flex-start;
gap: 20rpx;
}
// 复选框
.checkbox {
width: 44rpx;
height: 44rpx;
border: 2rpx solid #DDDDDD;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 40rpx;
&.checked {
background-color: #FF6900;
border-color: #FF6900;
}
.check-icon {
color: #FFFFFF;
font-size: 28rpx;
font-weight: bold;
}
}
// 商品图片
.goods-image {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
flex-shrink: 0;
background-color: #F5F5F5;
&.placeholder {
display: flex;
align-items: center;
justify-content: center;
background-color: #F5F5F5;
border: 1rpx solid #EEEEEE;
}
.placeholder-text {
font-size: 24rpx;
color: #CCCCCC;
}
}
// 商品信息
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
min-height: 160rpx;
}
.goods-name {
font-size: 28rpx;
color: #333333;
font-weight: 500;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
line-clamp: 1;
overflow: hidden;
margin-bottom: 12rpx;
}
.goods-spec {
font-size: 24rpx;
color: #999999;
margin-bottom: 8rpx;
}
.goods-points {
font-size: 30rpx;
color: #FF6900;
font-weight: 600;
margin-bottom: 20rpx;
}
.invalid-text {
color: #999999 !important;
}
.invalid-tip {
margin-top: 12rpx;
.tip-text {
font-size: 24rpx;
color: #999999;
}
}
// 数量控制区
.quantity-section {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
}
.quantity-control {
display: flex;
align-items: center;
background-color: #F5F5F5;
border-radius: 8rpx;
overflow: hidden;
}
.btn-minus,
.btn-plus {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #F5F5F5;
text {
font-size: 28rpx;
color: #333333;
}
&.disabled {
opacity: 0.4;
text {
color: #CCCCCC;
}
}
}
.quantity-num {
min-width: 60rpx;
text-align: center;
font-size: 28rpx;
color: #333333;
padding: 0 10rpx;
}
.btn-delete {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
}
.delete-icon {
width: 40rpx;
height: 40rpx;
opacity: 0.5;
}
// 失效商品区域
.invalid-section {
margin-top: 30rpx;
background-color: #FFFFFF;
border-radius: 16rpx;
overflow: hidden;
}
.invalid-header {
padding: 24rpx;
border-bottom: 1rpx solid #F0F0F0;
}
.invalid-title {
font-size: 28rpx;
color: #999999;
}
.invalid-list {
.cart-item {
position: relative;
padding-left: 80rpx;
&.invalid {
opacity: 0.7;
}
}
}
.invalid-badge {
position: absolute;
left: 24rpx;
top: 50%;
transform: translateY(-50%);
background-color: #EEEEEE;
border-radius: 4rpx;
padding: 4rpx 12rpx;
text {
font-size: 22rpx;
color: #999999;
}
}
.invalid-reason {
font-size: 24rpx;
color: #BBBBBB;
margin-top: 12rpx;
}
// 复选框禁用状态
.checkbox.disabled {
opacity: 0.5;
pointer-events: none;
}
// 空购物车
.empty-cart {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.empty-image {
width: 240rpx;
height: 240rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999999;
margin-bottom: 40rpx;
}
.btn-go-shop {
background: linear-gradient(90deg, #FF6900 0%, #F54900 100%);
color: #FFFFFF;
font-size: 28rpx;
padding: 20rpx 60rpx;
border-radius: 40rpx;
text {
color: #FFFFFF;
}
}
// 底部结算栏
.footer-bar {
position: fixed;
left: 0;
right: 0;
bottom: 100rpx;
background-color: #FFFFFF;
border-top: 1rpx solid #EEEEEE;
padding: 20rpx 30rpx;
z-index: 99;
}
.footer-top {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #F5F5F5;
margin-bottom: 16rpx;
}
.select-all {
display: flex;
align-items: center;
.checkbox {
margin-right: 12rpx;
margin-top: 0;
}
.select-text {
font-size: 28rpx;
color: #333333;
}
}
.selected-count {
font-size: 26rpx;
color: #666666;
}
.footer-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.points-info {
flex: 1;
}
.my-points {
font-size: 24rpx;
color: #999999;
display: block;
margin-bottom: 8rpx;
}
.total-points {
display: flex;
align-items: baseline;
.label {
font-size: 26rpx;
color: #333333;
}
.value {
font-size: 40rpx;
color: #FF6900;
font-weight: 600;
margin-left: 8rpx;
}
.unit {
font-size: 24rpx;
color: #FF6900;
margin-left: 4rpx;
}
}
.btn-checkout {
background: linear-gradient(90deg, #FF6900 0%, #F54900 100%);
color: #FFFFFF;
font-size: 28rpx;
padding: 20rpx 60rpx;
border-radius: 40rpx;
text {
color: #FFFFFF;
}
&.disabled {
background: #CCCCCC;
opacity: 0.8;
}
}
// 底部导航
.bottom-tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #FFFFFF;
display: flex;
border-top: 1px solid #EEEEEE;
z-index: 100;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4rpx;
position: relative;
&.active {
.tab-text {
color: #FF6900;
}
}
}
.tab-icon {
width: 44rpx;
height: 44rpx;
}
.tab-text {
font-size: 22rpx;
color: #999999;
}
.cart-badge {
position: absolute;
top: 4rpx;
right: 50%;
transform: translateX(30rpx);
min-width: 32rpx;
height: 32rpx;
background-color: #FF4D4F;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8rpx;
text {
font-size: 20rpx;
color: #FFFFFF;
font-weight: 500;
}
}
// 安全区域
.safe-area-inset-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
</style>