merge(dev_jacy): admin redeem detail UI (v3.4.13)
This commit is contained in:
@@ -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<string, unknown>;
|
||||||
|
/** 列表页已脱敏展示时可设为 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 (
|
||||||
|
<Descriptions column={1} bordered size="small">
|
||||||
|
<Descriptions.Item label="核销号">{String(detail.redeemNo ?? '—')}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="方式">{REDEEM_CHANNEL_LABELS[channel as RedeemChannel]}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="核销额">¥{Number(detail.amount ?? 0).toFixed(2)}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="结算额">¥{Number(detail.settleAmount ?? 0).toFixed(2)}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="时间">{fmtTime(String(detail.createdAt ?? ''))}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="用户编号">{user?.userNo ?? '—'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="用户昵称">{user?.nickname?.trim() || '—'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="用户手机">
|
||||||
|
{maskUserPhone ? maskPhone(user?.phone) : user?.phone || '—'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="门店">
|
||||||
|
{store?.name ?? '—'}
|
||||||
|
{store?.cityName ? `(${store.cityName})` : ''}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="门店地址">{store?.address ?? '—'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="合伙人">{store?.partnerAccount?.companyName ?? '—'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="权益券号">{coupon?.couponNo ?? '—'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="关联订单">{coupon?.order?.orderNo ?? '—'}</Descriptions.Item>
|
||||||
|
{Array.isArray(detail.allocations) && (detail.allocations as unknown[]).length > 0 ? (
|
||||||
|
<Descriptions.Item label="券分摊">
|
||||||
|
{(
|
||||||
|
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(';')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
) : null}
|
||||||
|
{detail.payout ? (
|
||||||
|
<Descriptions.Item label="门店结算单">
|
||||||
|
¥{Number((detail.payout as { settleAmount?: number }).settleAmount ?? 0).toFixed(2)}
|
||||||
|
{' / '}
|
||||||
|
{String((detail.payout as { status?: string }).status ?? '—')}
|
||||||
|
</Descriptions.Item>
|
||||||
|
) : null}
|
||||||
|
{detail.rating ? (
|
||||||
|
<Descriptions.Item label="评价">
|
||||||
|
服务 {(detail.rating as { serviceScore?: number }).serviceScore ?? '—'} 分 / 环境{' '}
|
||||||
|
{(detail.rating as { envScore?: number }).envScore ?? '—'} 分
|
||||||
|
</Descriptions.Item>
|
||||||
|
) : null}
|
||||||
|
</Descriptions>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import type { ColumnsType } from 'antd/es/table';
|
|||||||
import type { AdminBenefitGrantRequest } from '@dukang/shared-types';
|
import type { AdminBenefitGrantRequest } from '@dukang/shared-types';
|
||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import { COUPON_STATUS_LABELS, fmtTime } from '../lib/constants';
|
import { COUPON_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||||
|
import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
@@ -242,7 +243,8 @@ export default function BenefitCouponsPage() {
|
|||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
title="权益券详情"
|
title="权益券详情"
|
||||||
width={720}
|
width={980}
|
||||||
|
styles={{ body: { paddingBottom: 24 } }}
|
||||||
open={drawerOpen}
|
open={drawerOpen}
|
||||||
onClose={() => setDrawerOpen(false)}
|
onClose={() => setDrawerOpen(false)}
|
||||||
extra={
|
extra={
|
||||||
@@ -310,14 +312,15 @@ export default function BenefitCouponsPage() {
|
|||||||
size="small"
|
size="small"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
tableLayout="fixed"
|
||||||
locale={{ emptyText: '暂无关联核销单' }}
|
locale={{ emptyText: '暂无关联核销单' }}
|
||||||
dataSource={detail.redeemRecords ?? []}
|
dataSource={detail.redeemRecords ?? []}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: '核销号', dataIndex: 'redeemNo', width: 160, ellipsis: true },
|
{ title: '核销号', dataIndex: 'redeemNo', ellipsis: true },
|
||||||
{
|
{
|
||||||
title: '角色',
|
title: '角色',
|
||||||
dataIndex: 'role',
|
dataIndex: 'role',
|
||||||
width: 70,
|
width: 72,
|
||||||
render: (v: string | undefined) =>
|
render: (v: string | undefined) =>
|
||||||
v === 'SECONDARY' ? <Tag color="orange">次券</Tag> : <Tag color="blue">主券</Tag>,
|
v === 'SECONDARY' ? <Tag color="orange">次券</Tag> : <Tag color="blue">主券</Tag>,
|
||||||
},
|
},
|
||||||
@@ -331,31 +334,31 @@ export default function BenefitCouponsPage() {
|
|||||||
{
|
{
|
||||||
title: '本券分摊',
|
title: '本券分摊',
|
||||||
dataIndex: 'couponAmount',
|
dataIndex: 'couponAmount',
|
||||||
width: 95,
|
width: 96,
|
||||||
render: (v: number | undefined, row: CouponRedeemRecord) =>
|
render: (v: number | undefined, row: CouponRedeemRecord) =>
|
||||||
`¥${Number(v ?? row.amount).toFixed(2)}`,
|
`¥${Number(v ?? row.amount).toFixed(2)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '核销总额',
|
title: '核销总额',
|
||||||
dataIndex: 'amount',
|
dataIndex: 'amount',
|
||||||
width: 90,
|
width: 96,
|
||||||
render: (v: number) => `¥${Number(v).toFixed(2)}`,
|
render: (v: number) => `¥${Number(v).toFixed(2)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '结算额',
|
title: '结算额',
|
||||||
dataIndex: 'settleAmount',
|
dataIndex: 'settleAmount',
|
||||||
width: 90,
|
width: 96,
|
||||||
render: (v: number) => `¥${Number(v).toFixed(2)}`,
|
render: (v: number) => `¥${Number(v).toFixed(2)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '时间',
|
title: '时间',
|
||||||
dataIndex: 'createdAt',
|
dataIndex: 'createdAt',
|
||||||
width: 150,
|
width: 148,
|
||||||
render: (v: string) => fmtTime(v),
|
render: (v: string) => fmtTime(v),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 70,
|
width: 64,
|
||||||
render: (_: unknown, row: CouponRedeemRecord) => (
|
render: (_: unknown, row: CouponRedeemRecord) => (
|
||||||
<Button type="link" size="small" onClick={() => void openRedeemDetail(row.id)}>
|
<Button type="link" size="small" onClick={() => void openRedeemDetail(row.id)}>
|
||||||
详情
|
详情
|
||||||
@@ -374,7 +377,7 @@ export default function BenefitCouponsPage() {
|
|||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
title="核销单详情"
|
title="核销单详情"
|
||||||
width={520}
|
width={640}
|
||||||
open={redeemDrawerOpen}
|
open={redeemDrawerOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setRedeemDrawerOpen(false);
|
setRedeemDrawerOpen(false);
|
||||||
@@ -385,79 +388,7 @@ export default function BenefitCouponsPage() {
|
|||||||
{redeemDetailLoading ? (
|
{redeemDetailLoading ? (
|
||||||
<Typography.Text type="secondary">加载中…</Typography.Text>
|
<Typography.Text type="secondary">加载中…</Typography.Text>
|
||||||
) : redeemDetail ? (
|
) : redeemDetail ? (
|
||||||
<Descriptions column={1} bordered size="small">
|
<RedeemRecordDetailDescriptions detail={redeemDetail} />
|
||||||
<Descriptions.Item label="核销号">{String(redeemDetail.redeemNo ?? '—')}</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="核销额">
|
|
||||||
¥{Number(redeemDetail.amount ?? 0).toFixed(2)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="结算额">
|
|
||||||
¥{Number(redeemDetail.settleAmount ?? 0).toFixed(2)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="时间">{fmtTime(String(redeemDetail.createdAt ?? ''))}</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户">
|
|
||||||
{String((redeemDetail.user as { userNo?: string } | undefined)?.userNo ?? '—')}
|
|
||||||
{(redeemDetail.user as { phone?: string | null } | undefined)?.phone
|
|
||||||
? ` / ${(redeemDetail.user as { phone?: string | null }).phone}`
|
|
||||||
: ''}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="门店">
|
|
||||||
{String((redeemDetail.store as { name?: string } | undefined)?.name ?? '—')}
|
|
||||||
{(redeemDetail.store as { cityName?: string } | undefined)?.cityName
|
|
||||||
? `(${(redeemDetail.store as { cityName?: string }).cityName})`
|
|
||||||
: ''}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="门店地址">
|
|
||||||
{String((redeemDetail.store as { address?: string } | undefined)?.address ?? '—')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="合伙人">
|
|
||||||
{String(
|
|
||||||
(redeemDetail.store as { partnerAccount?: { companyName?: string } } | undefined)
|
|
||||||
?.partnerAccount?.companyName ?? '—',
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="权益券号">
|
|
||||||
{String((redeemDetail.coupon as { couponNo?: string } | undefined)?.couponNo ?? '—')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="关联订单">
|
|
||||||
{String(
|
|
||||||
(redeemDetail.coupon as { order?: { orderNo?: string } } | undefined)?.order?.orderNo ??
|
|
||||||
'—',
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
{Array.isArray(redeemDetail.allocations) &&
|
|
||||||
(redeemDetail.allocations as unknown[]).length > 0 ? (
|
|
||||||
<Descriptions.Item label="券分摊">
|
|
||||||
{(
|
|
||||||
redeemDetail.allocations as Array<{
|
|
||||||
couponNo?: string;
|
|
||||||
orderNo?: string | null;
|
|
||||||
amount?: number;
|
|
||||||
sortOrder?: number;
|
|
||||||
}>
|
|
||||||
)
|
|
||||||
.map((a, idx) => {
|
|
||||||
const role = idx === 0 ? '主' : '次';
|
|
||||||
return `${role} ${a.couponNo ?? '—'}¥${Number(a.amount ?? 0).toFixed(2)}${
|
|
||||||
a.orderNo ? `(订单 ${a.orderNo})` : ''
|
|
||||||
}`;
|
|
||||||
})
|
|
||||||
.join(';')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
) : null}
|
|
||||||
{redeemDetail.payout ? (
|
|
||||||
<Descriptions.Item label="门店结算单">
|
|
||||||
¥{Number((redeemDetail.payout as { settleAmount?: number }).settleAmount ?? 0).toFixed(2)}
|
|
||||||
{' / '}
|
|
||||||
{String((redeemDetail.payout as { status?: string }).status ?? '—')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
) : null}
|
|
||||||
{redeemDetail.rating ? (
|
|
||||||
<Descriptions.Item label="评价">
|
|
||||||
服务 {(redeemDetail.rating as { serviceScore?: number }).serviceScore ?? '—'} 分 / 环境{' '}
|
|
||||||
{(redeemDetail.rating as { envScore?: number }).envScore ?? '—'} 分
|
|
||||||
</Descriptions.Item>
|
|
||||||
) : null}
|
|
||||||
</Descriptions>
|
|
||||||
) : null}
|
) : null}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, Descriptions, Drawer, Form, Input, Select, Table, Tag, Typography } from 'antd';
|
import { Button, Drawer, Form, Input, Select, Table, Tag, Typography } from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { REDEEM_CHANNEL_LABELS, type RedeemChannel } from '@dukang/shared-types';
|
import { REDEEM_CHANNEL_LABELS, type RedeemChannel } from '@dukang/shared-types';
|
||||||
|
import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions';
|
||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
@@ -39,6 +40,17 @@ export default function RedeemRecordsPage() {
|
|||||||
);
|
);
|
||||||
const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
|
const [detail, setDetail] = useState<Record<string, unknown> | null>(null);
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
|
|
||||||
|
async function openDetail(id: string) {
|
||||||
|
setDetailLoading(true);
|
||||||
|
setDrawerOpen(true);
|
||||||
|
try {
|
||||||
|
setDetail(await request(`/admin/redeem-records/${id}`));
|
||||||
|
} finally {
|
||||||
|
setDetailLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns: ColumnsType<Row> = [
|
const columns: ColumnsType<Row> = [
|
||||||
{ title: '核销号', dataIndex: 'redeemNo', width: 180 },
|
{ title: '核销号', dataIndex: 'redeemNo', width: 180 },
|
||||||
@@ -77,14 +89,7 @@ export default function RedeemRecordsPage() {
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (_, row) => (
|
render: (_, row) => (
|
||||||
<Button
|
<Button type="link" size="small" onClick={() => void openDetail(row.id)}>
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
onClick={async () => {
|
|
||||||
setDetail(await request(`/admin/redeem-records/${row.id}`));
|
|
||||||
setDrawerOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
详情
|
详情
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@@ -142,35 +147,21 @@ export default function RedeemRecordsPage() {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Drawer title="核销详情" width={520} open={drawerOpen} onClose={() => setDrawerOpen(false)}>
|
<Drawer
|
||||||
{detail && (
|
title="核销详情"
|
||||||
<Descriptions column={1} bordered size="small">
|
width={640}
|
||||||
<Descriptions.Item label="核销号">{String(detail.redeemNo)}</Descriptions.Item>
|
open={drawerOpen}
|
||||||
<Descriptions.Item label="方式">
|
onClose={() => {
|
||||||
{
|
setDrawerOpen(false);
|
||||||
REDEEM_CHANNEL_LABELS[
|
setDetail(null);
|
||||||
(detail.channel === 'PHONE' ? 'PHONE' : 'SCAN') as RedeemChannel
|
}}
|
||||||
]
|
destroyOnClose
|
||||||
}
|
>
|
||||||
</Descriptions.Item>
|
{detailLoading ? (
|
||||||
<Descriptions.Item label="核销额">¥{String(detail.amount)}</Descriptions.Item>
|
<Typography.Text type="secondary">加载中…</Typography.Text>
|
||||||
<Descriptions.Item label="结算额">¥{String(detail.settleAmount)}</Descriptions.Item>
|
) : detail ? (
|
||||||
{detail.user && typeof detail.user === 'object' ? (
|
<RedeemRecordDetailDescriptions detail={detail} />
|
||||||
<>
|
|
||||||
<Descriptions.Item label="用户编号">
|
|
||||||
{String((detail.user as { userNo?: string }).userNo ?? '—')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户昵称">
|
|
||||||
{String((detail.user as { nickname?: string | null }).nickname ?? '—')}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户手机">
|
|
||||||
{maskPhone((detail.user as { phone?: string | null }).phone)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
</>
|
|
||||||
) : null}
|
) : null}
|
||||||
<Descriptions.Item label="时间">{fmtTime(String(detail.createdAt))}</Descriptions.Item>
|
|
||||||
</Descriptions>
|
|
||||||
)}
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -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) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
| 模块 | 内容 |
|
| 模块 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| admin-web | 推广码 attributionCount + **指标事件日志/ECharts 趋势**;核销记录用户信息;技术支持工单优先级;**大图上传前压缩** |
|
| admin-web | 推广码 attributionCount + **指标事件日志/ECharts 趋势**;**权益券详情加宽**;**核销记录详情增强(对齐权益券内核销详情)**;技术支持工单优先级;**大图上传前压缩** |
|
||||||
| mini-user | 门店电话脱敏+拨打埋点;门头/套餐展示;商品去分享+首图 preview;提货确认弹框;**我的页版本号 v3.4.x**;**低于 minClientVersion 强制更新/点我知道了退出**;**物流增强(签收照/拨号/时间线/ETA)**;**头像超 10MB 压缩** |
|
| mini-user | 门店电话脱敏+拨打埋点;门头/套餐展示;商品去分享+首图 preview;提货确认弹框;**我的页版本号 v3.4.x**;**低于 minClientVersion 强制更新/点我知道了退出**;**物流增强(签收照/拨号/时间线/ETA)**;**头像超 10MB 压缩** |
|
||||||
| h5-partner / h5-shop / h5-user | 登录 phone/code 前端校验;**OSS 图片超 10MB 自动 canvas 压缩后上传** |
|
| h5-partner / h5-shop / h5-user | 登录 phone/code 前端校验;**OSS 图片超 10MB 自动 canvas 压缩后上传** |
|
||||||
| 后端 | 工单 priority;client-config minClientVersion;合伙人微信暂停禁登;**Courier 适配器**;**log_promo_event 推广码指标日志** |
|
| 后端 | 工单 priority;client-config minClientVersion;合伙人微信暂停禁登;**Courier 适配器**;**log_promo_event 推广码指标日志** |
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
| ST1785906800359657 | 合伙人暂停后禁登 | ✅ loginPartnerWechat status |
|
| ST1785906800359657 | 合伙人暂停后禁登 | ✅ loginPartnerWechat status |
|
||||||
| ST1785906592340255 | PARTNER_H5 login 校验 | ✅ 前端空字段拦截 |
|
| ST1785906592340255 | PARTNER_H5 login 校验 | ✅ 前端空字段拦截 |
|
||||||
| ST1785906390321653 | 现场提货提交确认弹框 | ✅ showModal |
|
| ST1785906390321653 | 现场提货提交确认弹框 | ✅ showModal |
|
||||||
| ST1785905773501871 | 核销记录用户信息 | ✅ 列表+详情 |
|
| ST1785905773501871 | 核销记录用户信息 | ✅ 列表+详情;**详情含门店/合伙人/券分摊/结算/评价** |
|
||||||
| ST1785904849234806 | 工单中心 | ✅ 已有 |
|
| ST1785904849234806 | 工单中心 | ✅ 已有 |
|
||||||
| ST1785904076632841 | 门店列表开城合伙人 | ✅ 已有 |
|
| ST1785904076632841 | 门店列表开城合伙人 | ✅ 已有 |
|
||||||
| ST1785902173093977 | 去掉商品详情分享按钮 | ✅ 移除 ShareNavButton |
|
| ST1785902173093977 | 去掉商品详情分享按钮 | ✅ 移除 ShareNavButton |
|
||||||
@@ -145,7 +145,8 @@ POST https://api-test.dukanghaoke.com/api/v1/callbacks/courier/xfx/track
|
|||||||
|
|
||||||
- [ ] 推广码详情展示 attributionCount;**四指标事件日志 + ECharts 按日/按时趋势 + 高峰标注**
|
- [ ] 推广码详情展示 attributionCount;**四指标事件日志 + ECharts 按日/按时趋势 + 高峰标注**
|
||||||
- [ ] 推广码 SCAN/ATTRIBUTION/REGISTER/ORDER 事件含 time + IP;幂等(重复 touch 不计 SCAN)
|
- [ ] 推广码 SCAN/ATTRIBUTION/REGISTER/ORDER 事件含 time + IP;幂等(重复 touch 不计 SCAN)
|
||||||
- [ ] 核销记录含 userNo/nickname/phone
|
- [ ] 核销记录含 userNo/nickname/phone;**详情抽屉展示门店/地址/合伙人/权益券号/关联订单/券分摊/结算单/评价**
|
||||||
|
- [ ] **权益券详情抽屉加宽(980px),内嵌核销列表无横向滚动条**
|
||||||
- [ ] 技术支持可创建/筛选/编辑优先级
|
- [ ] 技术支持可创建/筛选/编辑优先级
|
||||||
- [ ] 合伙人/门店登录空字段前端提示;暂停合伙人微信登录被拒
|
- [ ] 合伙人/门店登录空字段前端提示;暂停合伙人微信登录被拒
|
||||||
- [ ] mini-user:我的页左下角显示 `杜康好客 v3.4.x`;低于 `minClientVersion` 时强制更新,无新包则点「我知道了」退出小程序
|
- [ ] mini-user:我的页左下角显示 `杜康好客 v3.4.x`;低于 `minClientVersion` 时强制更新,无新包则点「我知道了」退出小程序
|
||||||
|
|||||||
Reference in New Issue
Block a user