From a2640ba7dca96c884f9923a85d2546a02351c981 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 20:27:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(shop):=20=E5=AE=9E=E6=97=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=BB=8A=E6=97=A5=E6=95=B0=E6=8D=AE=E5=8F=8A=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=95=86=E5=9F=8E=E8=AE=BE=E7=BD=AE=E4=B8=8E=E5=95=86?= =?UTF-8?q?=E5=93=81=E5=88=97=E8=A1=A8=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 dashboard 今日数据统计,改为实时计算订单、销售额、用户数与优惠券使用量 - 统计数据写入 cmsStatistics 表,新增 couponUsedCount 状态与 getter - dashboard 页面移除原有按日期查询逻辑,优惠券使用数改为响应式读取 - 商城设置 Logo 上传改为直接调用 OSS 接口上传,移除图片库选择组件 - 新增商品列表中市场价和会员价列,数据为空时显示 "-" - 同步更新商品列表导出功能,包含新增的价格列与列宽配置 - 商品列表商品图片添加 OSS 压缩访问支持 - 调整商品分类搜索框与关键词输入框宽度以优化布局 --- .workbuddy/memory/2026-07-14.md | 54 ++++++++++++ src/store/modules/statistics.ts | 86 ++++++++++++------- src/views/shop/dashboard/index.vue | 26 ++---- .../shop/shopGoods/components/search.vue | 4 +- src/views/shop/shopGoods/index.vue | 26 +++++- .../shop/shopSetting/components/basic.vue | 70 +++++++++------ 6 files changed, 190 insertions(+), 76 deletions(-) diff --git a/.workbuddy/memory/2026-07-14.md b/.workbuddy/memory/2026-07-14.md index 6fd5158..b770b64 100644 --- a/.workbuddy/memory/2026-07-14.md +++ b/.workbuddy/memory/2026-07-14.md @@ -35,6 +35,29 @@ - `src/views/shop/shopOrder/useOrderNotify.ts` 增加 `onNewOrder` 回调参数,检测到新订单触发提醒(叮声+语音)时同时调用回调。 - `src/views/shop/shopOrder/index.vue` 调用 `useOrderNotify({ onNewOrder: () => reload() })`,使新订单提醒时自动刷新表格数据,保持数据同步。 +## 2026-07-14 实时统计 Dashboard 今日数据 + +### 问题 +用户再次反馈 `/shop/dashboard` 的「今日数据概况」与「待处理事项」全部显示为 0,且已确认存在今日订单/用户数据。根本原因是 `cmsStatistics` 统计表未写入今日数据,而前端仍依赖该表读取 todaySales/todayOrders/todayUsers;另外 dashboard 中直接按日期查询时使用了 `YYYY-MM-DD` 格式,缺少时间部分,导致时间范围不匹配。 + +### 修复 +- `src/store/modules/statistics.ts`: + - 引入 `dayjs` 与 `listShopOrder`。 + - 在 `fetchStatistics` 中实时调用 `listShopOrder({ createTimeStart, createTimeEnd })` 获取今日订单列表,直接计算 `todayOrders`、`todaySales`(仅已付款订单)、`couponUsedCount`。 + - 调用 `pageUsers({ createTimeStart, createTimeEnd })` 获取 `todayUsers`。 + - 将今日数据同步写入 `cmsStatistics` 表(若存在记录),同时新增 `couponUsedCount` store 状态与 getter。 +- `src/views/shop/dashboard/index.vue`: + - 移除原来使用 `createTimeStart/End: today`(仅日期)查询今日订单并作为优惠券使用数的占位逻辑。 + - `couponUsedCount` 改为从 `statisticsStore.couponUsedCount` 读取。 + - 今日销售额使用 `formatMoney` 保留两位小数显示。 + +### 验证 +- `pnpm run build` 成功。 +- `vue-tsc --noEmit` 在改动文件中未引入新错误。 + +### 备注 +- 待发货/退款仍使用 `statusFilter=1` 与 `statusFilter=6`;若后续出现货到付款订单未计入待发货,需再检查订单 `payStatus` 在后端是否被正确标记为已付款。 + ## 2026-07-14 订单列表商品图片接入 OSS 压缩 ### 改动内容 @@ -47,3 +70,34 @@ ### 待办 - 用户提到后端也需要此方法,但当前工作区只有前端代码,后端实现需在后端项目补充。 + +## 2026-07-14 商城设置 Logo 改为直传 + +### 修改文件 +- `src/views/shop/shopSetting/components/basic.vue` + +### 修改内容 +- 将商城 Logo 上传方式从 `SelectFile` 图片库选择改为 `a-upload` + `uploadOss` 直接上传,与商品编辑弹窗的商品图片上传方式保持一致。 +- 已上传图片使用 `a-image` 预览,下方提供"上传"按钮覆盖替换。 +- 删除不再使用的 `SelectFile` 导入、`logoList` 变量、`chooseImage`/`onDeleteImage` 回调。 +- 使用 `ele-admin-pro` 的 `messageLoading` 保持上传加载提示与商品编辑弹窗一致。 + +## 2026-07-14 商品列表增加三价格展示 + +### 改动文件 +- `src/views/shop/shopGoods/index.vue` + +### 列表页列配置 +- 原来只显示一列「价格」(`price`) +- 新增「市场价」列 (`salePrice`),数据为空时显示 `-` +- 新增「会员价」列 (`dealerPrice`),数据为空时显示 `-` +- 三列均设置 `width: 110`、`align: 'center'`、`customRender` 带 `¥` 前缀 + +### 导出功能同步更新 +- 导出表头增加「市场价」「会员价」两列 +- 数据行对应增加 `goods.salePrice` 和 `goods.dealerPrice` 输出 +- 列宽配置同步增加两列 `{ wch: 12 }` + +### 备注 +- 编辑表单 `shopGoodsEdit.vue` 已支持三个价格编辑(价格/市场价/会员价),无需修改 +- 数据模型字段映射:价格=price, 市场价=salePrice, 会员价=dealerPrice diff --git a/src/store/modules/statistics.ts b/src/store/modules/statistics.ts index 9f5823b..6295838 100644 --- a/src/store/modules/statistics.ts +++ b/src/store/modules/statistics.ts @@ -2,8 +2,9 @@ * 统计数据 store */ import { defineStore } from 'pinia'; +import dayjs from 'dayjs'; import { pageUsers } from '@/api/system/user'; -import { pageShopOrder, shopOrderTotal } from '@/api/shop/shopOrder'; +import { pageShopOrder, shopOrderTotal, listShopOrder } from '@/api/shop/shopOrder'; import { addCmsStatistics, listCmsStatistics, @@ -23,6 +24,8 @@ export interface StatisticsState { cacheExpiry: number; // 自动刷新定时器 refreshTimer: number | null; + // 今日使用优惠券数量 + couponUsedCount: number; } export const useStatisticsStore = defineStore('statistics', { @@ -32,7 +35,8 @@ export const useStatisticsStore = defineStore('statistics', { lastUpdateTime: null, // 默认缓存5分钟 cacheExpiry: 5 * 60 * 1000, - refreshTimer: null + refreshTimer: null, + couponUsedCount: 0 }), getters: { @@ -85,6 +89,13 @@ export const useStatisticsStore = defineStore('statistics', { return safeNumber(state.statistics?.todayUsers); }, + /** + * 获取今日使用优惠券数量 + */ + couponUsedCount: (state): number => { + return safeNumber(state.couponUsedCount); + }, + /** * 检查缓存是否有效 */ @@ -183,38 +194,49 @@ export const useStatisticsStore = defineStore('statistics', { return 0; })(); - // 安全获取今日销售额 - const todaySales = (() => { - if (statisticsData && statisticsData.length > 0) { - const stats = statisticsData[0]; - if (stats.todaySales !== undefined && stats.todaySales !== null) { - return safeNumber(stats.todaySales); - } - } - return 0; - })(); + // 实时计算今日数据(不依赖可能未更新的统计表) + const todayStart = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'); + const todayEnd = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss'); - // 安全获取今日订单数 - const todayOrders = (() => { - if (statisticsData && statisticsData.length > 0) { - const stats = statisticsData[0]; - if (stats.todayOrders !== undefined && stats.todayOrders !== null) { - return safeNumber(stats.todayOrders); - } + // 安全获取今日订单列表、销售额和优惠券使用量 + let todayOrders = 0; + let todaySales = 0; + let couponUsedCount = 0; + try { + const todayOrderList = await listShopOrder({ + createTimeStart: todayStart, + createTimeEnd: todayEnd + }); + if (Array.isArray(todayOrderList)) { + todayOrders = todayOrderList.length; + todaySales = todayOrderList.reduce((acc, order) => { + return acc + (order.payStatus ? safeNumber(order.payPrice) : 0); + }, 0); + couponUsedCount = todayOrderList.filter((order) => { + const couponType = order.couponType; + return couponType !== undefined && couponType !== null && couponType !== 0; + }).length; } - return 0; - })(); + } catch (e) { + console.warn('⚠️ 获取今日订单列表失败:', e); + } // 安全获取今日新增用户 - const todayUsers = (() => { - if (statisticsData && statisticsData.length > 0) { - const stats = statisticsData[0]; - if (stats.todayUsers !== undefined && stats.todayUsers !== null) { - return safeNumber(stats.todayUsers); - } + let todayUsers = 0; + try { + const todayUsersResult = await pageUsers({ + page: 1, + limit: 1, + createTimeStart: todayStart, + createTimeEnd: todayEnd + }); + if (todayUsersResult && typeof todayUsersResult === 'object' && 'count' in todayUsersResult) { + todayUsers = safeNumber(todayUsersResult.count); } - return 0; - })(); + } catch (e) { + console.warn('⚠️ 获取今日新增用户失败:', e); + } + const totalSales = (() => { if (!total) { console.warn('⚠️ 订单总额API返回空数据'); @@ -244,7 +266,10 @@ export const useStatisticsStore = defineStore('statistics', { id: existingStatistics.id, userCount: userCount, orderCount: orderCount, - totalSales: totalSales + totalSales: totalSales, + todaySales: todaySales, + todayOrders: todayOrders, + todayUsers: todayUsers }; // 异步更新数据库 @@ -293,6 +318,7 @@ export const useStatisticsStore = defineStore('statistics', { } this.statistics = statistics; + this.couponUsedCount = couponUsedCount; this.lastUpdateTime = Date.now(); return statistics; diff --git a/src/views/shop/dashboard/index.vue b/src/views/shop/dashboard/index.vue index facb508..51f232d 100644 --- a/src/views/shop/dashboard/index.vue +++ b/src/views/shop/dashboard/index.vue @@ -99,7 +99,7 @@
💰
-
{{ todayStats.salesAmount || '0.00' }}
+
{{ formatMoney(todayStats.salesAmount) }}
今日销售额(元)
@@ -267,7 +267,7 @@ const coreStats = computed(() => [ // 待处理事项数据 const pendingShipmentCount = ref(0); const pendingRefundCount = ref(0); -const couponUsedCount = ref(0); +const couponUsedCount = computed(() => statisticsStore.couponUsedCount); // 待处理事项(使用computed确保响应式更新) const todoItems = computed(() => [ @@ -301,6 +301,12 @@ const navigateTo = (path: string) => { } }; +// 金额格式化(保留两位小数) +const formatMoney = (value?: number | string) => { + const num = Number(value); + return Number.isFinite(num) ? num.toFixed(2) : '0.00'; +}; + // 清除缓存 const handleClearCache = () => { removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then( @@ -358,22 +364,6 @@ const loadData = async () => { pendingRefundCount.value = 0; } - try { - // 获取今日订单总数(用于计算优惠券使用率) - const today = new Date().toISOString().split('T')[0]; - const todayOrderResult = await pageShopOrder({ - createTimeStart: today, - createTimeEnd: today, - page: 1, - limit: 1 - }); - // 暂时使用今日订单数作为优惠券使用参考 - couponUsedCount.value = todayOrderResult?.count || 0; - } catch (e) { - console.warn('获取今日订单数失败:', e); - couponUsedCount.value = 0; - } - } catch (error) { console.error('加载数据失败:', error); } diff --git a/src/views/shop/shopGoods/components/search.vue b/src/views/shop/shopGoods/components/search.vue index dc73773..42459a9 100644 --- a/src/views/shop/shopGoods/components/search.vue +++ b/src/views/shop/shopGoods/components/search.vue @@ -66,7 +66,7 @@ allow-clear :tree-data="navigationList" tree-default-expand-all - style="width: 240px" + style="width: 180px" :listHeight="700" placeholder="请选择分类" :value="where.categoryId || undefined" @@ -77,7 +77,7 @@ (text ? `¥${text}` : '-') + }, { title: '价格', dataIndex: 'price', key: 'price', align: 'center', + width: 110, customRender: ({ text }) => `¥${text}` }, + { + title: '会员价', + dataIndex: 'dealerPrice', + key: 'dealerPrice', + align: 'center', + width: 110, + customRender: ({ text }) => (text ? `¥${text}` : '-') + }, { title: '销量', dataIndex: 'sales', @@ -347,6 +365,8 @@ '商品ID', '商品名称', '价格', + '市场价', + '会员价', '销量', '库存', '状态', @@ -368,6 +388,8 @@ `${goods.goodsId || ''}`, `${goods.name || ''}`, `¥${goods.price || 0}`, + `${goods.salePrice ? `¥${goods.salePrice}` : ''}`, + `${goods.dealerPrice ? `¥${goods.dealerPrice}` : ''}`, `${goods.sales || 0}`, `${goods.stock || 0}`, statusMap[goods.status || 0] || '', @@ -388,6 +410,8 @@ { wch: 10 }, { wch: 30 }, { wch: 12 }, + { wch: 12 }, + { wch: 12 }, { wch: 10 }, { wch: 10 }, { wch: 12 }, diff --git a/src/views/shop/shopSetting/components/basic.vue b/src/views/shop/shopSetting/components/basic.vue index 799e63a..6ffe482 100644 --- a/src/views/shop/shopSetting/components/basic.vue +++ b/src/views/shop/shopSetting/components/basic.vue @@ -16,13 +16,27 @@ /> - + + + + 上传 + + +
+ 建议上传 400x400 像素的正方形 Logo,图片大小不超过 10MB。 +