From 576c54ec9d6687d909169ea335c2623da5f337f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Tue, 7 Jul 2026 16:38:13 +0800 Subject: [PATCH] feat: add resident miniapp MVP --- miniapp/miniprogram/api/addressApi.js | 26 + miniapp/miniprogram/api/expressApi.js | 48 + miniapp/miniprogram/api/expressApi.test.js | 9 + miniapp/miniprogram/api/groupBuyApi.js | 41 + miniapp/miniprogram/api/homeApi.js | 21 + miniapp/miniprogram/api/noticeApi.js | 14 + miniapp/miniprogram/api/orderApi.js | 22 + miniapp/miniprogram/api/productApi.js | 31 + miniapp/miniprogram/api/secondGoodsApi.js | 26 + miniapp/miniprogram/app.js | 3 +- miniapp/miniprogram/app.wxss | 109 +- miniapp/miniprogram/pages/address/index.js | 65 +- miniapp/miniprogram/pages/address/index.wxml | 20 +- miniapp/miniprogram/pages/address/index.wxss | 4 +- miniapp/miniprogram/pages/express/create.js | 72 +- miniapp/miniprogram/pages/express/create.wxml | 24 +- miniapp/miniprogram/pages/express/create.wxss | 4 +- miniapp/miniprogram/pages/group-buy/detail.js | 70 +- .../miniprogram/pages/group-buy/detail.wxml | 26 +- .../miniprogram/pages/group-buy/detail.wxss | 6 +- miniapp/miniprogram/pages/group-buy/index.js | 29 +- .../miniprogram/pages/group-buy/index.wxml | 15 +- .../miniprogram/pages/group-buy/index.wxss | 5 +- miniapp/miniprogram/pages/home/index.js | 50 +- miniapp/miniprogram/pages/home/index.wxml | 58 +- miniapp/miniprogram/pages/home/index.wxss | 2 +- miniapp/miniprogram/pages/notices/index.js | 21 +- miniapp/miniprogram/pages/notices/index.wxml | 13 +- miniapp/miniprogram/pages/notices/index.wxss | 5 +- miniapp/miniprogram/pages/orders/detail.js | 57 +- miniapp/miniprogram/pages/orders/detail.wxml | 30 +- miniapp/miniprogram/pages/orders/detail.wxss | 4 +- miniapp/miniprogram/pages/orders/index.js | 71 +- miniapp/miniprogram/pages/orders/index.wxml | 18 +- miniapp/miniprogram/pages/orders/index.wxss | 5 +- miniapp/miniprogram/pages/products/detail.js | 94 +- .../miniprogram/pages/products/detail.wxml | 35 +- .../miniprogram/pages/products/detail.wxss | 6 +- miniapp/miniprogram/pages/products/index.js | 47 +- miniapp/miniprogram/pages/products/index.wxml | 29 +- miniapp/miniprogram/pages/products/index.wxss | 24 +- miniapp/miniprogram/pages/profile/index.js | 20 +- miniapp/miniprogram/pages/profile/index.wxml | 12 +- miniapp/miniprogram/pages/profile/index.wxss | 4 +- .../miniprogram/pages/second-hand/create.js | 53 +- .../miniprogram/pages/second-hand/create.wxml | 17 +- .../miniprogram/pages/second-hand/create.wxss | 6 +- .../miniprogram/pages/second-hand/index.js | 38 +- .../miniprogram/pages/second-hand/index.wxml | 19 +- .../miniprogram/pages/second-hand/index.wxss | 4 +- miniapp/miniprogram/utils/request.js | 88 +- miniapp/package-lock.json | 16411 ++++++++++++++++ 52 files changed, 17865 insertions(+), 66 deletions(-) create mode 100644 miniapp/miniprogram/api/addressApi.js create mode 100644 miniapp/miniprogram/api/expressApi.js create mode 100644 miniapp/miniprogram/api/expressApi.test.js create mode 100644 miniapp/miniprogram/api/groupBuyApi.js create mode 100644 miniapp/miniprogram/api/homeApi.js create mode 100644 miniapp/miniprogram/api/noticeApi.js create mode 100644 miniapp/miniprogram/api/orderApi.js create mode 100644 miniapp/miniprogram/api/productApi.js create mode 100644 miniapp/miniprogram/api/secondGoodsApi.js create mode 100644 miniapp/package-lock.json diff --git a/miniapp/miniprogram/api/addressApi.js b/miniapp/miniprogram/api/addressApi.js new file mode 100644 index 0000000..25ec33a --- /dev/null +++ b/miniapp/miniprogram/api/addressApi.js @@ -0,0 +1,26 @@ +const { request } = require('../utils/request') + +function listAddresses() { + return request({ url: '/api/mini/addresses' }) +} + +function createAddress(data) { + return request({ + url: '/api/mini/addresses', + method: 'POST', + data + }) +} + +function setDefaultAddress(id) { + return request({ + url: '/api/mini/addresses/' + id + '/default', + method: 'PUT' + }) +} + +module.exports = { + listAddresses, + createAddress, + setDefaultAddress +} diff --git a/miniapp/miniprogram/api/expressApi.js b/miniapp/miniprogram/api/expressApi.js new file mode 100644 index 0000000..3ce2fea --- /dev/null +++ b/miniapp/miniprogram/api/expressApi.js @@ -0,0 +1,48 @@ +const { request } = require('../utils/request') + +function calculateExpressFeeCent(packageCount) { + if (packageCount <= 0) { + throw new Error('件数必须大于0') + } + return 300 + Math.max(0, packageCount - 1) * 100 +} + +function createExpressOrder(data) { + return request({ + url: '/api/mini/express-orders', + method: 'POST', + data + }) +} + +function listExpressOrders() { + return request({ url: '/api/mini/express-orders' }) +} + +function getExpressOrderDetail(id) { + return request({ url: '/api/mini/express-orders/' + id }) +} + +function cancelExpressOrder(id, reason) { + return request({ + url: '/api/mini/express-orders/' + id + '/cancel', + method: 'PUT', + data: { reason } + }) +} + +function completeExpressOrder(id) { + return request({ + url: '/api/mini/express-orders/' + id + '/complete', + method: 'PUT' + }) +} + +module.exports = { + calculateExpressFeeCent, + createExpressOrder, + listExpressOrders, + getExpressOrderDetail, + cancelExpressOrder, + completeExpressOrder +} diff --git a/miniapp/miniprogram/api/expressApi.test.js b/miniapp/miniprogram/api/expressApi.test.js new file mode 100644 index 0000000..9e58553 --- /dev/null +++ b/miniapp/miniprogram/api/expressApi.test.js @@ -0,0 +1,9 @@ +const { calculateExpressFeeCent } = require('./expressApi') + +describe('calculateExpressFeeCent', () => { + test('matches backend express fee rule', () => { + expect(calculateExpressFeeCent(1)).toBe(300) + expect(calculateExpressFeeCent(2)).toBe(400) + expect(calculateExpressFeeCent(4)).toBe(600) + }) +}) diff --git a/miniapp/miniprogram/api/groupBuyApi.js b/miniapp/miniprogram/api/groupBuyApi.js new file mode 100644 index 0000000..b0f7614 --- /dev/null +++ b/miniapp/miniprogram/api/groupBuyApi.js @@ -0,0 +1,41 @@ +const { request } = require('../utils/request') + +function listGroupBuys() { + return request({ url: '/api/mini/group-buys' }) +} + +function getGroupBuyDetail(id) { + return request({ url: '/api/mini/group-buys/' + id }) +} + +function createGroupBuyOrder(data) { + return request({ + url: '/api/mini/group-buy-orders', + method: 'POST', + data + }) +} + +function listGroupBuyOrders() { + return request({ url: '/api/mini/group-buy-orders' }) +} + +function getGroupBuyOrderDetail(id) { + return request({ url: '/api/mini/group-buy-orders/' + id }) +} + +function cancelGroupBuyOrder(id) { + return request({ + url: '/api/mini/group-buy-orders/' + id + '/cancel', + method: 'PUT' + }) +} + +module.exports = { + listGroupBuys, + getGroupBuyDetail, + createGroupBuyOrder, + listGroupBuyOrders, + getGroupBuyOrderDetail, + cancelGroupBuyOrder +} diff --git a/miniapp/miniprogram/api/homeApi.js b/miniapp/miniprogram/api/homeApi.js new file mode 100644 index 0000000..bc97bdc --- /dev/null +++ b/miniapp/miniprogram/api/homeApi.js @@ -0,0 +1,21 @@ +const { request } = require('../utils/request') + +function getHomeData() { + return Promise.all([ + request({ url: '/api/mini/banners' }).catch(() => []), + request({ url: '/api/mini/notices' }).catch(() => []), + request({ url: '/api/mini/products' }).catch(() => []), + request({ url: '/api/mini/group-buys' }).catch(() => []), + request({ url: '/api/mini/second-goods' }).catch(() => []) + ]).then(([banners, notices, products, groupBuys, secondGoods]) => ({ + banners, + notices, + products, + groupBuys, + secondGoods + })) +} + +module.exports = { + getHomeData +} diff --git a/miniapp/miniprogram/api/noticeApi.js b/miniapp/miniprogram/api/noticeApi.js new file mode 100644 index 0000000..1c588ba --- /dev/null +++ b/miniapp/miniprogram/api/noticeApi.js @@ -0,0 +1,14 @@ +const { request } = require('../utils/request') + +function listNotices() { + return request({ url: '/api/mini/notices' }) +} + +function getNoticeDetail(id) { + return request({ url: '/api/mini/notices/' + id }) +} + +module.exports = { + listNotices, + getNoticeDetail +} diff --git a/miniapp/miniprogram/api/orderApi.js b/miniapp/miniprogram/api/orderApi.js new file mode 100644 index 0000000..73a6ab1 --- /dev/null +++ b/miniapp/miniprogram/api/orderApi.js @@ -0,0 +1,22 @@ +const { request } = require('../utils/request') + +function listGoodsOrders() { + return request({ url: '/api/mini/goods-orders' }) +} + +function getGoodsOrderDetail(id) { + return request({ url: '/api/mini/goods-orders/' + id }) +} + +function cancelGoodsOrder(id) { + return request({ + url: '/api/mini/goods-orders/' + id + '/cancel', + method: 'PUT' + }) +} + +module.exports = { + listGoodsOrders, + getGoodsOrderDetail, + cancelGoodsOrder +} diff --git a/miniapp/miniprogram/api/productApi.js b/miniapp/miniprogram/api/productApi.js new file mode 100644 index 0000000..cd1b8e3 --- /dev/null +++ b/miniapp/miniprogram/api/productApi.js @@ -0,0 +1,31 @@ +const { request } = require('../utils/request') + +function listCategories() { + return request({ url: '/api/mini/product-categories' }) +} + +function listProducts(categoryId) { + return request({ + url: '/api/mini/products', + data: categoryId ? { categoryId } : {} + }) +} + +function getProductDetail(id) { + return request({ url: '/api/mini/products/' + id }) +} + +function createGoodsOrder(data) { + return request({ + url: '/api/mini/goods-orders', + method: 'POST', + data + }) +} + +module.exports = { + listCategories, + listProducts, + getProductDetail, + createGoodsOrder +} diff --git a/miniapp/miniprogram/api/secondGoodsApi.js b/miniapp/miniprogram/api/secondGoodsApi.js new file mode 100644 index 0000000..c663118 --- /dev/null +++ b/miniapp/miniprogram/api/secondGoodsApi.js @@ -0,0 +1,26 @@ +const { request } = require('../utils/request') + +function listSecondGoods(category) { + return request({ + url: '/api/mini/second-goods', + data: category ? { category } : {} + }) +} + +function getSecondGoodsDetail(id) { + return request({ url: '/api/mini/second-goods/' + id }) +} + +function createSecondGoods(data) { + return request({ + url: '/api/mini/second-goods', + method: 'POST', + data + }) +} + +module.exports = { + listSecondGoods, + getSecondGoodsDetail, + createSecondGoods +} diff --git a/miniapp/miniprogram/app.js b/miniapp/miniprogram/app.js index 6da605c..3315e62 100644 --- a/miniapp/miniprogram/app.js +++ b/miniapp/miniprogram/app.js @@ -1,6 +1,7 @@ App({ globalData: { apiBaseUrl: 'http://localhost:8080', - token: '' + token: '', + user: null } }) diff --git a/miniapp/miniprogram/app.wxss b/miniapp/miniprogram/app.wxss index 8565793..3939b1c 100644 --- a/miniapp/miniprogram/app.wxss +++ b/miniapp/miniprogram/app.wxss @@ -1,9 +1,114 @@ page { - background: #f6f7f9; - color: #111827; + background: #f4f6f8; + color: #17212b; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; + font-size: 28rpx; } .page { + min-height: 100vh; padding: 24rpx; } + +.section { + margin-bottom: 24rpx; +} + +.section-title { + margin: 0 0 16rpx; + font-size: 32rpx; + font-weight: 700; +} + +.card { + background: #ffffff; + border: 1rpx solid #e5e9ef; + border-radius: 8rpx; + padding: 20rpx; + margin-bottom: 16rpx; +} + +.row { + display: flex; + align-items: center; + gap: 16rpx; +} + +.between { + justify-content: space-between; +} + +.muted { + color: #64748b; + font-size: 24rpx; +} + +.price { + color: #c2410c; + font-weight: 700; +} + +.tag { + display: inline-block; + padding: 4rpx 12rpx; + background: #e6f4f1; + color: #0f766e; + border-radius: 6rpx; + font-size: 22rpx; +} + +.grid { + display: flex; + flex-wrap: wrap; + gap: 16rpx; +} + +.shortcut { + width: calc((100% - 32rpx) / 3); + height: 116rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #ffffff; + border: 1rpx solid #e5e9ef; + border-radius: 8rpx; + font-weight: 600; +} + +.input, +.textarea, +.picker { + width: 100%; + min-height: 76rpx; + padding: 0 18rpx; + background: #ffffff; + border: 1rpx solid #d9e0e8; + border-radius: 6rpx; + margin-bottom: 16rpx; +} + +.textarea { + min-height: 140rpx; + padding-top: 16rpx; +} + +.button-row { + display: flex; + gap: 16rpx; +} + +button { + border-radius: 6rpx; +} + +.primary { + background: #0f766e; + color: #ffffff; +} + +.ghost { + background: #ffffff; + color: #0f766e; + border: 1rpx solid #0f766e; +} diff --git a/miniapp/miniprogram/pages/address/index.js b/miniapp/miniprogram/pages/address/index.js index ba76804..3efb373 100644 --- a/miniapp/miniprogram/pages/address/index.js +++ b/miniapp/miniprogram/pages/address/index.js @@ -1 +1,64 @@ -Page({}) +const { listAddresses, createAddress, setDefaultAddress } = require('../../api/addressApi') +const { setField } = require('../../utils/request') + +Page({ + data: { + addresses: [], + submitting: false, + form: { + contactName: '', + phone: '', + building: '', + room: '', + detail: '', + default: false + } + }, + + onLoad() { + this.load() + }, + + load() { + return listAddresses() + .then((addresses) => this.setData({ addresses: addresses || [] })) + .catch((error) => wx.showToast({ title: error.message, icon: 'none' })) + }, + + onFieldInput(event) { + setField(this, event) + }, + + submit() { + if (this.data.submitting) { + return + } + this.setData({ submitting: true }) + createAddress(this.data.form) + .then(() => { + wx.showToast({ title: '已保存' }) + this.setData({ + form: { + contactName: '', + phone: '', + building: '', + room: '', + detail: '', + default: false + } + }) + return this.load() + }) + .catch((error) => wx.showToast({ title: error.message, icon: 'none' })) + .finally(() => this.setData({ submitting: false })) + }, + + setDefault(event) { + setDefaultAddress(event.currentTarget.dataset.id) + .then(() => { + wx.showToast({ title: '已设默认' }) + return this.load() + }) + .catch((error) => wx.showToast({ title: error.message, icon: 'none' })) + } +}) diff --git a/miniapp/miniprogram/pages/address/index.wxml b/miniapp/miniprogram/pages/address/index.wxml index aa13d10..d6a83db 100644 --- a/miniapp/miniprogram/pages/address/index.wxml +++ b/miniapp/miniprogram/pages/address/index.wxml @@ -1 +1,19 @@ -地址管理 + + 地址管理 + + + + + + + + + + + {{item.contactName}} {{item.phone}} + 默认 + + {{item.building}} {{item.room}} {{item.detail}} + + + diff --git a/miniapp/miniprogram/pages/address/index.wxss b/miniapp/miniprogram/pages/address/index.wxss index 67a49e7..4049a75 100644 --- a/miniapp/miniprogram/pages/address/index.wxss +++ b/miniapp/miniprogram/pages/address/index.wxss @@ -1,3 +1,3 @@ -.page { - min-height: 100vh; +.ghost { + margin-top: 16rpx; } diff --git a/miniapp/miniprogram/pages/express/create.js b/miniapp/miniprogram/pages/express/create.js index ba76804..4b8096d 100644 --- a/miniapp/miniprogram/pages/express/create.js +++ b/miniapp/miniprogram/pages/express/create.js @@ -1 +1,71 @@ -Page({}) +const { calculateExpressFeeCent, createExpressOrder } = require('../../api/expressApi') +const { listAddresses } = require('../../api/addressApi') +const { setField } = require('../../utils/request') + +Page({ + data: { + addresses: [], + addressIndex: 0, + feeCent: 300, + submitting: false, + form: { + expressCompany: '', + pickupCode: '', + pickupAddress: '', + receiverPhone: '', + addressId: null, + packageCount: 1, + remark: '' + } + }, + + onLoad() { + listAddresses() + .then((addresses) => { + this.setData({ + addresses: addresses || [], + 'form.addressId': addresses && addresses[0] ? addresses[0].id : null + }) + }) + .catch(() => {}) + }, + + onFieldInput(event) { + setField(this, event) + }, + + onAddressChange(event) { + const index = Number(event.detail.value) + const address = this.data.addresses[index] + this.setData({ + addressIndex: index, + 'form.addressId': address ? address.id : null + }) + }, + + onPackageCountChange(event) { + const packageCount = Number(event.detail.value || 1) + this.setData({ + 'form.packageCount': packageCount, + feeCent: calculateExpressFeeCent(packageCount) + }) + }, + + submit() { + if (this.data.submitting) { + return + } + if (!this.data.form.addressId) { + wx.showToast({ title: '请先添加地址', icon: 'none' }) + return + } + this.setData({ submitting: true }) + createExpressOrder(this.data.form) + .then(() => { + wx.showToast({ title: '已提交' }) + wx.navigateTo({ url: '/pages/orders/index' }) + }) + .catch((error) => wx.showToast({ title: error.message, icon: 'none' })) + .finally(() => this.setData({ submitting: false })) + } +}) diff --git a/miniapp/miniprogram/pages/express/create.wxml b/miniapp/miniprogram/pages/express/create.wxml index 9c659e1..2fe1a1e 100644 --- a/miniapp/miniprogram/pages/express/create.wxml +++ b/miniapp/miniprogram/pages/express/create.wxml @@ -1 +1,23 @@ -代取快递 + + 代取快递 + + + + + + 送达地址 + + {{addresses[addressIndex] ? addresses[addressIndex].building + ' ' + addresses[addressIndex].room + ' ' + addresses[addressIndex].detail : '请选择地址'}} + + + + + 预估服务费 + ¥{{feeCent / 100}} + + 首件 3 元,续件每件 1 元 + +