代下单功能
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Breadcrumb, Button, Space, Spin, Tabs, Tag, Typography } from 'antd';
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
PROMO_CODE_STATUS_LABELS,
|
||||
type PromoCodeItem,
|
||||
type PromoCodeStats,
|
||||
} from '@dukang/shared-types';
|
||||
import { request } from '../../lib/api';
|
||||
|
||||
export type PromoCodeDetailContext = {
|
||||
detail: PromoCodeItem & { stats?: PromoCodeStats };
|
||||
reload: () => Promise<void>;
|
||||
};
|
||||
|
||||
export default function PromoCodeDetailLayout() {
|
||||
const { id = '' } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [detail, setDetail] = useState<(PromoCodeItem & { stats?: PromoCodeStats }) | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
async function loadDetail() {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await request<PromoCodeItem & { stats?: PromoCodeStats }>(`/admin/promo-codes/${id}`);
|
||||
setDetail(res);
|
||||
} catch {
|
||||
setDetail(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void loadDetail();
|
||||
}, [id]);
|
||||
|
||||
const tabKey = location.pathname.endsWith('/users') ? 'users' : 'overview';
|
||||
|
||||
if (loading) {
|
||||
return <Spin style={{ display: 'block', margin: '80px auto' }} />;
|
||||
}
|
||||
|
||||
if (!detail) {
|
||||
return (
|
||||
<div>
|
||||
<Typography.Text type="danger">推广码不存在或加载失败</Typography.Text>
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<Button onClick={() => navigate('/promo-codes')}>返回列表</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Breadcrumb
|
||||
style={{ marginBottom: 12 }}
|
||||
items={[
|
||||
{ title: <Link to="/promo-codes">推广码管理</Link> },
|
||||
{ title: detail.name },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Space align="center">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate('/promo-codes')}
|
||||
style={{ marginLeft: -8 }}
|
||||
/>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{detail.name}
|
||||
</Typography.Title>
|
||||
<Tag color={detail.status === 'ACTIVE' ? 'green' : 'default'}>
|
||||
{PROMO_CODE_STATUS_LABELS[detail.status] || detail.status}
|
||||
</Tag>
|
||||
</Space>
|
||||
<Typography.Text type="secondary" copyable={{ text: detail.code }}>
|
||||
码值:{detail.code}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Tabs
|
||||
activeKey={tabKey}
|
||||
onChange={(key) => {
|
||||
if (key === 'users') navigate(`/promo-codes/${id}/users`);
|
||||
else navigate(`/promo-codes/${id}`);
|
||||
}}
|
||||
items={[
|
||||
{ key: 'overview', label: '概览' },
|
||||
{
|
||||
key: 'users',
|
||||
label: `关联用户${detail.stats?.sourceMarkedCount != null ? ` (${detail.stats.sourceMarkedCount})` : ''}`,
|
||||
},
|
||||
]}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Outlet context={{ detail, reload: loadDetail } satisfies PromoCodeDetailContext} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user