feat(M09-D4): 完成商品选购与运营工作台

This commit is contained in:
Codex
2026-08-11 08:06:13 +08:00
parent 60264cbd11
commit e91b979128
32 changed files with 1065 additions and 60 deletions
+1
View File
@@ -2,6 +2,7 @@
"pages": [
"pages/index/index",
"pages/store/detail",
"pages/goods/index",
"pages/room/detail",
"pages/orders/list",
"pages/orders/detail",
+323
View File
@@ -0,0 +1,323 @@
const { request, ensureLogin, cents, clientRequestId } = require('../../utils/api.js')
const ACTIVE_STORAGE = ['STORED', 'PARTIALLY_RETRIEVED']
Page({
data: {
storeId: '', activeTab: 'catalog', loading: false, submitting: false,
errorMessage: '', catalog: { salesOpen: false, categories: [] },
cartItems: [], cartQuantity: 0, cartTotalText: cents(0),
orders: [], storages: [], selectedStorage: null,
oneTimeCredential: '', oneTimeCredentialTitle: '',
},
onLoad(options) {
this.setData({
storeId: options.storeId || '',
activeTab: ['catalog', 'orders', 'storages'].includes(options.tab) ? options.tab : 'catalog',
})
this.loadAll()
},
onShow() {
if (this.data.orders.length || this.data.storages.length) this.loadBusinessLists()
},
async loadAll() {
this.setData({ loading: true, errorMessage: '' })
try {
await ensureLogin()
const jobs = [
request('/product-orders?page=1&pageSize=50'),
request('/product-storages?page=1&pageSize=50'),
]
if (this.data.storeId) {
jobs.push(request(`/stores/${encodeURIComponent(this.data.storeId)}/product-catalog`))
}
const [ordersResponse, storagesResponse, catalogResponse] = await Promise.all(jobs)
this.setData({
orders: (ordersResponse.data.items || []).map(formatOrder),
storages: (storagesResponse.data.items || []).map(formatStorage),
catalog: catalogResponse ? formatCatalog(catalogResponse.data) : this.data.catalog,
})
} catch (error) {
this.setData({ errorMessage: error.message || '商品服务加载失败' })
} finally {
this.setData({ loading: false })
}
},
async loadBusinessLists() {
try {
const [ordersResponse, storagesResponse] = await Promise.all([
request('/product-orders?page=1&pageSize=50'),
request('/product-storages?page=1&pageSize=50'),
])
this.setData({
orders: (ordersResponse.data.items || []).map(formatOrder),
storages: (storagesResponse.data.items || []).map(formatStorage),
})
} catch (error) {
this.setData({ errorMessage: error.message || '订单与寄存刷新失败' })
}
},
changeTab(event) {
this.setData({ activeTab: event.currentTarget.dataset.tab, selectedStorage: null })
},
changeQuantity(event) {
const skuId = event.currentTarget.dataset.skuId
const delta = Number(event.currentTarget.dataset.delta || 0)
const categories = this.data.catalog.categories.map((category) => ({
...category,
products: category.products.map((product) => ({
...product,
skus: product.skus.map((sku) => sku.id === skuId
? { ...sku, selectedQuantity: Math.max(0, Math.min(99, sku.selectedQuantity + delta)) }
: sku),
})),
}))
this.setData({ 'catalog.categories': categories })
this.rebuildCart(categories)
},
rebuildCart(categories) {
const cartItems = []
categories.forEach((category) => category.products.forEach((product) => {
product.skus.forEach((sku) => {
if (sku.selectedQuantity > 0) cartItems.push({
skuId: sku.id, name: `${product.name} · ${sku.name}`,
quantity: sku.selectedQuantity, salePriceCents: sku.salePriceCents,
})
})
}))
const cartQuantity = cartItems.reduce((sum, item) => sum + item.quantity, 0)
const total = cartItems.reduce((sum, item) => sum + item.quantity * item.salePriceCents, 0)
this.setData({ cartItems, cartQuantity, cartTotalText: cents(total) })
},
async createOrder() {
if (!this.data.storeId || !this.data.cartItems.length || !this.data.catalog.salesOpen) return
await this.run(async () => {
const response = await request('/product-orders', {
method: 'POST',
data: {
storeId: this.data.storeId, requestId: clientRequestId('goods-order'),
fulfillmentMode: 'SELF_SERVICE', roomOrderId: null,
note: '顾客小程序自助商品订单',
items: this.data.cartItems.map((item) => ({
skuId: item.skuId, quantity: item.quantity, note: '',
})),
},
})
wx.showToast({ title: '订单已创建', icon: 'success' })
this.clearCart()
this.setData({ activeTab: 'orders' })
await this.loadBusinessLists()
if (response.data.status === 'PENDING_PAYMENT') wx.showModal({
title: '等待支付',
content: '金额已由服务端复算并锁定库存。生产支付入口启用后可继续支付;超时未支付会自动释放库存。',
showCancel: false,
})
}, '商品订单创建失败')
},
clearCart() {
const categories = this.data.catalog.categories.map((category) => ({
...category,
products: category.products.map((product) => ({
...product,
skus: product.skus.map((sku) => ({ ...sku, selectedQuantity: 0 })),
})),
}))
this.setData({
'catalog.categories': categories,
cartItems: [], cartQuantity: 0, cartTotalText: cents(0),
})
},
async cancelOrder(event) {
const order = this.data.orders.find((item) => item.id === event.currentTarget.dataset.orderId)
if (!order || !(await confirm(`取消商品订单 ${order.orderNo}?库存与退款会按当前状态处理。`))) return
await this.run(async () => {
await request(`/product-orders/${encodeURIComponent(order.id)}/cancel`, {
method: 'POST',
data: { requestId: clientRequestId('goods-cancel'), reason: '顾客小程序取消商品订单' },
})
wx.showToast({ title: '订单已取消', icon: 'success' })
await this.loadBusinessLists()
}, '商品订单取消失败')
},
async storeOrder(event) {
const orderId = event.currentTarget.dataset.orderId
if (!(await confirm('将此已完成商品订单全部转为寄存?领取码只会显示一次。'))) return
await this.run(async () => {
const response = await request('/product-storages', {
method: 'POST',
data: {
requestId: clientRequestId('goods-storage'), sourceOrderId: orderId,
expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
},
})
this.showCredential(response.data.claimCredential, '寄存已创建')
this.setData({ activeTab: 'storages' })
await this.loadBusinessLists()
}, '创建寄存失败')
},
async openStorage(event) {
await this.run(async () => {
const response = await request(`/product-storages/${encodeURIComponent(event.currentTarget.dataset.storageId)}`)
this.setData({ selectedStorage: formatStorageDetail(response.data) })
}, '寄存详情加载失败')
},
closeStorage() { this.setData({ selectedStorage: null }) },
async retrieveItem(event) {
const storage = this.data.selectedStorage
const storageItemId = event.currentTarget.dataset.itemId
const item = storage?.items.find((entry) => entry.id === storageItemId)
if (!storage || !item || item.remainingQuantity < 1) return
const credential = await prompt('领取码核验', '请输入当前一次性领取码')
if (!credential) return
const quantity = Number(await prompt(
'取出数量', `当前剩余 ${item.remainingQuantity},请输入本次取出数量`, '1'
))
if (!Number.isInteger(quantity) || quantity < 1 || quantity > item.remainingQuantity) {
this.setData({ errorMessage: '取出数量必须是未超过剩余量的正整数' })
return
}
await this.run(async () => {
const response = await request(`/product-storages/${encodeURIComponent(storage.id)}/retrieve`, {
method: 'POST',
data: {
requestId: clientRequestId('goods-retrieve'), claimCredential: credential,
items: [{ storageItemId, quantity }],
},
})
if (response.data.nextClaimCredential) {
this.showCredential(response.data.nextClaimCredential, '部分取出成功,旧码已失效')
} else wx.showToast({ title: '取出成功', icon: 'success' })
this.setData({ selectedStorage: formatStorageDetail(response.data) })
await this.loadBusinessLists()
}, '商品取出失败')
},
async rotateCredential() {
const storage = this.data.selectedStorage
if (!storage || !ACTIVE_STORAGE.includes(storage.status)
|| !(await confirm('轮换后当前领取码立即失效,新码只显示一次。继续吗?'))) return
await this.run(async () => {
const response = await request(`/product-storages/${encodeURIComponent(storage.id)}/credential/rotate`, {
method: 'POST', data: { requestId: clientRequestId('goods-rotate') },
})
this.showCredential(response.data.claimCredential, '领取码已轮换')
this.setData({ selectedStorage: formatStorageDetail(response.data) })
await this.loadBusinessLists()
}, '领取码轮换失败')
},
async cancelStorage() {
const storage = this.data.selectedStorage
if (!storage || !ACTIVE_STORAGE.includes(storage.status)) return
const reason = await prompt('取消寄存', '取消后不能继续领取,剩余商品不会自动回库存。请输入原因。')
if (!reason) return
await this.run(async () => {
const response = await request(`/product-storages/${encodeURIComponent(storage.id)}/cancel`, {
method: 'POST', data: { requestId: clientRequestId('goods-storage-cancel'), reason },
})
this.setData({ selectedStorage: formatStorageDetail(response.data) })
await this.loadBusinessLists()
wx.showToast({ title: '寄存已取消', icon: 'success' })
}, '取消寄存失败')
},
showCredential(value, title) {
if (value) this.setData({ oneTimeCredential: value, oneTimeCredentialTitle: title })
},
copyCredential() {
if (this.data.oneTimeCredential) wx.setClipboardData({ data: this.data.oneTimeCredential })
},
clearCredential() { this.setData({ oneTimeCredential: '', oneTimeCredentialTitle: '' }) },
async run(work, fallback) {
this.setData({ submitting: true, errorMessage: '' })
try { await work() } catch (error) {
this.setData({ errorMessage: error.message || fallback })
} finally { this.setData({ submitting: false }) }
},
})
function formatCatalog(catalog) {
return {
...catalog,
categories: (catalog.categories || []).map((category) => ({
...category,
products: (category.products || []).map((product) => ({
...product,
skus: (product.skus || []).map((sku) => ({
...sku, priceText: cents(sku.salePriceCents), selectedQuantity: 0,
})),
})),
})),
}
}
function formatOrder(order) {
const labels = {
PENDING_PAYMENT: '待支付', PAID: '已支付', ACCEPTED: '已接单', DELIVERING: '配送中',
READY_FOR_SELF_SERVICE: '待自取', COMPLETED: '已完成', CANCELLED: '已取消',
REFUNDING: '退款中', REFUNDED: '已退款', REFUND_FAILED: '退款失败',
}
return {
...order, statusText: labels[order.status] || order.status,
amountText: cents(order.totalAmountCents), createdText: formatDate(order.createdAt),
canCancel: ['PENDING_PAYMENT', 'PAID', 'ACCEPTED'].includes(order.status),
canStore: order.status === 'COMPLETED',
}
}
function formatStorage(storage) {
const labels = {
STORED: '已寄存', PARTIALLY_RETRIEVED: '部分取出', RETRIEVED: '已取完',
EXPIRED: '已过期', CANCELLED: '已取消',
}
return {
...storage, statusText: labels[storage.status] || storage.status,
expiresText: formatDate(storage.expiresAt), active: ACTIVE_STORAGE.includes(storage.status),
}
}
function formatStorageDetail(storage) {
return {
...formatStorage(storage), items: (storage.items || []).map((item) => ({ ...item })),
movements: (storage.movements || []).map((item) => ({
...item, createdText: formatDate(item.createdAt),
})),
}
}
function formatDate(value) {
if (!value) return '-'
const date = new Date(value)
const pad = (input) => String(input).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
}
function confirm(content) {
return new Promise((resolve) => wx.showModal({
title: '请确认', content,
success: (result) => resolve(Boolean(result.confirm)), fail: () => resolve(false),
}))
}
function prompt(title, placeholderText, defaultValue = '') {
return new Promise((resolve) => wx.showModal({
title, placeholderText, editable: true, content: defaultValue,
success: (result) => resolve(result.confirm ? String(result.content || '').trim() : ''),
fail: () => resolve(''),
}))
}
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "商品与寄存"
}
+118
View File
@@ -0,0 +1,118 @@
<scroll-view class="scrollarea" scroll-y type="list">
<view class="page">
<view class="hero">
<view>
<view class="eyebrow">M09 · 门店增值服务</view>
<view class="title">商品与寄存</view>
</view>
<view class="hero-store">门店 #{{storeId || '-'}}</view>
</view>
<view class="tabs">
<view class="tab {{activeTab === 'catalog' ? 'active' : ''}}" data-tab="catalog" bindtap="changeTab">选购</view>
<view class="tab {{activeTab === 'orders' ? 'active' : ''}}" data-tab="orders" bindtap="changeTab">商品订单</view>
<view class="tab {{activeTab === 'storages' ? 'active' : ''}}" data-tab="storages" bindtap="changeTab">我的寄存</view>
</view>
<view wx:if="{{errorMessage}}" class="error">{{errorMessage}}</view>
<view wx:if="{{loading}}" class="notice">正在加载商品服务…</view>
<view wx:if="{{oneTimeCredential}}" class="credential-card">
<view class="credential-title">{{oneTimeCredentialTitle}}</view>
<view class="credential-warning">领取码仅展示本次,不会写入本地缓存。请立即妥善保存。</view>
<view class="credential-value">{{oneTimeCredential}}</view>
<view class="row-actions">
<button class="button primary compact" bindtap="copyCredential">复制领取码</button>
<button class="button ghost compact" bindtap="clearCredential">我已保存并隐藏</button>
</view>
</view>
<block wx:if="{{activeTab === 'catalog'}}">
<view wx:if="{{!storeId}}" class="notice">请从门店详情进入商品页,以载入对应门店的商品目录。</view>
<view wx:elif="{{!catalog.salesOpen}}" class="notice warning">当前不在商品售卖时段,可查看目录,暂不能提交订单。</view>
<block wx:for="{{catalog.categories}}" wx:key="id" wx:for-item="category">
<view class="section-title">{{category.name}}</view>
<view wx:for="{{category.products}}" wx:key="id" wx:for-item="product" class="product-card">
<image wx:if="{{product.coverUrl}}" class="product-cover" src="{{product.coverUrl}}" mode="aspectFill" />
<view class="product-main">
<view class="product-head">
<view>
<view class="product-name">{{product.name}}</view>
<view class="meta">{{product.unitName}} · {{product.fulfillmentMode === 'DELIVERY' ? '可配送' : '门店自取'}}</view>
</view>
<view wx:if="{{product.storageEnabled}}" class="chip">支持寄存</view>
</view>
<view wx:if="{{product.description}}" class="description">{{product.description}}</view>
<view wx:for="{{product.skus}}" wx:key="id" wx:for-item="sku" class="sku-row">
<view>
<view class="sku-name">{{sku.name}}</view>
<view class="price">{{sku.priceText}}</view>
</view>
<view class="quantity-control">
<button class="quantity-button" data-sku-id="{{sku.id}}" data-delta="-1" bindtap="changeQuantity"></button>
<text>{{sku.selectedQuantity}}</text>
<button class="quantity-button plus" data-sku-id="{{sku.id}}" data-delta="1" bindtap="changeQuantity"></button>
</view>
</view>
</view>
</view>
</block>
<view wx:if="{{storeId && !loading && !catalog.categories.length}}" class="empty">当前门店暂无在售商品</view>
<view wx:if="{{cartQuantity > 0}}" class="cart-bar">
<view><view class="cart-count">已选 {{cartQuantity}} 件</view><view class="cart-total">{{cartTotalText}}</view></view>
<button class="button primary cart-submit" loading="{{submitting}}" disabled="{{submitting || !catalog.salesOpen}}" bindtap="createOrder">提交订单</button>
</view>
</block>
<block wx:if="{{activeTab === 'orders'}}">
<view wx:for="{{orders}}" wx:key="id" wx:for-item="order" class="business-card">
<view class="card-head">
<view><view class="card-title">{{order.orderNo}}</view><view class="meta">{{order.createdText}}</view></view>
<view class="status">{{order.statusText}}</view>
</view>
<view class="summary-row"><text>{{order.totalQuantity}} 件 · {{order.fulfillmentMode === 'DELIVERY' ? '配送' : '门店自取'}}</text><strong>{{order.amountText}}</strong></view>
<view class="row-actions">
<button wx:if="{{order.canCancel}}" class="button ghost compact" data-order-id="{{order.id}}" bindtap="cancelOrder">取消订单</button>
<button wx:if="{{order.canStore}}" class="button primary compact" data-order-id="{{order.id}}" bindtap="storeOrder">整单寄存</button>
</view>
</view>
<view wx:if="{{!loading && !orders.length}}" class="empty">暂无商品订单</view>
</block>
<block wx:if="{{activeTab === 'storages'}}">
<view wx:if="{{!selectedStorage}}">
<view wx:for="{{storages}}" wx:key="id" wx:for-item="storage" class="business-card tappable" data-storage-id="{{storage.id}}" bindtap="openStorage">
<view class="card-head">
<view><view class="card-title">{{storage.storageNo}}</view><view class="meta">到期 {{storage.expiresText}}</view></view>
<view class="status">{{storage.statusText}}</view>
</view>
<view class="summary-row"><text>剩余 / 总数</text><strong>{{storage.remainingQuantity}} / {{storage.totalQuantity}}</strong></view>
<view class="detail-link">查看寄存明细与取出记录 </view>
</view>
<view wx:if="{{!loading && !storages.length}}" class="empty">暂无商品寄存</view>
</view>
<view wx:if="{{selectedStorage}}" class="storage-detail">
<view class="back" bindtap="closeStorage"> 返回寄存列表</view>
<view class="card-head">
<view><view class="card-title">{{selectedStorage.storageNo}}</view><view class="meta">到期 {{selectedStorage.expiresText}}</view></view>
<view class="status">{{selectedStorage.statusText}}</view>
</view>
<view class="credential-warning">取出时需输入当前领取码;服务端仅保存不可逆摘要。</view>
<view wx:for="{{selectedStorage.items}}" wx:key="id" wx:for-item="item" class="storage-item">
<view><view class="sku-name">{{item.productName}} · {{item.skuName}}</view><view class="meta">剩余 {{item.remainingQuantity}} / {{item.totalQuantity}}</view></view>
<button wx:if="{{selectedStorage.active && item.remainingQuantity > 0}}" class="button primary compact" data-item-id="{{item.id}}" bindtap="retrieveItem">取出</button>
</view>
<view wx:if="{{selectedStorage.active}}" class="row-actions detail-actions">
<button class="button ghost compact" bindtap="rotateCredential">更换领取码</button>
<button class="button danger compact" bindtap="cancelStorage">取消寄存</button>
</view>
<view class="section-title small">取出流水</view>
<view wx:for="{{selectedStorage.movements}}" wx:key="id" wx:for-item="movement" class="movement-row">
<text>{{movement.createdText}} · 明细 #{{movement.storageItemId}}</text><strong>取出 {{movement.quantity}},剩余 {{movement.remainingAfter}}</strong>
</view>
<view wx:if="{{!selectedStorage.movements.length}}" class="empty inline">暂无取出记录</view>
</view>
</block>
</view>
</scroll-view>
+52
View File
@@ -0,0 +1,52 @@
.scrollarea { height: 100vh; background: #f4f6f8; color: #152019; }
.page { min-height: 100%; padding: 36rpx 28rpx 180rpx; box-sizing: border-box; }
.hero { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 28rpx; }
.eyebrow { color: #6a776f; font-size: 22rpx; letter-spacing: 2rpx; }
.title { margin-top: 8rpx; font-size: 46rpx; line-height: 1.2; font-weight: 750; }
.hero-store { padding: 10rpx 16rpx; border-radius: 999rpx; background: #e4ebe7; color: #506158; font-size: 22rpx; }
.tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8rpx; margin-bottom: 24rpx; padding: 8rpx; border-radius: 22rpx; background: #e7ece9; }
.tab { padding: 18rpx 8rpx; border-radius: 16rpx; color: #647168; font-size: 26rpx; text-align: center; }
.tab.active { background: #fff; color: #16794b; font-weight: 700; box-shadow: 0 6rpx 20rpx rgba(22, 60, 40, .08); }
.notice, .error, .empty { margin: 20rpx 0; padding: 28rpx; border-radius: 20rpx; background: #fff; color: #6a756e; text-align: center; }
.notice.warning { background: #fff6dd; color: #865b00; }
.error { background: #fff0ee; color: #b53b31; text-align: left; }
.section-title { margin: 34rpx 4rpx 18rpx; font-size: 30rpx; font-weight: 750; }
.section-title.small { margin-top: 36rpx; font-size: 26rpx; }
.product-card, .business-card, .storage-detail, .credential-card { margin-bottom: 20rpx; overflow: hidden; border: 1rpx solid #e4e9e6; border-radius: 24rpx; background: #fff; box-shadow: 0 10rpx 30rpx rgba(38, 57, 46, .05); }
.product-cover { display: block; width: 100%; height: 280rpx; background: #e6ebe8; }
.product-main, .business-card, .storage-detail, .credential-card { padding: 26rpx; }
.product-head, .card-head, .summary-row, .storage-item { display: flex; align-items: center; justify-content: space-between; gap: 20rpx; }
.product-name, .card-title { font-size: 30rpx; font-weight: 750; }
.meta, .description { margin-top: 8rpx; color: #748077; font-size: 23rpx; line-height: 1.5; }
.description { margin: 20rpx 0 4rpx; }
.chip, .status { flex: 0 0 auto; padding: 8rpx 14rpx; border-radius: 999rpx; background: #e4f3eb; color: #17794c; font-size: 21rpx; }
.sku-row { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; margin-top: 22rpx; padding-top: 22rpx; border-top: 1rpx solid #edf0ee; }
.sku-name { font-size: 26rpx; font-weight: 650; }
.price { margin-top: 6rpx; color: #d45d32; font-size: 28rpx; font-weight: 750; }
.quantity-control { display: flex; align-items: center; gap: 18rpx; }
.quantity-button { width: 58rpx; min-width: 58rpx; height: 58rpx; padding: 0; border: 0; border-radius: 18rpx; background: #eef2ef; color: #486052; font-size: 34rpx; line-height: 58rpx; }
.quantity-button::after, .button::after { border: 0; }
.quantity-button.plus { background: #16794b; color: #fff; }
.summary-row { margin-top: 26rpx; padding-top: 22rpx; border-top: 1rpx solid #edf0ee; color: #657269; font-size: 25rpx; }
.summary-row strong { color: #18251d; font-size: 28rpx; }
.row-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 14rpx; margin-top: 22rpx; }
.button { margin: 0; border-radius: 16rpx; font-size: 25rpx; font-weight: 650; }
.button.compact { min-width: 150rpx; padding: 0 24rpx; line-height: 70rpx; }
.button.primary { background: #16794b; color: #fff; }
.button.ghost { background: #edf2ef; color: #315442; }
.button.danger { background: #fff0ee; color: #b23f37; }
.detail-link, .back { margin-top: 22rpx; color: #16794b; font-size: 24rpx; }
.tappable:active { transform: scale(.992); }
.credential-card { border-color: #f2d783; background: #fffbeb; }
.credential-title { color: #765100; font-size: 28rpx; font-weight: 750; }
.credential-warning { margin-top: 10rpx; color: #8b6b1d; font-size: 23rpx; line-height: 1.5; }
.credential-value { margin-top: 20rpx; padding: 20rpx; border-radius: 16rpx; background: #fff; font-family: monospace; font-size: 25rpx; line-height: 1.5; word-break: break-all; user-select: all; }
.storage-item { margin-top: 22rpx; padding-top: 22rpx; border-top: 1rpx solid #edf0ee; }
.detail-actions { justify-content: flex-start; }
.movement-row { display: grid; gap: 8rpx; padding: 18rpx 0; border-top: 1rpx solid #edf0ee; color: #6b786f; font-size: 22rpx; }
.movement-row strong { color: #27362d; font-size: 24rpx; }
.empty.inline { margin: 0; padding: 20rpx; background: #f7f9f8; }
.cart-bar { position: fixed; right: 24rpx; bottom: calc(24rpx + env(safe-area-inset-bottom)); left: 24rpx; z-index: 20; display: flex; align-items: center; justify-content: space-between; padding: 22rpx 24rpx; border-radius: 24rpx; background: #13291e; color: #fff; box-shadow: 0 18rpx 50rpx rgba(14, 42, 27, .25); }
.cart-count { color: #b8c8bf; font-size: 22rpx; }
.cart-total { margin-top: 4rpx; font-size: 32rpx; font-weight: 750; }
.cart-submit { min-width: 210rpx; }
+4
View File
@@ -40,6 +40,10 @@ Page({
openBenefits() {
wx.navigateTo({ url: '/pages/benefits/index' })
},
openGoods() {
wx.navigateTo({ url: '/pages/goods/index?tab=orders' })
},
})
function formatProfile(profile) {
+1
View File
@@ -73,6 +73,7 @@
</view>
<button bindtap="openRecharge">充值</button>
<button bindtap="openOrders">查看订单</button>
<button bindtap="openGoods">商品订单与寄存</button>
</view>
<view class="section-title ledger-title">最近账单</view>
+7
View File
@@ -44,6 +44,13 @@ Page({
})
},
openGoods() {
if (!this.data.storeId) return
wx.navigateTo({
url: `/pages/goods/index?storeId=${encodeURIComponent(this.data.storeId)}`,
})
},
async loadWifi() {
if (!this.data.storeId) return
this.setData({ loading: true, errorMessage: '' })
+1
View File
@@ -8,6 +8,7 @@
<view wx:else class="title">门店详情</view>
<view wx:if="{{errorMessage}}" class="error">{{errorMessage}}</view>
<button class="goods-entry" bindtap="openGoods">选购商品 · 查看商品订单与寄存</button>
<view class="section-title">房间</view>
<view wx:if="{{!loading && rooms.length === 0}}" class="empty">暂无可预订房间</view>
+6
View File
@@ -67,3 +67,9 @@
color: #888888;
text-align: center;
}
.goods-entry {
margin: 24rpx 0;
color: #ffffff;
background: #1d4ed8;
}