3ef4432b2f
Share redeem detail fields across benefit coupon and redeem records pages; widen coupon drawer to avoid horizontal scroll. Co-authored-by: Cursor <cursoragent@cursor.com>
85 lines
3.8 KiB
TypeScript
85 lines
3.8 KiB
TypeScript
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>
|
||
);
|
||
}
|