From 894592c2903404a9e24be567ce02044933e13d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Tue, 14 Jul 2026 22:17:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(shop):=20=E6=96=B0=E5=A2=9E=E7=BA=BF?= =?UTF-8?q?=E4=B8=8B=E4=BB=98=E6=AC=BE=E6=94=AF=E6=8C=81=E5=8F=8A=E4=BC=98?= =?UTF-8?q?=E5=8C=96Dashboard=E6=95=B0=E6=8D=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增线下付款(payType=9)支付方式,支持订单中标识和确认收款操作 - 实现商家确认线下付款后订单进入待发货状态功能 - 更新支付方式组件和订单列表及详情页的支付状态显示 - 优化shop/dashboard快捷操作按钮为商城常用功能,修正快速入口路由路径 - 修复Dashboard待发货订单数统计时未过滤商城订单导致数据不准的问题 - 修复Dashboard运行天数为0的异常,将租户信息请求独立处理,避免某些请求失败影响 - 修复statisticsStore中couponUsedCount与getter同名冲突,改名为safeCouponUsedCount并同步更新引用 --- .workbuddy/memory/2026-07-14.md | 24 ++++++ src/api/shop/shopOrder/index.ts | 16 ++++ src/api/shop/shopOrder/model/index.ts | 4 +- src/components/PayMethod/index.vue | 6 ++ src/store/modules/statistics.ts | 4 +- src/utils/shop.ts | 4 + src/views/cms/dashboard/index.vue | 23 ++++- src/views/shop/dashboard/index.vue | 84 ++++++++++++------- .../shop/shopOrder/components/orderInfo.vue | 19 ++++- src/views/shop/shopOrder/index.vue | 71 +++++++++++++++- src/views/shop/shopSetting/index.vue | 8 +- 11 files changed, 217 insertions(+), 46 deletions(-) diff --git a/.workbuddy/memory/2026-07-14.md b/.workbuddy/memory/2026-07-14.md index a33196d..a5592ac 100644 --- a/.workbuddy/memory/2026-07-14.md +++ b/.workbuddy/memory/2026-07-14.md @@ -6,3 +6,27 @@ - 在 `src/views/shop/dashboard/index.vue` 的欢迎横幅右侧添加新订单提醒开关栏(开关 + 测试按钮),检测到新订单后自动调用 loadData() 刷新 Dashboard 数据。 - 在 `src/views/cms/dashboard/index.vue` 的概况卡片标题栏添加新订单提醒控件,同样检测到新订单后刷新数据。 - 提取了 CMS Dashboard 的 loadData 函数使其可复用。 + +## 更新 shop/dashboard 快捷操作按钮为商城常用功能 + +- 快捷操作按钮从原来的通用功能(参数配置/用户管理/站点管理/登录日志)改为商城系统常用功能:订单管理、商品管理、商品分类、优惠券管理、会员管理、商城设置、清除缓存。 +- 路由路径统一使用 `/shop/shopXxx` 格式(与 `/shop/shopOrder` 等已确认路径一致),修复了快速入口中 `/shopGoods` → `/shop/shopGoods` 的路径不一致问题。 +- 快速入口九宫格也同步改为商城相关入口。 +- 图标导入更新:移除 UngroupOutlined/CalendarOutlined/UserOutlined/FileTextOutlined,新增 ShoppingCartOutlined/AppstoreOutlined/GiftOutlined/TeamOutlined/SettingOutlined。 + +## 修复 shop/dashboard 待发货订单数量统计不准确 + +- 根因:订单列表页 datasource 中 `where.type = 0`(只查商城订单),但 Dashboard 统计未传 `type: 0`,导致把预定订单/外卖(type=1)和会员卡订单(type=2)也计入。 +- 修复:为 pendingShipmentCount 和 pendingRefundCount 查询都加上 `type: 0`。 + +## 修复 Dashboard 运行天数为 0 的问题 + +- 根因:`getTenantInfo()` 被放在 `await Promise.all([...])` 之后,如果 `statisticsStore.fetchStatistics()` 抛异常(`couponUsedCount` 错误),`Promise.all` reject 后直接跳到 `catch`,`getTenantInfo` 根本不会执行,`tenantCreateTime` 永远是空值。 +- 修复:在 `shop/dashboard/index.vue` 和 `cms/dashboard/index.vue` 的 `loadData` 中,把 `getTenantInfo()` 放到 `Promise.all` 外面,用 `.then` 独立执行,不受其他请求失败影响。 +- 运行天数计算基于 `getTenantInfo()` 返回的 `Company.createTime`(租户创建时间)。 + +## 修复 statisticsStore couponUsedCount 冲突 + +- 根因:`couponUsedCount` 同时被定义成 state 属性和 getter,Pinia 中同名冲突导致 action 里 `this.couponUsedCount = ...` 报错:`'set' on proxy: trap returned falsish`。 +- 修复:将 getter 改名为 `safeCouponUsedCount`,`shop/dashboard/index.vue` 中同步更新引用(该引用当前被注释,但保持一致性)。 + diff --git a/src/api/shop/shopOrder/index.ts b/src/api/shop/shopOrder/index.ts index 3dddf75..c58b7d1 100644 --- a/src/api/shop/shopOrder/index.ts +++ b/src/api/shop/shopOrder/index.ts @@ -150,3 +150,19 @@ export async function refundShopOrder(data: ShopOrder) { } return Promise.reject(new Error(res.data.message)); } + +/** + * 确认线下付款收款 + * 商家确认已收到线下转账(微信转账/银行汇款等),确认后订单进入待发货状态 + */ +export async function confirmOfflinePayment(id: number, remarks?: string) { + const res = await request.put>( + MODULES_API_URL + '/shop/shop-order/confirm-offline-payment/' + id, + null, + { params: { remarks } } + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/shop/shopOrder/model/index.ts b/src/api/shop/shopOrder/model/index.ts index e7f75c2..e89add4 100644 --- a/src/api/shop/shopOrder/model/index.ts +++ b/src/api/shop/shopOrder/model/index.ts @@ -85,9 +85,9 @@ export interface ShopOrder { coachId?: number; // 支付的用户id payUserId?: number; - // 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃 + // 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9线下付款, 10~18 已废弃 payType?: number; - // 代付支付方式, 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃 + // 代付支付方式, 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9线下付款, 10~18 已废弃 friendPayType?: number; // 0未付款,1已付款 payStatus?: number; diff --git a/src/components/PayMethod/index.vue b/src/components/PayMethod/index.vue index fc24683..4b41ce0 100644 --- a/src/components/PayMethod/index.vue +++ b/src/components/PayMethod/index.vue @@ -84,6 +84,12 @@ label: '货到付款', key: 'codPay', icon: 'IdcardOutlined' + }, + { + value: 9, + label: '线下付款', + key: 'offlinePay', + icon: 'IdcardOutlined' } ]); diff --git a/src/store/modules/statistics.ts b/src/store/modules/statistics.ts index 6295838..9e7c101 100644 --- a/src/store/modules/statistics.ts +++ b/src/store/modules/statistics.ts @@ -90,9 +90,9 @@ export const useStatisticsStore = defineStore('statistics', { }, /** - * 获取今日使用优惠券数量 + * 获取今日使用优惠券数量(安全取值) */ - couponUsedCount: (state): number => { + safeCouponUsedCount: (state): number => { return safeNumber(state.couponUsedCount); }, diff --git a/src/utils/shop.ts b/src/utils/shop.ts index 81e41cb..af220b5 100644 --- a/src/utils/shop.ts +++ b/src/utils/shop.ts @@ -36,6 +36,10 @@ export function getPayType(index?: number): any { { value: 8, label: '货到付款' + }, + { + value: 9, + label: '线下付款' } ]; if (index != null) { diff --git a/src/views/cms/dashboard/index.vue b/src/views/cms/dashboard/index.vue index acf8f0e..8562bb8 100644 --- a/src/views/cms/dashboard/index.vue +++ b/src/views/cms/dashboard/index.vue @@ -213,11 +213,14 @@ import { openNew } from '@/utils/common'; import { useSiteStore } from '@/store/modules/site'; import { useStatisticsStore } from '@/store/modules/statistics'; + import { useUserStore } from '@/store/modules/user'; + import { getTenantInfo } from '@/api/layout'; import { storeToRefs } from 'pinia'; // 使用状态管理 const siteStore = useSiteStore(); const statisticsStore = useStatisticsStore(); + const userStore = useUserStore(); // 从 store 中获取响应式数据 const { siteInfo, loading: siteLoading } = storeToRefs(siteStore); @@ -238,7 +241,13 @@ }); // 计算属性 - const runDays = computed(() => siteStore.runDays); + const now = ref(Date.now()); + const tenantCreateTime = ref(''); + let runDaysTimer: ReturnType; + const runDays = computed(() => { + if (!tenantCreateTime.value) return 0; + return Math.floor((now.value - new Date(tenantCreateTime.value).getTime()) / (24 * 60 * 60 * 1000)); + }); const userCount = computed(() => statisticsStore.userCount); const orderCount = computed(() => statisticsStore.orderCount); const totalSales = computed(() => statisticsStore.totalSales); @@ -248,6 +257,15 @@ // 加载数据 const loadData = async () => { + // 独立请求租户信息,不受其他请求失败影响 + getTenantInfo() + .then((res) => { + tenantCreateTime.value = res?.createTime || ''; + }) + .catch((e) => { + console.warn('获取租户信息失败:', e); + }); + try { await Promise.all([ siteStore.fetchSiteInfo(), @@ -267,11 +285,14 @@ await loadData(); // 开始自动刷新统计数据(每5分钟) statisticsStore.startAutoRefresh(); + // 运行天数每小时检查一次(跨天自动更新) + runDaysTimer = setInterval(() => { now.value = Date.now(); }, 60 * 60 * 1000); }); onUnmounted(() => { // 组件卸载时停止自动刷新 statisticsStore.stopAutoRefresh(); + clearInterval(runDaysTimer); }); diff --git a/src/views/shop/dashboard/index.vue b/src/views/shop/dashboard/index.vue index 3c5a5b3..dbdac18 100644 --- a/src/views/shop/dashboard/index.vue +++ b/src/views/shop/dashboard/index.vue @@ -180,27 +180,30 @@ - - 参数配置 - - - + 订单管理 - - - 用户管理 - - + - 站点管理 + 商品管理 - - - 登录日志 + + + 商品分类 + + + + 优惠券管理 + + + + 会员管理 + + + + 商城设置 @@ -221,11 +224,12 @@ import { useOrderNotify } from '@/views/shop/shopOrder/useOrderNotify'; import { ReloadOutlined, RightOutlined, - UngroupOutlined, - CalendarOutlined, - UserOutlined, + ShoppingCartOutlined, ShopOutlined, - FileTextOutlined, + AppstoreOutlined, + GiftOutlined, + TeamOutlined, + SettingOutlined, ClearOutlined, InfoCircleOutlined } from '@ant-design/icons-vue'; @@ -234,6 +238,7 @@ import { useSiteStore } from '@/store/modules/site'; import { useStatisticsStore } from '@/store/modules/statistics'; import { useUserStore } from '@/store/modules/user'; import { pageShopOrder } from '@/api/shop/shopOrder'; +import { getTenantInfo } from '@/api/layout'; import { storeToRefs } from 'pinia'; import { removeSiteInfoCache } from '@/api/cms/cmsWebsite'; @@ -257,7 +262,13 @@ const systemInfo = reactive({ }); // 计算属性 -const runDays = computed(() => siteStore.runDays); +const now = ref(Date.now()); +const tenantCreateTime = ref(''); +let runDaysTimer: ReturnType; +const runDays = computed(() => { + if (!tenantCreateTime.value) return 0; + return Math.floor((now.value - new Date(tenantCreateTime.value).getTime()) / (24 * 60 * 60 * 1000)); +}); const userCount = computed(() => statisticsStore.userCount); const orderCount = computed(() => statisticsStore.orderCount); const totalSales = computed(() => statisticsStore.totalSales); @@ -279,7 +290,7 @@ const coreStats = computed(() => [ // 待处理事项数据 const pendingShipmentCount = ref(0); const pendingRefundCount = ref(0); -const couponUsedCount = computed(() => statisticsStore.couponUsedCount); +const couponUsedCount = computed(() => statisticsStore.safeCouponUsedCount); // 待处理事项(使用computed确保响应式更新) const todoItems = computed(() => [ @@ -298,12 +309,12 @@ const todayStats = computed(() => ({ // 快速入口 const quickLinks = [ - { to: '/website/field', icon: '⚙️', label: '参数配置', bg: '#eff6ff' }, { to: '/shop/shopOrder', icon: '📦', label: '订单管理', bg: '#f0fdf4' }, - { to: '/system/user', icon: '👥', label: '用户管理', bg: '#fff7ed' }, - { to: '/website/index', icon: '🏪', label: '站点管理', bg: '#faf5ff' }, - { to: '/shopGoods', icon: '🏸', label: '商品管理', bg: '#ecfdf5' }, - { to: '/cmsArticle', icon: '📝', label: '文章管理', bg: '#fefce8' }, + { to: '/shop/shopGoods', icon: '🏸', label: '商品管理', bg: '#ecfdf5' }, + { to: '/shop/shopGoodsCategory', icon: '🗂️', label: '商品分类', bg: '#eff6ff' }, + { to: '/shop/shopCoupon', icon: '🎁', label: '优惠券', bg: '#fff7ed' }, + { to: '/shop/shopUser', icon: '👥', label: '会员管理', bg: '#faf5ff' }, + { to: '/shop/shopSetting', icon: '⚙️', label: '商城设置', bg: '#fefce8' }, ]; // 导航跳转 @@ -348,6 +359,15 @@ const refreshStatistics = async () => { // 加载数据 const loadData = async () => { + // 独立请求租户信息,不受其他请求失败影响 + getTenantInfo() + .then((res) => { + tenantCreateTime.value = res?.createTime || ''; + }) + .catch((e) => { + console.warn('获取租户信息失败:', e); + }); + try { await Promise.all([ siteStore.fetchSiteInfo(), @@ -356,8 +376,9 @@ const loadData = async () => { // 获取待处理事项数据 try { - // 获取待发货订单数(使用 statusFilter=1 对应待发货) + // 获取待发货订单数(type=0商城订单, statusFilter=1待发货) const shipmentResult = await pageShopOrder({ + type: 0, statusFilter: 1, page: 1, limit: 1 @@ -369,8 +390,9 @@ const loadData = async () => { } try { - // 获取退款/售后订单数(使用 statusFilter=6 对应退款/售后) + // 获取退款/售后订单数(type=0商城订单, statusFilter=6退款/售后) const refundResult = await pageShopOrder({ + type: 0, statusFilter: 6, page: 1, limit: 1 @@ -391,11 +413,15 @@ onMounted(async () => { // 开始自动刷新统计数据(每5分钟) statisticsStore.startAutoRefresh(); + + // 运行天数每小时检查一次(跨天自动更新) + runDaysTimer = setInterval(() => { now.value = Date.now(); }, 60 * 60 * 1000); }); onUnmounted(() => { // 组件卸载时停止自动刷新 statisticsStore.stopAutoRefresh(); + clearInterval(runDaysTimer); }); diff --git a/src/views/shop/shopOrder/components/orderInfo.vue b/src/views/shop/shopOrder/components/orderInfo.vue index 28083bd..5bcc165 100644 --- a/src/views/shop/shopOrder/components/orderInfo.vue +++ b/src/views/shop/shopOrder/components/orderInfo.vue @@ -71,9 +71,11 @@ label="支付状态" :labelStyle="{ width: '90px', color: '#808080' }" > - 已付款 + 已付款 待收货付款 - 未付款 + 已确认收款 + 待确认收款 + 未付款 未付款,占场中 @@ -147,7 +149,7 @@ - IC月卡 + 线下付款 @@ -187,7 +189,16 @@ diff --git a/src/views/shop/shopOrder/index.vue b/src/views/shop/shopOrder/index.vue index c138272..72eb21c 100644 --- a/src/views/shop/shopOrder/index.vue +++ b/src/views/shop/shopOrder/index.vue @@ -93,10 +93,15 @@ v-if="record.payType === 8" color="blue" >货到付款 + + 线下付款 待收货付款 + 已确认收款 + 待确认收款 货到付款 + +