feat: add resident miniapp MVP
This commit is contained in:
@@ -1 +1,93 @@
|
||||
Page({})
|
||||
const { getProductDetail, createGoodsOrder } = require('../../api/productApi')
|
||||
const { listAddresses } = require('../../api/addressApi')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
product: null,
|
||||
addresses: [],
|
||||
addressIndex: 0,
|
||||
skuIndex: 0,
|
||||
submitting: false,
|
||||
form: {
|
||||
quantity: 1,
|
||||
deliveryMethod: 'IMMEDIATE',
|
||||
scheduledTime: '',
|
||||
remark: ''
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.productId = Number(options.id)
|
||||
this.load()
|
||||
},
|
||||
|
||||
load() {
|
||||
Promise.all([getProductDetail(this.productId), listAddresses().catch(() => [])])
|
||||
.then(([product, addresses]) => {
|
||||
this.setData({
|
||||
product,
|
||||
addresses: addresses || []
|
||||
})
|
||||
})
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
},
|
||||
|
||||
onSkuChange(event) {
|
||||
this.setData({ skuIndex: Number(event.detail.value) })
|
||||
},
|
||||
|
||||
onAddressChange(event) {
|
||||
this.setData({ addressIndex: Number(event.detail.value) })
|
||||
},
|
||||
|
||||
onQuantityChange(event) {
|
||||
this.setData({ 'form.quantity': Number(event.detail.value || 1) })
|
||||
},
|
||||
|
||||
onMethodChange(event) {
|
||||
this.setData({ 'form.deliveryMethod': event.detail.value })
|
||||
},
|
||||
|
||||
onScheduledTimeChange(event) {
|
||||
this.setData({ 'form.scheduledTime': event.detail.value })
|
||||
},
|
||||
|
||||
onRemarkInput(event) {
|
||||
this.setData({ 'form.remark': event.detail.value })
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (!this.data.product || this.data.submitting) {
|
||||
return
|
||||
}
|
||||
const sku = this.data.product.skus[this.data.skuIndex]
|
||||
if (!sku) {
|
||||
wx.showToast({ title: '请选择规格', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const address = this.data.addresses[this.data.addressIndex]
|
||||
if (this.data.form.deliveryMethod !== 'SELF_PICKUP' && !address) {
|
||||
wx.showToast({ title: '请先添加地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ submitting: true })
|
||||
createGoodsOrder({
|
||||
merchantId: this.data.product.merchantId,
|
||||
addressId: address ? address.id : null,
|
||||
deliveryMethod: this.data.form.deliveryMethod,
|
||||
scheduledTime: this.data.form.scheduledTime || null,
|
||||
remark: this.data.form.remark,
|
||||
items: [{
|
||||
productId: this.data.product.id,
|
||||
skuId: sku.id,
|
||||
quantity: this.data.form.quantity
|
||||
}]
|
||||
})
|
||||
.then(() => {
|
||||
wx.showToast({ title: '已提交' })
|
||||
wx.navigateTo({ url: '/pages/orders/index' })
|
||||
})
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
.finally(() => this.setData({ submitting: false }))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1 +1,34 @@
|
||||
<view class="page">商品详情</view>
|
||||
<view class="page" wx:if="{{product}}">
|
||||
<view class="card">
|
||||
<view class="section-title">{{product.name}}</view>
|
||||
<view class="muted">{{product.description || '社区商品'}}</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="muted">规格</view>
|
||||
<picker mode="selector" range="{{product.skus}}" range-key="skuName" value="{{skuIndex}}" bindchange="onSkuChange">
|
||||
<view class="picker">{{product.skus[skuIndex].skuName}} / ¥{{product.skus[skuIndex].priceCent / 100}} / 库存 {{product.skus[skuIndex].stock}}</view>
|
||||
</picker>
|
||||
<view class="muted">数量</view>
|
||||
<input class="input" type="number" value="{{form.quantity}}" bindinput="onQuantityChange" />
|
||||
<view class="muted">配送方式</view>
|
||||
<radio-group bindchange="onMethodChange">
|
||||
<label class="radio"><radio value="IMMEDIATE" checked="{{form.deliveryMethod === 'IMMEDIATE'}}" />立即配送</label>
|
||||
<label class="radio"><radio value="SCHEDULED" checked="{{form.deliveryMethod === 'SCHEDULED'}}" />预约配送</label>
|
||||
<label class="radio"><radio value="SELF_PICKUP" checked="{{form.deliveryMethod === 'SELF_PICKUP'}}" />自提</label>
|
||||
</radio-group>
|
||||
<view wx:if="{{form.deliveryMethod !== 'SELF_PICKUP'}}">
|
||||
<view class="muted">收货地址</view>
|
||||
<picker mode="selector" range="{{addresses}}" range-key="detail" value="{{addressIndex}}" bindchange="onAddressChange">
|
||||
<view class="picker">{{addresses[addressIndex] ? addresses[addressIndex].building + ' ' + addresses[addressIndex].room + ' ' + addresses[addressIndex].detail : '请选择地址'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view wx:if="{{form.deliveryMethod === 'SCHEDULED'}}">
|
||||
<view class="muted">预约时间</view>
|
||||
<input class="input" placeholder="2026-07-07T18:30:00" bindinput="onScheduledTimeChange" />
|
||||
</view>
|
||||
<view class="muted">备注</view>
|
||||
<textarea class="textarea" bindinput="onRemarkInput" />
|
||||
<button class="primary" loading="{{submitting}}" bindtap="submit">提交订单</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
.radio {
|
||||
display: block;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
|
||||
@@ -1 +1,46 @@
|
||||
Page({})
|
||||
const { listCategories, listProducts } = require('../../api/productApi')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: false,
|
||||
categories: [],
|
||||
activeCategoryId: null,
|
||||
products: []
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.load()
|
||||
},
|
||||
|
||||
load() {
|
||||
this.setData({ loading: true })
|
||||
Promise.all([listCategories(), listProducts()])
|
||||
.then(([categories, products]) => {
|
||||
this.setData({
|
||||
categories: categories || [],
|
||||
products: products || []
|
||||
})
|
||||
})
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
.finally(() => this.setData({ loading: false }))
|
||||
},
|
||||
|
||||
chooseCategory(event) {
|
||||
const categoryId = event.currentTarget.dataset.id
|
||||
this.setData({ activeCategoryId: categoryId })
|
||||
listProducts(categoryId)
|
||||
.then((products) => this.setData({ products: products || [] }))
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
},
|
||||
|
||||
clearCategory() {
|
||||
this.setData({ activeCategoryId: null })
|
||||
listProducts()
|
||||
.then((products) => this.setData({ products: products || [] }))
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
},
|
||||
|
||||
goDetail(event) {
|
||||
wx.navigateTo({ url: '/pages/products/detail?id=' + event.currentTarget.dataset.id })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1 +1,28 @@
|
||||
<view class="page">商品预定</view>
|
||||
<view class="page">
|
||||
<view class="section-title">商品预定</view>
|
||||
<scroll-view scroll-x class="category-scroll">
|
||||
<view class="category-item {{!activeCategoryId ? 'active' : ''}}" bindtap="clearCategory">全部</view>
|
||||
<view
|
||||
wx:for="{{categories}}"
|
||||
wx:key="id"
|
||||
class="category-item {{activeCategoryId === item.id ? 'active' : ''}}"
|
||||
bindtap="chooseCategory"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
{{item.name}}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:for="{{products}}" wx:key="id" class="card" bindtap="goDetail" data-id="{{item.id}}">
|
||||
<view class="row between">
|
||||
<view>
|
||||
<view>{{item.name}}</view>
|
||||
<view class="muted">{{item.description || '社区精选商品'}}</view>
|
||||
</view>
|
||||
<view class="tag">{{item.status}}</view>
|
||||
</view>
|
||||
<view wx:if="{{item.skus.length}}" class="price">¥{{item.skus[0].priceCent / 100}} 起</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!products.length && !loading}}" class="card muted">暂无商品</view>
|
||||
</view>
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
.category-scroll {
|
||||
white-space: nowrap;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 120rpx;
|
||||
height: 64rpx;
|
||||
padding: 0 20rpx;
|
||||
margin-right: 12rpx;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #d9e0e8;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
|
||||
.category-item.active {
|
||||
color: #ffffff;
|
||||
background: #0f766e;
|
||||
border-color: #0f766e;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user