feat: 添加订单结算页面及相关功能

实现订单结算页面,包括:
1. 新增结算页面路由配置
2. 创建结算组件处理普通商品和租用商品的结算逻辑
3. 添加租用机柜容器组件
4. 更新购物车跳转逻辑指向新结算页面
5. 添加产品图片占位SVG
6. 更新迁移文档说明
This commit is contained in:
dzq 2025-11-03 09:02:26 +08:00
parent 80307e6992
commit cb445624e6
8 changed files with 963 additions and 6 deletions

View File

@ -5,7 +5,10 @@
"Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\api\\shop')", "Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\api\\shop')",
"Bash(tree 'E:\\code\\智柜宝\\wx\\src\\api' -I 'foo|goods|layout|login|me|order|system|types' -L 2)", "Bash(tree 'E:\\code\\智柜宝\\wx\\src\\api' -I 'foo|goods|layout|login|me|order|system|types' -L 2)",
"Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\pages\\home')", "Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\pages\\home')",
"Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\pages\\home\\components')" "Bash(mkdir -p 'E:\\code\\智柜宝\\wx\\src\\pages\\home\\components')",
"Bash(cp 'E:\\code\\智柜宝\\wx\\doc\\thirdParty\\src\\pages\\product\\components\\checkout.vue' 'E:\\code\\智柜宝\\wx\\src\\pages\\index\\components\\checkout.vue')",
"Bash(cp 'E:\\code\\智柜宝\\wx\\doc\\thirdParty\\src\\pages\\product\\components\\RentingCabinetContainer.vue' 'E:\\code\\智柜宝\\wx\\src\\pages\\index\\components\\renting-cabinet-container.vue')",
"Bash(test -f 'E:\\code\\智柜宝\\wx\\src\\pages\\index\\components\\product-container.vue')"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

10
doc/迁移指令.md Normal file
View File

@ -0,0 +1,10 @@
@doc\thirdParty\ 文件夹下是旧H5项目的代码现在已经将其api部分的代码移植到了本微信小程序项目。现在需要移植页面改为
微信小程序uni-app的写法第一步先移植首页 @doc\thirdParty\src\pages\product\ 内的页面和全部组件,小程序写法参考
@src\pages\index\index.vue
。仅参考语法api需要使用原ProductList.vue中已经移植到本项目的相应apistores也需要使用移植后的pinia。生成的代码写到
@src\pages\home\ 文件夹下
参考已迁移至本项目的代码 @src\pages\index\ 。将 @doc\thirdParty\src\pages\product\components\
下的另外两个component也迁移到本项目。注意thirdParty下的是H5项目现在需要改为微信小程序uni-app。api需要使用原Product
List.vue中已经移植到本项目的相应apistores也需要使用移植后的pinia。

View File

