From 61e9879b5df51228f79b4338d6752d9ddbc9065d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Wed, 1 Jul 2026 13:18:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(store):=20=E5=AE=9E=E7=8E=B0=E9=97=A8?= =?UTF-8?q?=E5=BA=97=E4=B8=AD=E5=BF=83=E5=8A=9F=E8=83=BD=E5=8F=8A=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在“我的钱包”上方新增“门店中心”按钮,仅管理员可见 - 新建门店中心页面,实现订单列表按状态筛选功能 - 支持订单操作:标记已付款、标记已发货、标记已完成 - 操作时必须上传操作凭证图片,支持拍照和相册选择,最多3张 - 凭证图片上传至指定接口,操作成功后刷新订单列表 - 更新 app.config 注册门店中心新页面及对应配置 - 说明订单状态字段及对应数值含义,便于状态判断与展示 --- .workbuddy/memory/2026-07-01.md | 15 + src/app.config.ts | 1 + src/pages/store/center/index.config.ts | 3 + src/pages/store/center/index.tsx | 382 +++++++++++++++++++++++++ src/pages/user/user.tsx | 2 + 5 files changed, 403 insertions(+) create mode 100644 src/pages/store/center/index.config.ts create mode 100644 src/pages/store/center/index.tsx diff --git a/.workbuddy/memory/2026-07-01.md b/.workbuddy/memory/2026-07-01.md index cafd254..8ed17fa 100644 --- a/.workbuddy/memory/2026-07-01.md +++ b/.workbuddy/memory/2026-07-01.md @@ -1 +1,16 @@ - 修改 `src/pages/user/about/index.tsx`:根据截图将“广西金炜建设工程有限公司”的简介更新为“玉林市玉州区鑫龙家电经营部”相关内容,包括头部品牌、公司简介、核心业务、联系信息及版权。 + +## 门店中心功能实现 + +- 修改 `src/pages/user/user.tsx`:在“我的钱包”上方添加“门店中心”按钮,仅当 `user.isAdmin === true` 时显示。 +- 新建门店中心页面 `src/pages/store/center/index.tsx`: + - 订单列表按状态筛选(待付款/待发货/待收货/已完成) + - 支持操作:标记已付款、标记已发货、标记已完成 + - 操作时需要上传凭证图片(拍照或相册选择,最多3张) + - 凭证图片上传到 `https://shop-api.websoft.top/api/oss/upload` + - 操作成功后刷新订单列表 +- 修改 `src/app.config.ts`:注册新页面 `pages/store/center/index` +- 订单状态字段说明(`ShopOrder`): + - `payStatus: boolean` — `false`=未付款,`true`=已付款 + - `deliveryStatus: number` — `10`=未发货,`20`=已发货 + - `orderStatus: number` — `0`=正常,`1`=已完成,`2`=已取消 diff --git a/src/app.config.ts b/src/app.config.ts index be86734..126d95e 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -86,6 +86,7 @@ export default { // 门店页面 'pages/store/list/index', 'pages/store/booking/index', + 'pages/store/center/index', // 售后页面 'pages/after-sale/apply/index', 'pages/after-sale/progress/index', diff --git a/src/pages/store/center/index.config.ts b/src/pages/store/center/index.config.ts new file mode 100644 index 0000000..d4c7e72 --- /dev/null +++ b/src/pages/store/center/index.config.ts @@ -0,0 +1,3 @@ +export default { + navigationBarTitleText: '门店中心', +} diff --git a/src/pages/store/center/index.tsx b/src/pages/store/center/index.tsx new file mode 100644 index 0000000..7e4aa4d --- /dev/null +++ b/src/pages/store/center/index.tsx @@ -0,0 +1,382 @@ +import React, { useState, useEffect } from 'react' +import { View, Text, Image, ScrollView } from '@tarojs/components' +import Taro from '@tarojs/taro' +import { pageShopOrder, updateShopOrder } from '@/api/shop/shopOrder' +import type { ShopOrder } from '@/api/shop/shopOrder/model' +import { TenantId } from '../../../../config/app' + +type TabKey = 'unpaid' | 'unshipped' | 'shipped' | 'completed' + +const TABS = [ + { key: 'unpaid', label: '待付款', params: { payStatus: 0 } }, + { key: 'unshipped', label: '待发货', params: { payStatus: 1, deliveryStatus: 10 } }, + { key: 'shipped', label: '待收货', params: { payStatus: 1, deliveryStatus: 20 } }, + { key: 'completed', label: '已完成', params: { orderStatus: 1, payStatus: 1 } }, +] as const + +type OpType = 'pay' | 'ship' | 'complete' + +const OP_LABEL: Record = { + pay: '已付款', + ship: '已发货', + complete: '已完成', +} + +/** 上传图片到后端,返回图片 URL */ +const uploadImage = (filePath: string): Promise => { + return new Promise((resolve, reject) => { + Taro.uploadFile({ + url: 'https://shop-api.websoft.top/api/oss/upload', + filePath, + name: 'file', + header: { 'content-type': 'application/json', TenantId }, + success: (res) => { + try { + const data = JSON.parse(res.data) + if (data.code === 0 && data.data?.url) { + resolve(data.data.url) + } else { + reject(new Error(data.message || '上传失败')) + } + } catch { + reject(new Error('解析上传响应失败')) + } + }, + fail: () => reject(new Error('上传请求失败')), + }) + }) +} + +export default function StoreCenterPage() { + const [activeTab, setActiveTab] = useState('unshipped') + const [orderList, setOrderList] = useState([]) + const [total, setTotal] = useState(0) + const [pageNo, setPageNo] = useState(1) + const [loading, setLoading] = useState(false) + const pageSize = 10 + + // 操作弹窗状态 + const [showModal, setShowModal] = useState(false) + const [currentOrder, setCurrentOrder] = useState(null) + const [opType, setOpType] = useState('pay') + const [proofImages, setProofImages] = useState([]) + const [submitting, setSubmitting] = useState(false) + + /** 加载订单列表 */ + const loadOrders = async (tab: TabKey = activeTab, page: number = 1, append = false) => { + const tabConfig = TABS.find(t => t.key === tab) + if (!tabConfig) return + setLoading(true) + try { + const res: any = await pageShopOrder({ + ...tabConfig.params, + pageNo, + pageSize, + } as any) + const records: ShopOrder[] = res?.records || res?.data?.records || [] + const totalCount: number = res?.total || res?.data?.total || 0 + setOrderList(append ? prev => [...prev, ...records] : records) + setTotal(totalCount) + setPageNo(page) + } catch (e: any) { + Taro.showToast({ title: e.message || '加载失败', icon: 'none' }) + } finally { + setLoading(false) + } + } + + useEffect(() => { loadOrders(activeTab, 1) }, [activeTab]) + + /** 打开操作弹窗 */ + const openModal = (order: ShopOrder, type: OpType) => { + setCurrentOrder(order) + setOpType(type) + setProofImages([]) + setShowModal(true) + } + + /** 关闭弹窗 */ + const closeModal = () => { + setShowModal(false) + setCurrentOrder(null) + setProofImages([]) + } + + /** 选择凭证图片 */ + const chooseProofImage = () => { + Taro.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['camera', 'album'], + success: async (res) => { + try { + Taro.showLoading({ title: '上传中...' }) + const url = await uploadImage(res.tempFilePaths[0]) + Taro.hideLoading() + setProofImages(prev => [...prev, url]) + } catch (e: any) { + Taro.hideLoading() + Taro.showToast({ title: e.message || '上传失败', icon: 'none' }) + } + }, + }) + } + + /** 移除凭证图片 */ + const removeProofImage = (idx: number) => { + setProofImages(prev => prev.filter((_, i) => i !== idx)) + } + + /** 提交操作 */ + const submitOperation = async () => { + if (!currentOrder) return + if (proofImages.length === 0) { + Taro.showToast({ title: '请上传操作凭证', icon: 'none' }) + return + } + setSubmitting(true) + try { + const proofText = `【门店操作凭证】${OP_LABEL[opType]}:${proofImages.join(',')}` + const updateData: any = { orderId: currentOrder.orderId } + + if (opType === 'pay') { + updateData.payStatus = true + updateData.payTime = new Date().toISOString() + } else if (opType === 'ship') { + updateData.deliveryStatus = 20 + updateData.deliveryTime = new Date().toISOString() + } else if (opType === 'complete') { + updateData.orderStatus = 1 + } + + // 将凭证追加到备注 + updateData.comments = (currentOrder.comments || '') + `\n${proofText}` + + await updateShopOrder(updateData) + Taro.showToast({ title: '操作成功', icon: 'success' }) + closeModal() + loadOrders(activeTab, 1) + } catch (e: any) { + Taro.showToast({ title: e.message || '操作失败', icon: 'none' }) + } finally { + setSubmitting(false) + } + } + + /** 获取订单可进行的操作 */ + const getOrderActions = (order: ShopOrder) => { + const actions: { label: string; type: OpType }[] = [] + if (!order.payStatus) { + actions.push({ label: '标记已付款', type: 'pay' }) + } + if (order.payStatus && order.deliveryStatus === 10) { + actions.push({ label: '标记已发货', type: 'ship' }) + } + if (order.payStatus && order.deliveryStatus === 20 && order.orderStatus === 0) { + actions.push({ label: '标记已完成', type: 'complete' }) + } + return actions + } + + const renderOrderCard = (order: ShopOrder) => { + const actions = getOrderActions(order) + return ( + + {/* 订单头部 */} + + 订单号:{order.orderNo} + + {getStatusText(order)} + + + + {/* 商品列表 */} + {(order as any).orderGoods?.map((goods: any, idx: number) => ( + + {goods.coverImage && ( + + )} + + {goods.goodsName} + {goods.specInfo && ( + {goods.specInfo} + )} + + ¥{goods.price} + x{goods.quantity} + + + + ))} + + {/* 收货信息 */} + {(order.realName || order.phone) && ( + + {order.realName} {order.phone} + {order.address && ( + {order.address} + )} + + )} + + {/* 金额 */} + + + 共{(order as any).orderGoods?.length || 0}件商品 + + + 实付:¥{order.payPrice || order.totalPrice} + + + + {/* 操作按钮 */} + {actions.length > 0 && ( + + {actions.map(act => ( + openModal(order, act.type)} + > + {act.label} + + ))} + + )} + + ) + } + + return ( + + {/* Tab 栏 */} + + {TABS.map(tab => ( + setActiveTab(tab.key as TabKey)} + > + + {tab.label} + + + ))} + + + {/* 订单列表 */} + { + if (orderList.length < total && !loading) loadOrders(activeTab, pageNo + 1, true) + }} + > + {loading && orderList.length === 0 ? ( + + 加载中... + + ) : orderList.length === 0 ? ( + + 暂无订单 + + ) : ( + orderList.map(renderOrderCard) + )} + {loading && orderList.length > 0 && ( + + 加载中... + + )} + + + + {/* 操作弹窗 */} + {showModal && ( + + {/* 遮罩 */} + + {/* 弹窗内容 */} + + + 确认{OP_LABEL[opType]} + + + {/* 订单信息 */} + {currentOrder && ( + + 订单号:{currentOrder.orderNo} + + 实付金额:¥{currentOrder.payPrice || currentOrder.totalPrice} + + + )} + + {/* 凭证上传 */} + 上传操作凭证(必填,最多3张) + + {proofImages.map((url, idx) => ( + + + removeProofImage(idx)} + > + × + + + ))} + {proofImages.length < 3 && ( + + + + + )} + + + {/* 操作按钮 */} + + + 取消 + + + {submitting ? ( + 提交中... + ) : ( + 确认{OP_LABEL[opType]} + )} + + + + + )} + + ) +} + +/** 获取订单状态文字 */ +function getStatusText(order: ShopOrder): string { + if (order.payStatus === false) return '待付款' + if (order.orderStatus === 2) return '已取消' + if (order.deliveryStatus === 10) return '待发货' + if (order.deliveryStatus === 20) return '待收货' + if (order.orderStatus === 1) return '已完成' + return '处理中' +} + +/** 获取订单状态颜色 */ +function getStatusColor(order: ShopOrder): string { + if (order.payStatus === false) return '#ee0a24' + if (order.orderStatus === 2) return '#999' + if (order.deliveryStatus === 10) return '#ff7d00' + if (order.deliveryStatus === 20) return '#4b9cf5' + if (order.orderStatus === 1) return '#0e932e' + return '#999' +} diff --git a/src/pages/user/user.tsx b/src/pages/user/user.tsx index 3ddec4d..6e5215d 100644 --- a/src/pages/user/user.tsx +++ b/src/pages/user/user.tsx @@ -56,6 +56,8 @@ const UserPage: React.FC = () => { // { icon: '🏆', label: '赛事活动', url: '/pages/event/my/index' }, // { icon: '📅', label: '预约穿线', url: '/pages/booking/list/index' }, // { icon: '🎫', label: '优惠券', url: '/pages/user/coupon-list' }, + // 门店中心:仅管理员显示 + ...(user?.isAdmin ? [{ icon: '🏪', label: '门店中心', url: '/pages/store/center/index' }] : []), { icon: '💰', label: '我的钱包', url: '/pages/user/wallet' }, { icon: '📍', label: '收货地址', url: '/pages/user/address-list' }, // { icon: '⭐', label: '积分明细', url: '/pages/user/points-record' },