From 3ef4432b2f95a57e049f84443a88216fa7176131 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Thu, 6 Aug 2026 00:27:11 +0800 Subject: [PATCH] feat(admin-web): widen benefit coupon detail and enrich redeem record drawer (v3.4.13) Share redeem detail fields across benefit coupon and redeem records pages; widen coupon drawer to avoid horizontal scroll. Co-authored-by: Cursor --- .../RedeemRecordDetailDescriptions.tsx | 84 ++++++++++++++++ .../src/pages/BenefitCouponsPage.tsx | 95 +++---------------- .../admin-web/src/pages/RedeemRecordsPage.tsx | 67 ++++++------- 杜康好客-v3-PRD.md | 2 +- 杜康好客-v3.4.13-体验优化开发文档.md | 7 +- 5 files changed, 131 insertions(+), 124 deletions(-) create mode 100644 apps/admin-web/src/components/RedeemRecordDetailDescriptions.tsx diff --git a/apps/admin-web/src/components/RedeemRecordDetailDescriptions.tsx b/apps/admin-web/src/components/RedeemRecordDetailDescriptions.tsx new file mode 100644 index 0000000..3742597 --- /dev/null +++ b/apps/admin-web/src/components/RedeemRecordDetailDescriptions.tsx @@ -0,0 +1,84 @@ +import { Descriptions } from 'antd'; +import { REDEEM_CHANNEL_LABELS, type RedeemChannel } from '@dukang/shared-types'; +import { fmtTime } from '../lib/constants'; + +function maskPhone(phone: string | null | undefined) { + if (!phone || phone.length < 7) return phone ?? '—'; + return `${phone.slice(0, 3)}****${phone.slice(-4)}`; +} + +type Props = { + detail: Record; + /** 列表页已脱敏展示时可设为 false,详情抽屉展示完整手机号 */ + maskUserPhone?: boolean; +}; + +export default function RedeemRecordDetailDescriptions({ detail, maskUserPhone = false }: Props) { + const user = detail.user as + | { userNo?: string; nickname?: string | null; phone?: string | null } + | undefined; + const store = detail.store as + | { + name?: string; + cityName?: string; + address?: string; + partnerAccount?: { companyName?: string }; + } + | undefined; + const coupon = detail.coupon as { couponNo?: string; order?: { orderNo?: string } } | undefined; + const channel = detail.channel === 'PHONE' ? 'PHONE' : 'SCAN'; + + return ( + + {String(detail.redeemNo ?? '—')} + {REDEEM_CHANNEL_LABELS[channel as RedeemChannel]} + ¥{Number(detail.amount ?? 0).toFixed(2)} + ¥{Number(detail.settleAmount ?? 0).toFixed(2)} + {fmtTime(String(detail.createdAt ?? ''))} + {user?.userNo ?? '—'} + {user?.nickname?.trim() || '—'} + + {maskUserPhone ? maskPhone(user?.phone) : user?.phone || '—'} + + + {store?.name ?? '—'} + {store?.cityName ? `(${store.cityName})` : ''} + + {store?.address ?? '—'} + {store?.partnerAccount?.companyName ?? '—'} + {coupon?.couponNo ?? '—'} + {coupon?.order?.orderNo ?? '—'} + {Array.isArray(detail.allocations) && (detail.allocations as unknown[]).length > 0 ? ( + + {( + detail.allocations as Array<{ + couponNo?: string; + orderNo?: string | null; + amount?: number; + }> + ) + .map((a, idx) => { + const role = idx === 0 ? '主' : '次'; + return `${role} ${a.couponNo ?? '—'}¥${Number(a.amount ?? 0).toFixed(2)}${ + a.orderNo ? `(订单 ${a.orderNo})` : '' + }`; + }) + .join(';')} + + ) : null} + {detail.payout ? ( + + ¥{Number((detail.payout as { settleAmount?: number }).settleAmount ?? 0).toFixed(2)} + {' / '} + {String((detail.payout as { status?: string }).status ?? '—')} + + ) : null} + {detail.rating ? ( + + 服务 {(detail.rating as { serviceScore?: number }).serviceScore ?? '—'} 分 / 环境{' '} + {(detail.rating as { envScore?: number }).envScore ?? '—'} 分 + + ) : null} + + ); +} diff --git a/apps/admin-web/src/pages/BenefitCouponsPage.tsx b/apps/admin-web/src/pages/BenefitCouponsPage.tsx index 8ba2db3..eaa2eef 100644 --- a/apps/admin-web/src/pages/BenefitCouponsPage.tsx +++ b/apps/admin-web/src/pages/BenefitCouponsPage.tsx @@ -18,6 +18,7 @@ import type { ColumnsType } from 'antd/es/table'; import type { AdminBenefitGrantRequest } from '@dukang/shared-types'; import { request } from '../lib/api'; import { COUPON_STATUS_LABELS, fmtTime } from '../lib/constants'; +import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions'; import { useAdminList } from '../lib/useAdminList'; type Row = { @@ -242,7 +243,8 @@ export default function BenefitCouponsPage() { setDrawerOpen(false)} extra={ @@ -310,14 +312,15 @@ export default function BenefitCouponsPage() { size="small" rowKey="id" pagination={false} + tableLayout="fixed" locale={{ emptyText: '暂无关联核销单' }} dataSource={detail.redeemRecords ?? []} columns={[ - { title: '核销号', dataIndex: 'redeemNo', width: 160, ellipsis: true }, + { title: '核销号', dataIndex: 'redeemNo', ellipsis: true }, { title: '角色', dataIndex: 'role', - width: 70, + width: 72, render: (v: string | undefined) => v === 'SECONDARY' ? 次券 : 主券, }, @@ -331,31 +334,31 @@ export default function BenefitCouponsPage() { { title: '本券分摊', dataIndex: 'couponAmount', - width: 95, + width: 96, render: (v: number | undefined, row: CouponRedeemRecord) => `¥${Number(v ?? row.amount).toFixed(2)}`, }, { title: '核销总额', dataIndex: 'amount', - width: 90, + width: 96, render: (v: number) => `¥${Number(v).toFixed(2)}`, }, { title: '结算额', dataIndex: 'settleAmount', - width: 90, + width: 96, render: (v: number) => `¥${Number(v).toFixed(2)}`, }, { title: '时间', dataIndex: 'createdAt', - width: 150, + width: 148, render: (v: string) => fmtTime(v), }, { title: '操作', - width: 70, + width: 64, render: (_: unknown, row: CouponRedeemRecord) => ( ), @@ -142,35 +147,21 @@ export default function RedeemRecordsPage() { }, }} /> - setDrawerOpen(false)}> - {detail && ( - - {String(detail.redeemNo)} - - { - REDEEM_CHANNEL_LABELS[ - (detail.channel === 'PHONE' ? 'PHONE' : 'SCAN') as RedeemChannel - ] - } - - ¥{String(detail.amount)} - ¥{String(detail.settleAmount)} - {detail.user && typeof detail.user === 'object' ? ( - <> - - {String((detail.user as { userNo?: string }).userNo ?? '—')} - - - {String((detail.user as { nickname?: string | null }).nickname ?? '—')} - - - {maskPhone((detail.user as { phone?: string | null }).phone)} - - - ) : null} - {fmtTime(String(detail.createdAt))} - - )} + { + setDrawerOpen(false); + setDetail(null); + }} + destroyOnClose + > + {detailLoading ? ( + 加载中… + ) : detail ? ( + + ) : null} ); diff --git a/杜康好客-v3-PRD.md b/杜康好客-v3-PRD.md index 9b34f25..0f22e37 100644 --- a/杜康好客-v3-PRD.md +++ b/杜康好客-v3-PRD.md @@ -48,7 +48,7 @@ | 版本 | 日期 | 说明 | |------|------|------| -| **3.4.13** | 2026-08-05 | 推广码归因统计 + **指标事件日志/高峰趋势**、核销用户信息、技术支持工单优先级、mini-user **我的页版本号/低于 min 强制退出**、门店/商品/提货/**物流增强(签收照/拨号/ETA/路由回调签收→已完成)**、H5 登录校验、合伙人微信暂停禁登、**OSS 图片超 10MB 客户端压缩**;开发设计见 [`杜康好客-v3.4.13-体验优化开发文档.md`](./杜康好客-v3.4.13-体验优化开发文档.md) | +| **3.4.13** | 2026-08-05 | 推广码归因统计 + **指标事件日志/高峰趋势**、核销用户信息 + **权益券详情加宽/核销详情增强**、技术支持工单优先级、mini-user **我的页版本号/低于 min 强制退出**、门店/商品/提货/**物流增强(签收照/拨号/ETA/路由回调签收→已完成)**、H5 登录校验、合伙人微信暂停禁登、**OSS 图片超 10MB 客户端压缩**;开发设计见 [`杜康好客-v3.4.13-体验优化开发文档.md`](./杜康好客-v3.4.13-体验优化开发文档.md) | --- diff --git a/杜康好客-v3.4.13-体验优化开发文档.md b/杜康好客-v3.4.13-体验优化开发文档.md index 3edc381..9bcccb2 100644 --- a/杜康好客-v3.4.13-体验优化开发文档.md +++ b/杜康好客-v3.4.13-体验优化开发文档.md @@ -7,7 +7,7 @@ | 模块 | 内容 | |------|------| -| admin-web | 推广码 attributionCount + **指标事件日志/ECharts 趋势**;核销记录用户信息;技术支持工单优先级;**大图上传前压缩** | +| admin-web | 推广码 attributionCount + **指标事件日志/ECharts 趋势**;**权益券详情加宽**;**核销记录详情增强(对齐权益券内核销详情)**;技术支持工单优先级;**大图上传前压缩** | | mini-user | 门店电话脱敏+拨打埋点;门头/套餐展示;商品去分享+首图 preview;提货确认弹框;**我的页版本号 v3.4.x**;**低于 minClientVersion 强制更新/点我知道了退出**;**物流增强(签收照/拨号/时间线/ETA)**;**头像超 10MB 压缩** | | h5-partner / h5-shop / h5-user | 登录 phone/code 前端校验;**OSS 图片超 10MB 自动 canvas 压缩后上传** | | 后端 | 工单 priority;client-config minClientVersion;合伙人微信暂停禁登;**Courier 适配器**;**log_promo_event 推广码指标日志** | @@ -24,7 +24,7 @@ | ST1785906800359657 | 合伙人暂停后禁登 | ✅ loginPartnerWechat status | | ST1785906592340255 | PARTNER_H5 login 校验 | ✅ 前端空字段拦截 | | ST1785906390321653 | 现场提货提交确认弹框 | ✅ showModal | -| ST1785905773501871 | 核销记录用户信息 | ✅ 列表+详情 | +| ST1785905773501871 | 核销记录用户信息 | ✅ 列表+详情;**详情含门店/合伙人/券分摊/结算/评价** | | ST1785904849234806 | 工单中心 | ✅ 已有 | | ST1785904076632841 | 门店列表开城合伙人 | ✅ 已有 | | ST1785902173093977 | 去掉商品详情分享按钮 | ✅ 移除 ShareNavButton | @@ -145,7 +145,8 @@ POST https://api-test.dukanghaoke.com/api/v1/callbacks/courier/xfx/track - [ ] 推广码详情展示 attributionCount;**四指标事件日志 + ECharts 按日/按时趋势 + 高峰标注** - [ ] 推广码 SCAN/ATTRIBUTION/REGISTER/ORDER 事件含 time + IP;幂等(重复 touch 不计 SCAN) -- [ ] 核销记录含 userNo/nickname/phone +- [ ] 核销记录含 userNo/nickname/phone;**详情抽屉展示门店/地址/合伙人/权益券号/关联订单/券分摊/结算单/评价** +- [ ] **权益券详情抽屉加宽(980px),内嵌核销列表无横向滚动条** - [ ] 技术支持可创建/筛选/编辑优先级 - [ ] 合伙人/门店登录空字段前端提示;暂停合伙人微信登录被拒 - [ ] mini-user:我的页左下角显示 `杜康好客 v3.4.x`;低于 `minClientVersion` 时强制更新,无新包则点「我知道了」退出小程序