@ -61,6 +61,13 @@
}, },
"excludeLoginPath": false "excludeLoginPath": false
}, },
{
"path": "pages/index/checkout",
"type": "page",
"style": {
"navigationBarTitleText": "订单结算"
}
},
{ {
"path": "pages/login/faceLogin", "path": "pages/login/faceLogin",
"type": "page", "type": "page",

View File

@ -0,0 +1,495 @@
<script setup lang="ts">
import { useCartStore } from "@/pinia/stores/cart";
import { useWxStore } from "@/pinia/stores/wx";
import { useAb98UserStore } from '@/pinia/stores/ab98-user';
import { storeToRefs } from "pinia";
import { submitOrderApi } from "@/api/shop";
import type { SubmitOrderRequestData } from "@/api/shop/types";
import { useProductStore } from "@/pinia/stores/product";
import { useRentingCabinetStore } from "@/pinia/stores/rentingCabinet";
//
import { paymentMethodOptions, modeToPaymentMethodMap } from "@/utils/maps/payment";
const cartStore = useCartStore();
const { cartItems, totalPrice } = storeToRefs(cartStore);
// rentingCabinet store
const rentingCabinetStore = useRentingCabinetStore();
const { rentingCartItems, rentingCartTotalPrice } = storeToRefs(rentingCabinetStore);
const wxStore = useWxStore();
const { openid, balance, corpidLogin, userid: qyUserid, name: qyName } = storeToRefs(wxStore);
const ab98UserStore = useAb98UserStore();
const { tel, userid: ab98Userid, name } = storeToRefs(ab98UserStore);
const productStore = useProductStore();
// (mode 3)
const isRentingMode = computed(() => productStore.selectedShop?.mode === 3);
const currentTotalPrice = computed(() => {
return isRentingMode.value ? rentingCartTotalPrice.value : totalPrice.value;
});
//
const supportedPayments = computed(() => {
const shopMode = productStore.selectedShop?.mode || 0;
const allowedValues = modeToPaymentMethodMap[shopMode] || [];
//
// 1.
// 2. (0)
return paymentMethodOptions.filter(option => allowedValues.includes(option.value)
&& (option.value !== 0 || !corpidLogin.value));
});
//
const selectedPayment = ref<string>(supportedPayments.value[0]?.value.toString() || '0');
const applyRemark = ref("");
const submitting = ref(false);
//
const isApproval = computed(() => {
// (belongType == 1)
//
if (isRentingMode.value) {
return false; //
} else {
return cartItems.value.some(item => item.product.belongType == 1);
}
});
// value
const paymentValueToType: Record<string, 'wechat' | 'balance' | 'approval'> = {
'0': 'wechat',
'1': 'balance',
'2': 'approval',
'3': 'balance'
};
// uni-app
async function callUniPay(paymentInfo: any) {
return new Promise((resolve, reject) => {
// uni-appAPI
/* uni.requestPayment({
provider: 'wxpay',
timeStamp: paymentInfo.timeStamp,
nonceStr: paymentInfo.nonceStr,
package: paymentInfo.package.startsWith('prepay_id=')
? paymentInfo.package
: `prepay_id=${paymentInfo.package}`,
signType: paymentInfo.signType,
paySign: paymentInfo.paySign,
success: () => {
resolve(true);
},
fail: (err) => {
reject(new Error(err.errMsg || '支付未完成'));
}
}); */
});
}
//
function showToast(title: string) {
uni.showToast({
title,
icon: 'none'
});
}
async function handleSubmit() {
if (isRentingMode.value) {
if (!rentingCartItems.value.length) {
return showToast('请先选择租用商品后再结算');
}
} else {
if (!cartItems.value.length) {
return showToast('请先选择商品后再结算');
}
}
//
if (supportedPayments.value.length === 0) {
return showToast('没有支持的支付方法,请从微信进入智借还');
}
if (!openid.value && !wxStore.isFakeQyLogin) {
return showToast('请从微信中打开');
}
//
if (!/^1[3-9]\d{9}$/.test(tel.value)) {
return showToast('请输入有效的手机号码');
}
submitting.value = true;
try {
//
// 2 -
// 1 -
// 0 -
const isInternal = corpidLogin.value ? 2 : ab98Userid.value ? 1 : 0;
// goodsList
let goodsListToSend;
if (isRentingMode.value) {
goodsListToSend = rentingCartItems.value.map(item => ({
quantity: item.quantity,
cellId: item.cabinetCell.cellId, // cellId
mode: productStore.selectedShop?.mode || 0,
}));
} else {
goodsListToSend = cartItems.value.map(item => ({
goodsId: item.product.id,
quantity: item.quantity,
cellId: item.product.cellId,
mode: productStore.selectedShop?.mode || 0,
}));
}
const requestData: SubmitOrderRequestData = {
openid: openid.value,
userid: wxStore.userid,
corpid: wxStore.corpid,
goodsList: goodsListToSend,
// value
paymentType: paymentValueToType[selectedPayment.value] || 'wechat',
mobile: tel.value,
name: isInternal === 2 ? qyName.value : name.value,
applyRemark: applyRemark.value,
qyUserid: wxStore.userid,
mode: productStore.selectedShop?.mode || 0,
isInternal: isInternal
};
const { code, data } = await submitOrderApi(requestData);
if (code !== 0) {
throw new Error("订单提交失败");
}
if (selectedPayment.value == '0') { //
if (data.paymentInfo) {
await callUniPay(data.paymentInfo);
}
} else if (selectedPayment.value == '1' || selectedPayment.value == '3') { //
wxStore.setBalance(data.newBalance || 0);
showToast(`${selectedPayment.value === '1' ? '借呗' : '余额'}支付成功,剩余余额:¥${data.newBalance?.toFixed(2)}`);
} else if (selectedPayment.value == '2') { // ()
showToast('提交领用申请成功,请等待管理员审批');
}
//
uni.navigateTo({
url: `/pages/index/order-success?orderId=${data.orderId}`
});
//
if (isRentingMode.value) {
rentingCabinetStore.clearRentingCart();
} else {
cartStore.clearCart();
}
} catch (error) {
if (error !== 'user_cancel') {
showToast(error instanceof Error ? error.message : "支付流程中断");
}
} finally {
submitting.value = false;
}
}
</script>
<template>
<view class="checkout-container">
<!-- <uni-nav-bar title="结算页面" left-text="返回" left-arrow @clickLeft="$router.go(-1)" /> -->
<view class="content-wrapper">
<!-- 根据 isRentingMode 动态渲染列表 -->
<view class="product-list">
<view v-if="isRentingMode">
<view v-for="item in rentingCartItems" :key="item.cabinetCell.cellId" class="product-item">
<view class="product-info-wrapper">
<view class="product-image-wrapper">
<image src="/static/svg/product-image.svg" class="product-image" />
</view>
<view class="product-info">
<view class="product-name">
{{ `格口号 ${item.cabinetCell.cellNo}` }}
</view>
<view class="price-row">
<text class="product-price">
¥{{ item.cabinetCell.cellPrice?.toFixed(2) }}
</text>
<text class="quantity">
×{{ item.quantity }}
</text>
</view>
</view>
</view>
</view>
</view>
<view v-else>
<view v-for="item in cartItems" :key="item.product.id" class="product-item">
<view class="product-info-wrapper">
<view class="product-image-wrapper">
<image :src="item.product.image" class="product-image" />
</view>
<view class="product-info">
<view class="product-name">
{{ item.product.name }}
</view>
<view class="price-row">
<text class="product-price">
¥{{ item.product.price.toFixed(2) }}
</text>
<text class="quantity">
×{{ item.quantity }}
</text>
</view>
</view>
</view>
<view v-if="item.product.usageInstruction" class="usage-instruction">
<view class="instruction-content">
使用说明{{ item.product.usageInstruction }}
</view>
</view>
</view>
</view>
<view class="tel-input-wrapper">
<view class="tel-label">
<text class="required-star">*</text>
<text class="label-text">手机号</text>
</view>
<input v-model="tel" placeholder="请输入手机号" class="tel-input" type="number" />
</view>
</view>
<!-- 支付方式 -->
<view v-if="!isApproval" class="contact-form">
<view v-for="method in supportedPayments" :key="method.value"
:class="['payment-option', { selected: selectedPayment === method.value.toString(), disabled: method.value === 1 && (balance < currentTotalPrice) }]"
@click="selectedPayment = method.value.toString()">
<text class="method-label">
{{ method.label }}
<text v-if="method.value === 1" class="balance-amount">当前¥{{ balance.toFixed(2) }}</text>
</text>
<view class="empty"></view>
<text v-if="selectedPayment === method.value.toString()" class="check-icon"></text>
</view>
</view>
<!-- 提交订单栏 -->
<view class="submit-bar">
<view class="total-price">
合计¥{{ currentTotalPrice.toFixed(2) }}
</view>
<button type="primary" size="default" :loading="submitting" loading-text="提交中..."
class="submit-button" @click="handleSubmit">
提交订单
</button>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.checkout-container {
padding: 12px 16px 80px;
background: #f7f8fa;
min-height: 100vh;
}
.content-wrapper {
padding-top: 0px;
}
.product-list {
margin-bottom: 20px;
background: #fff;
border-radius: 8px;
overflow: hidden;
}
.product-item {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.product-info-wrapper {
display: flex;
align-items: flex-start;
}
.product-image-wrapper {
margin-right: 12px;
}
.product-image {
width: 60px;
height: 60px;
border-radius: 4px;
}
.product-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 60px;
flex: 1;
}
.product-name {
font-size: 14px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
color: #e95d5d;
font-weight: bold;
font-size: 14px;
}
.quantity {
color: #666;
font-size: 13px;
}
.usage-instruction {
margin-top: 8px;
padding: 8px;
background: #f9f9f9;
border-radius: 4px;
}
.instruction-content {
font-size: 12px;
color: #666;
line-height: 1.5;
}
.tel-input-wrapper {
padding: 16px;
display: flex;
}
.tel-label {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.required-star {
color: #e95d5d;
margin-right: 2px;
font-size: 14px;
}
.label-text {
font-size: 14px;
color: #333;
}
.tel-input {
background-color: #f5f5f5;
margin-left: 12px;
border-radius: 4px;
padding: 8px 12px;
font-size: 14px;
}
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 10px 16px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
z-index: 999;
}
.total-price {
font-size: 16px;
color: #e95d5d;
font-weight: bold;
}
.submit-button {
background-color: #e95d5d;
border: none;
border-radius: 8px;
margin-right: 0;
padding: 0 24px;
height: 40px;
line-height: 40px;
font-size: 16px;
}
.contact-form {
background: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 20px;
}
.payment-option {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 8px 0;
padding: 12px;
border: 1px solid #ebedf0;
border-radius: 8px;
transition: all 0.2s;
}
.payment-option.selected {
border-color: #07c160;
background-color: #f6ffed;
}
.payment-option.disabled {
opacity: 0.6;
filter: grayscale(1);
}
.method-label {
font-size: 15px;
color: #333;
}
.balance-amount {
font-size: 13px;
color: #666;
margin-left: 8px;
}
.empty {
flex-grow: 1;
margin-right: 20px;
}
.check-icon {
color: #07c160;
font-size: 18px;
font-weight: bold;
}
</style>

View File

@ -68,9 +68,10 @@ function handleClearCartOrRentingCart() {
// //
function handleCheckout() { function handleCheckout() {
handleClose() handleClose();
// emit uni.navigateTo({
// router.push("/product/checkout") url: '/pages/index/checkout'
});
} }
</script> </script>

View File

@ -0,0 +1,435 @@
<script setup lang="ts">
import { useRentingCabinetStore } from "@/pinia/stores/rentingCabinet"
import { storeToRefs } from "pinia"
import { computed, onMounted, ref } from "vue"
// Props
const props = defineProps<{
shopId: number; // ID
}>();
// Emit
const emit = defineEmits<{
(e: 'backToShopList'): void;
(e: 'checkoutRenting'): void; //
}>();
//
const rentingCabinetStore = useRentingCabinetStore();
const { rentingCabinets, rentingCartItems, rentingCartTotalQuantity } = storeToRefs(rentingCabinetStore);
// props
const activeCategory = ref(0); // cabinetName
//
const showCartPopup = ref(false);
const searchQuery = ref('');
//
function handleCategoryClick(index: number) {
activeCategory.value = index;
}
// URL
const PLACEHOLDER_IMAGE_URL = '/static/svg/product-image.svg';
//
function handleAddToCart(cabinetCell: any) {
// 1
const existingItem = rentingCartItems.value.find(item => item.cabinetCell.cellId === cabinetCell.cellId);
if (!existingItem) {
rentingCabinetStore.addToRentingCart(cabinetCell, 1);
}
}
//
function handleRemoveFromCart(cellId: number) {
rentingCabinetStore.removeFromRentingCart(cellId, 1);
}
// 01
function getRentingCartItemCount(cellId: number) {
const item = rentingCartItems.value.find(item => item.cabinetCell.cellId === cellId);
return item ? item.quantity : 0;
}
//
const filteredRentingCells = computed(() => {
let cells: any[] = [];
//
if (rentingCabinets.value.length > 0) {
// Flattern all cells from all cabinets
cells = rentingCabinets.value.flatMap(cabinet => cabinet.cells);
}
// (isRented = 1) (usageStatus = 2)
const availableCells = cells.filter(cell => cell.isRented === 0 && cell.usageStatus === 1);
//
if (!searchQuery.value) {
return availableCells;
}
// (cellNo)
return availableCells.filter(cell =>
String(cell.cellNo).includes(searchQuery.value) ||
(cell.cabinetName && cell.cabinetName.toLowerCase().includes(searchQuery.value.toLowerCase()))
);
});
//
onMounted(() => {
rentingCabinetStore.fetchRentingCabinetDetail(props.shopId);
});
//
function handleCheckout() {
emit('checkoutRenting');
}
//
function showCartDetail() {
showCartPopup.value = true;
}
</script>
<template>
<view class="product-container">
<!-- 左侧分类导航 -->
<view class="category-nav-wrapper">
<button type="default" class="showShopListBtn" @click="emit('backToShopList')">重选地址</button>
<scroll-view scroll-y class="category-nav">
<view v-for="(cabinet, index) in rentingCabinets" :key="cabinet.cabinetId" :class="['category-item', { active: activeCategory === index }]" @click="handleCategoryClick(index)">
{{ cabinet.cabinetName }}
</view>
</scroll-view>
</view>
<!-- 右侧可租用格口列表 -->
<view class="product-list">
<view class="search-wrapper">
<input v-model="searchQuery" placeholder="搜索格口号或柜机名称" class="search-box" />
</view>
<scroll-view scroll-y class="category-section">
<view v-for="cell in filteredRentingCells" :key="cell.cellId" class="product-item">
<view class="product-info-wrapper">
<view class="product-image-wrapper">
<image :src="PLACEHOLDER_IMAGE_URL" class="product-image">
<!-- 已被占用或故障的格口显示 '不可租用' -->
<view v-if="cell.isRented === 1 || cell.usageStatus === 2 || cell.availableStatus === 2" class="sold-out-overlay">
<text class="sold-out-text">不可租用</text>
</view>
</image>
</view>
<view class="product-info">
<view class="product-name">
格口号: {{ cell.cellNo }}
</view>
<view class="product-price">
¥{{ (cell.cellPrice || 0).toFixed(2) }}
</view>
<view class="action-row">
<view v-if="cell.isRented === 0 && cell.usageStatus === 1 && cell.availableStatus === 1" class="stock-count">
可租用
</view>
<view class="cart-actions">
<!-- 数量减按钮如果已在购物车则显示 -->
<button v-if="getRentingCartItemCount(cell.cellId) > 0" size="mini" class="cart-btn minus-btn" @click.stop="handleRemoveFromCart(cell.cellId)">-</button>
<!-- 数量显示0或1 -->
<text v-if="getRentingCartItemCount(cell.cellId) > 0" class="cart-count">{{ getRentingCartItemCount(cell.cellId) }}</text>
<!-- 数量加按钮如果未在购物车且可租用则显示 -->
<button size="mini" type="primary" class="add-cart-btn"
:disabled="getRentingCartItemCount(cell.cellId) > 0 || cell.isRented === 1 || cell.usageStatus === 2 || cell.availableStatus === 2"
@click.stop="handleAddToCart(cell)"
>
+
</button>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 底部购物车栏 -->
<view v-if="rentingCartItems.length" class="shopping-cart-bar" @click="showCartDetail">
<view class="cart-info">
<view class="badge-wrapper">
<text class="cart-icon">🛒</text>
<view class="badge">{{ rentingCartTotalQuantity }}</view>
</view>
<view class="total-price">
已选格口数{{ rentingCartTotalQuantity }}
</view>
</view>
<button type="primary" size="default" @click.stop="handleCheckout" class="checkout-btn">
去结算
</button>
</view>
</view>
</template>
<style scoped lang="scss">
.product-container {
display: flex;
height: 100vh;
background: #f7f8fa;
position: relative;
overflow: hidden;
}
.category-nav-wrapper {
width: 100px;
flex-shrink: 0;
background: #fff;
display: flex;
flex-direction: column;
border-right: 1px solid #e0e0e0;
}
.showShopListBtn {
width: 100%;
padding: 12px;
border: none;
border-bottom: 1px solid #e0e0e0;
background: #fff;
font-size: 14px;
color: #333;
}
.category-nav {
flex: 1;
overflow-y: auto;
}
.category-item {
padding: 12px 8px;
text-align: center;
font-size: 13px;
color: #666;
border-bottom: 1px solid #f0f0f0;
}
.category-item.active {
background: #f7f8fa;
color: #e95d5d;
font-weight: bold;
}
.product-list {
flex: 1;
display: flex;
flex-direction: column;
background: #ffffff;
}
.search-wrapper {
padding: 10px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
}
.search-box {
width: 100%;
padding: 8px 12px;
background: #f5f5f5;
border-radius: 20px;
font-size: 14px;
text-align: center;
}
.category-section {
flex: 1;
overflow-y: auto;
padding: 10px;
}
.product-item {
margin-bottom: 10px;
padding: 12px;
background: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.product-info-wrapper {
display: flex;
}
.product-image-wrapper {
position: relative;
margin-right: 12px;
}
.product-image {
width: 80px;
height: 80px;
border-radius: 4px;
background: #f5f5f5;
}
.sold-out-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.sold-out-text {
color: #999;
font-size: 14px;
transform: rotate(-15deg);
border: 1px solid #eee;
padding: 2px 8px;
border-radius: 4px;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
}
.product-name {
font-size: 14px;
color: #333;
line-height: 1.4;
margin-bottom: 8px;
}
.product-price {
font-size: 16px;
color: #e95d5d;
font-weight: bold;
margin-bottom: 8px;
}
.action-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: auto;
}
.stock-count {
font-size: 11px;
color: #bbbbbb;
margin-right: 8px;
}
.cart-actions {
display: flex;
align-items: center;
gap: 4px;
margin-left: auto;
.cart-count {
font-size: 12px;
min-width: 20px;
text-align: center;
color: #333;
}
button {
min-width: 24px;
height: 24px;
line-height: 24px;
padding: 0;
border-radius: 12px;
font-size: 16px;
font-weight: bold;
}
.minus-btn {
background: #fff;
color: #e95d5d;
border: 1px solid #e95d5d;
}
.add-cart-btn {
background: #e95d5d;
border: none;
color: #fff;
}
button[disabled] {
background: #ccc !important;
border-color: #ccc !important;
}
}
//
.product-item:has(.sold-out-overlay) {
opacity: 0.6;
}
/* 修改购物车栏样式 */
.shopping-cart-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
z-index: 100;
}
.cart-info {
display: flex;
align-items: center;
gap: 12px;
}
.badge-wrapper {
position: relative;
}
.cart-icon {
font-size: 24px;
}
.badge {
position: absolute;
top: -6px;
right: -8px;
background: #e95d5d;
color: #fff;
border-radius: 10px;
padding: 0 6px;
font-size: 10px;
line-height: 18px;
min-width: 18px;
text-align: center;
}
.total-price {
font-size: 14px;
color: #333;
font-weight: bold;
}
.checkout-btn {
background-color: #e95d5d;
border: none;
border-radius: 16px;
padding: 0 24px;
height: 32px;
line-height: 32px;
font-size: 14px;
color: #fff;
}
</style>

View File

@ -34,7 +34,7 @@ onMounted(async () => {
if (res?.code === 0 && res?.data?.length > 0) { if (res?.code === 0 && res?.data?.length > 0) {
shopList.value = res.data; shopList.value = res.data;
shopList.value = [...shopList.value, ...res.data, ...res.data, ...res.data]; // shopList.value = [...shopList.value, ...res.data, ...res.data, ...res.data];
} }
} catch (error) { } catch (error) {
console.error('获取店铺列表失败:', error) console.error('获取店铺列表失败:', error)
@ -70,7 +70,7 @@ function backToShopList() {
// //
function handleCheckout() { function handleCheckout() {
uni.navigateTo({ uni.navigateTo({
url: '/pages/index/shopping-cart/index' url: '/pages/index/checkout'
}) })
} }
</script> </script>

View File

@ -0,0 +1,6 @@
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="5" width="90" height="90" rx="10" fill="#E8F5E9" stroke="#81C784"
stroke-width="2" />
<text x="50" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
fill="#2E7D32" text-anchor="middle">空闲</text>
</svg>

After

Width:  |  Height:  |  Size: 473 B