v3.5.8和v3.5.9版本更新
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-08-25 09:20:32 +08:00
parent 7304c7a8e1
commit 5935024ea8
101 changed files with 7640 additions and 5364 deletions
+322 -158
View File
@@ -64,6 +64,20 @@ export default function DashboardPage() {
const [profile, setProfile] = useState<HqProfile | null>(null);
const [deploying, setDeploying] = useState(false);
const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN';
const keys = profile?.permissionKeys ?? [];
const has = (k: string) => keys.includes(k);
const cityScoped = !!profile?.cityScoped;
const canUsers = has('users');
const canOrders = has('orders');
const canStores = has('stores');
const canStoreAudits = has('store_audits');
const canPartners = has('partners');
const canPromo = has('promo_codes');
const canFinance = has('finance');
const canBenefit = has('benefit');
const canDeliveries = has('deliveries');
const canTickets = has('tickets');
const permsReady = profile !== null;
const [filterForm] = Form.useForm<{
range: [Dayjs, Dayjs];
@@ -115,18 +129,26 @@ export default function DashboardPage() {
.catch(() => {
setVersionLoading(false);
});
}, [loadVersion]);
useEffect(() => {
if (!profile) return;
void request<Paginated<CityOption>>(`/admin/cities?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
.then((res) => setCities(res.items ?? []))
.catch(() => setCities([]));
void request<Paginated<PromoOption>>(`/admin/promo-codes?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
.then((res) => setPromos(res.items ?? []))
.catch(() => setPromos([]));
void request<Paginated<{ id: string; companyName?: string | null; name: string }>>(
`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`,
)
.then((res) => setPartners(res.items ?? []))
.catch(() => setPartners([]));
}, [loadVersion]);
if ((profile.permissionKeys ?? []).includes('promo_codes')) {
void request<Paginated<PromoOption>>(`/admin/promo-codes?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`)
.then((res) => setPromos(res.items ?? []))
.catch(() => setPromos([]));
}
if ((profile.permissionKeys ?? []).includes('partners')) {
void request<Paginated<{ id: string; companyName?: string | null; name: string }>>(
`/admin/partners?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`,
)
.then((res) => setPartners(res.items ?? []))
.catch(() => setPartners([]));
}
}, [profile]);
useEffect(() => {
void loadAnalytics(filters);
@@ -206,9 +228,19 @@ export default function DashboardPage() {
const byDateOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byDate ?? [];
const legend: string[] = [];
const series: EChartsOption['series'] = [];
if (canUsers) {
legend.push('新增用户');
series.push({ name: '新增用户', type: 'line', smooth: true, data: rows.map((r) => r.users) });
}
if (canOrders) {
legend.push('订单数');
series.push({ name: '订单数', type: 'line', smooth: true, data: rows.map((r) => r.orders) });
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['新增用户', '订单数'] },
legend: { data: legend },
grid: { left: 40, right: 20, top: 40, bottom: 40 },
xAxis: {
type: 'category',
@@ -216,18 +248,25 @@ export default function DashboardPage() {
axisLabel: { rotate: rows.length > 14 ? 45 : 0 },
},
yAxis: { type: 'value', minInterval: 1 },
series: [
{ name: '新增用户', type: 'line', smooth: true, data: rows.map((r) => r.users) },
{ name: '订单数', type: 'line', smooth: true, data: rows.map((r) => r.orders) },
],
series,
};
}, [analytics]);
}, [analytics, canUsers, canOrders]);
const byCityOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byCity ?? [];
const legend: string[] = [];
const series: EChartsOption['series'] = [];
if (canUsers) {
legend.push('用户');
series.push({ name: '用户', type: 'bar', data: rows.map((r) => r.users), barMaxWidth: 36 });
}
if (canOrders) {
legend.push('订单');
series.push({ name: '订单', type: 'bar', data: rows.map((r) => r.orders), barMaxWidth: 36 });
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['用户', '订单'] },
legend: { data: legend },
grid: { left: 48, right: 20, top: 40, bottom: 48 },
xAxis: {
type: 'category',
@@ -235,18 +274,25 @@ export default function DashboardPage() {
axisLabel: { interval: 0, rotate: rows.length > 4 ? 30 : 0 },
},
yAxis: { type: 'value', minInterval: 1 },
series: [
{ name: '用户', type: 'bar', data: rows.map((r) => r.users), barMaxWidth: 36 },
{ name: '订单', type: 'bar', data: rows.map((r) => r.orders), barMaxWidth: 36 },
],
series,
};
}, [analytics]);
}, [analytics, canUsers, canOrders]);
const byPromoOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byPromo ?? [];
const legend: string[] = [];
const series: EChartsOption['series'] = [];
if (canUsers) {
legend.push('用户');
series.push({ name: '用户', type: 'bar', data: rows.map((r) => r.users), barMaxWidth: 36 });
}
if (canOrders) {
legend.push('订单');
series.push({ name: '订单', type: 'bar', data: rows.map((r) => r.orders), barMaxWidth: 36 });
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['用户', '订单'] },
legend: { data: legend },
grid: { left: 48, right: 20, top: 40, bottom: 64 },
xAxis: {
type: 'category',
@@ -254,18 +300,36 @@ export default function DashboardPage() {
axisLabel: { interval: 0, rotate: rows.length > 3 ? 30 : 0 },
},
yAxis: { type: 'value', minInterval: 1 },
series: [
{ name: '用户', type: 'bar', data: rows.map((r) => r.users), barMaxWidth: 36 },
{ name: '订单', type: 'bar', data: rows.map((r) => r.orders), barMaxWidth: 36 },
],
series,
};
}, [analytics]);
}, [analytics, canUsers, canOrders]);
const opsByDateOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byDate ?? [];
const legend: string[] = [];
const series: NonNullable<EChartsOption['series']> = [];
if (canPartners) {
legend.push('新增合伙人');
series.push({ name: '新增合伙人', type: 'line', smooth: true, data: rows.map((r) => r.partners) });
}
if (canStores) {
legend.push('新签门店');
series.push({ name: '新签门店', type: 'line', smooth: true, data: rows.map((r) => r.stores) });
}
if (canBenefit) {
legend.push('核销笔数', '核销金额');
series.push({ name: '核销笔数', type: 'line', smooth: true, data: rows.map((r) => r.redeems) });
series.push({
name: '核销金额',
type: 'line',
smooth: true,
yAxisIndex: 1,
data: rows.map((r) => r.redeemAmount),
});
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['新增合伙人', '新签门店', '核销笔数', '核销金额'] },
legend: { data: legend },
grid: { left: 48, right: 48, top: 48, bottom: 40 },
xAxis: {
type: 'category',
@@ -276,26 +340,29 @@ export default function DashboardPage() {
{ type: 'value', name: '数量', minInterval: 1 },
{ type: 'value', name: '金额', minInterval: 1 },
],
series: [
{ name: '新增合伙人', type: 'line', smooth: true, data: rows.map((r) => r.partners) },
{ name: '新签门店', type: 'line', smooth: true, data: rows.map((r) => r.stores) },
{ name: '核销笔数', type: 'line', smooth: true, data: rows.map((r) => r.redeems) },
{
name: '核销金额',
type: 'line',
smooth: true,
yAxisIndex: 1,
data: rows.map((r) => r.redeemAmount),
},
],
series,
};
}, [analytics]);
}, [analytics, canPartners, canStores, canBenefit]);
const opsByCityOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byCity ?? [];
const legend: string[] = [];
const series: EChartsOption['series'] = [];
if (canPartners) {
legend.push('合伙人');
series.push({ name: '合伙人', type: 'bar', data: rows.map((r) => r.partners), barMaxWidth: 28 });
}
if (canStores) {
legend.push('门店');
series.push({ name: '门店', type: 'bar', data: rows.map((r) => r.stores), barMaxWidth: 28 });
}
if (canBenefit) {
legend.push('核销笔数');
series.push({ name: '核销笔数', type: 'bar', data: rows.map((r) => r.redeems), barMaxWidth: 28 });
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['合伙人', '门店', '核销笔数'] },
legend: { data: legend },
grid: { left: 48, right: 20, top: 40, bottom: 48 },
xAxis: {
type: 'category',
@@ -303,19 +370,31 @@ export default function DashboardPage() {
axisLabel: { interval: 0, rotate: rows.length > 4 ? 30 : 0 },
},
yAxis: { type: 'value', minInterval: 1 },
series: [
{ name: '合伙人', type: 'bar', data: rows.map((r) => r.partners), barMaxWidth: 28 },
{ name: '门店', type: 'bar', data: rows.map((r) => r.stores), barMaxWidth: 28 },
{ name: '核销笔数', type: 'bar', data: rows.map((r) => r.redeems), barMaxWidth: 28 },
],
series,
};
}, [analytics]);
}, [analytics, canPartners, canStores, canBenefit]);
const opsByPartnerOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byPartner ?? [];
const legend: string[] = [];
const series: NonNullable<EChartsOption['series']> = [];
if (canStores) {
legend.push('门店');
series.push({ name: '门店', type: 'bar', data: rows.map((r) => r.stores), barMaxWidth: 28 });
}
if (canBenefit) {
legend.push('核销笔数', '核销金额');
series.push({ name: '核销笔数', type: 'bar', data: rows.map((r) => r.redeems), barMaxWidth: 28 });
series.push({
name: '核销金额',
type: 'line',
yAxisIndex: 1,
data: rows.map((r) => r.redeemAmount),
});
}
return {
tooltip: { trigger: 'axis' },
legend: { data: ['门店', '核销笔数', '核销金额'] },
legend: { data: legend },
grid: { left: 48, right: 48, top: 40, bottom: 64 },
xAxis: {
type: 'category',
@@ -326,18 +405,9 @@ export default function DashboardPage() {
{ type: 'value', name: '数量', minInterval: 1 },
{ type: 'value', name: '金额' },
],
series: [
{ name: '门店', type: 'bar', data: rows.map((r) => r.stores), barMaxWidth: 28 },
{ name: '核销笔数', type: 'bar', data: rows.map((r) => r.redeems), barMaxWidth: 28 },
{
name: '核销金额',
type: 'line',
yAxisIndex: 1,
data: rows.map((r) => r.redeemAmount),
},
],
series,
};
}, [analytics]);
}, [analytics, canStores, canBenefit]);
const redeemByCityOption = useMemo<EChartsOption>(() => {
const rows = analytics?.byCity ?? [];
@@ -368,7 +438,9 @@ export default function DashboardPage() {
return (
<div>
<Typography.Title level={4}></Typography.Title>
<Typography.Title level={4}>
{cityScoped ? '数据概览(负责城市)' : '数据概览'}
</Typography.Title>
{isSuperAdmin ? (
<Card
@@ -411,46 +483,94 @@ export default function DashboardPage() {
) : null}
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="有效用户" value={stats?.usersTotal ?? 0} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="访客(未验手机)" value={stats?.guestUsers ?? 0} valueStyle={{ color: '#faad14' }} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="已验手机" value={stats?.verifiedUsers ?? 0} valueStyle={{ color: '#52c41a' }} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="今日下单" value={stats?.ordersToday ?? 0} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="门店" value={stats?.storesTotal ?? 0} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="合伙人" value={stats?.partnersTotal ?? 0} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="今日核销" value={stats?.redeemToday ?? 0} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="配送单" value={stats?.deliveriesTotal ?? 0} />
</Card>
</Col>
{!permsReady ? (
<Col xs={24} sm={12} lg={6}>
<Card loading />
</Col>
) : null}
{permsReady && canUsers ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="有效用户" value={stats?.usersTotal ?? 0} />
</Card>
</Col>
) : null}
{permsReady && canUsers ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="访客(未验手机)" value={stats?.guestUsers ?? 0} valueStyle={{ color: '#faad14' }} />
</Card>
</Col>
) : null}
{permsReady && canUsers ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="已验手机" value={stats?.verifiedUsers ?? 0} valueStyle={{ color: '#52c41a' }} />
</Card>
</Col>
) : null}
{permsReady && canOrders ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="今日下单" value={stats?.ordersToday ?? 0} />
</Card>
</Col>
) : null}
{permsReady && canStores ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="门店" value={stats?.storesTotal ?? 0} />
</Card>
</Col>
) : null}
{permsReady && canStores ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading} extra={<Link to="/stores?auditStatus=PENDING"></Link>}>
<Statistic
title="待入驻审核"
value={stats?.pendingStoreOnboard ?? 0}
valueStyle={{ color: (stats?.pendingStoreOnboard ?? 0) > 0 ? '#fa8c16' : undefined }}
/>
</Card>
</Col>
) : null}
{permsReady && canStoreAudits ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading} extra={<Link to="/store-package-audits"></Link>}>
<Statistic
title="待套餐/信息审核"
value={(stats?.pendingStorePackageAudits ?? 0) + (stats?.pendingStoreInfoChanges ?? 0)}
valueStyle={{
color:
(stats?.pendingStorePackageAudits ?? 0) + (stats?.pendingStoreInfoChanges ?? 0) > 0
? '#fa8c16'
: undefined,
}}
/>
</Card>
</Col>
) : null}
{permsReady && canPartners ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="合伙人" value={stats?.partnersTotal ?? 0} />
</Card>
</Col>
) : null}
{permsReady && canBenefit ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="今日核销" value={stats?.redeemToday ?? 0} />
</Card>
</Col>
) : null}
{permsReady && canDeliveries ? (
<Col xs={24} sm={12} lg={6}>
<Card loading={loading}>
<Statistic title="配送单" value={stats?.deliveriesTotal ?? 0} />
</Card>
</Col>
) : null}
</Row>
<Card
@@ -478,62 +598,73 @@ export default function DashboardPage() {
<Form.Item name="cityId" label="城市">
<Select
allowClear
placeholder="全部开城"
placeholder={cityScoped ? '全部负责城市' : '全部开城'}
style={{ width: 160 }}
options={[
{ value: 'none', label: '未选城' },
...(cityScoped ? [] : [{ value: 'none', label: '未选城' }]),
...cities.map((c) => ({ value: c.id, label: c.name })),
]}
/>
</Form.Item>
<Form.Item name="promoCodeId" label="推广码">
<Select
allowClear
placeholder="全部来源"
style={{ width: 200 }}
options={[
{ value: 'none', label: '自然量 / 无推广码' },
...promos.map((p) => ({ value: p.id, label: `${p.name}${p.code}` })),
]}
/>
</Form.Item>
<Form.Item name="partnerAccountId" label="合伙人">
<Select
allowClear
placeholder="全部合伙人"
style={{ width: 200 }}
options={partners.map((p) => ({
value: p.id,
label: p.companyName || p.name,
}))}
/>
</Form.Item>
{canPromo ? (
<Form.Item name="promoCodeId" label="推广码">
<Select
allowClear
placeholder="全部来源"
style={{ width: 200 }}
options={[
{ value: 'none', label: '自然量 / 无推广码' },
...promos.map((p) => ({ value: p.id, label: `${p.name}${p.code}` })),
]}
/>
</Form.Item>
) : null}
{canPartners ? (
<Form.Item name="partnerAccountId" label="合伙人">
<Select
allowClear
placeholder="全部合伙人"
style={{ width: 200 }}
options={partners.map((p) => ({
value: p.id,
label: p.companyName || p.name,
}))}
/>
</Form.Item>
) : null}
<Form.Item>
<Button type="primary" htmlType="submit"></Button>
</Form.Item>
</Form>
</Card>
{canUsers || canOrders ? (
<Card
title="用户 / 订单统计"
title={canUsers && canOrders ? '用户 / 订单统计' : canUsers ? '用户统计' : '订单统计'}
style={{ marginBottom: 24 }}
extra={
<Space>
<Link to={`/orders?${ordersDrillQs}`}></Link>
<Link to="/users"></Link>
{canOrders ? <Link to={`/orders?${ordersDrillQs}`}></Link> : null}
{canUsers ? <Link to="/users"></Link> : null}
</Space>
}
>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col xs={24} sm={8}>
<Statistic title="区间新增用户" value={analytics?.summary.users ?? 0} />
</Col>
<Col xs={24} sm={8}>
<Statistic title="区间订单数" value={analytics?.summary.orders ?? 0} />
</Col>
<Col xs={24} sm={8}>
<Statistic title="区间付费用户" value={analytics?.summary.payingUsers ?? 0} />
</Col>
{canUsers ? (
<Col xs={24} sm={8}>
<Statistic title="区间新增用户" value={analytics?.summary.users ?? 0} />
</Col>
) : null}
{canOrders ? (
<Col xs={24} sm={8}>
<Statistic title="区间订单数" value={analytics?.summary.orders ?? 0} />
</Col>
) : null}
{canOrders ? (
<Col xs={24} sm={8}>
<Statistic title="区间付费用户" value={analytics?.summary.payingUsers ?? 0} />
</Col>
) : null}
</Row>
<Row gutter={[16, 16]}>
@@ -542,7 +673,7 @@ export default function DashboardPage() {
<ReactECharts option={byDateOption} style={{ height: 320 }} notMerge />
</Card>
</Col>
<Col xs={24} lg={12}>
<Col xs={24} lg={canPromo ? 12 : 24}>
<Card
type="inner"
title="按城市(点击柱联动筛选)"
@@ -562,6 +693,7 @@ export default function DashboardPage() {
/>
</Card>
</Col>
{canPromo ? (
<Col xs={24} lg={12}>
<Card
type="inner"
@@ -584,42 +716,53 @@ export default function DashboardPage() {
/>
</Card>
</Col>
) : null}
</Row>
</Card>
) : null}
{canPartners || canStores || canBenefit ? (
<Card
title="合伙人 / 门店 / 核销统计"
title={[canPartners && '合伙人', canStores && '门店', canBenefit && '核销'].filter(Boolean).join(' / ') + '统计'}
style={{ marginBottom: 24 }}
extra={
<Space>
<Link to="/city-partners"></Link>
<Link to="/stores"></Link>
<Link to="/redeem-records"></Link>
{canPartners ? <Link to="/city-partners"></Link> : null}
{canStores ? <Link to="/stores"></Link> : null}
{canBenefit ? <Link to="/redeem-records"></Link> : null}
</Space>
}
>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col xs={24} sm={6}>
<Statistic title="区间新增合伙人" value={analytics?.summary.partners ?? 0} />
</Col>
<Col xs={24} sm={6}>
<Statistic title="区间新签门店" value={analytics?.summary.stores ?? 0} />
</Col>
<Col xs={24} sm={6}>
<Statistic title="区间核销笔数" value={analytics?.summary.redeems ?? 0} />
</Col>
<Col xs={24} sm={6}>
<Statistic title="区间核销金额" value={analytics?.summary.redeemAmount ?? 0} precision={2} />
</Col>
{canPartners ? (
<Col xs={24} sm={6}>
<Statistic title="区间新增合伙人" value={analytics?.summary.partners ?? 0} />
</Col>
) : null}
{canStores ? (
<Col xs={24} sm={6}>
<Statistic title="区间新签门店" value={analytics?.summary.stores ?? 0} />
</Col>
) : null}
{canBenefit ? (
<Col xs={24} sm={6}>
<Statistic title="区间核销笔数" value={analytics?.summary.redeems ?? 0} />
</Col>
) : null}
{canBenefit ? (
<Col xs={24} sm={6}>
<Statistic title="区间核销金额" value={analytics?.summary.redeemAmount ?? 0} precision={2} />
</Col>
) : null}
</Row>
<Row gutter={[16, 16]}>
<Col xs={24}>
<Card type="inner" title="按日趋势(合伙人 / 门店 / 核销)" loading={analyticsLoading} size="small">
<Card type="inner" title="按日趋势" loading={analyticsLoading} size="small">
<ReactECharts option={opsByDateOption} style={{ height: 320 }} notMerge />
</Card>
</Col>
<Col xs={24} lg={12}>
<Col xs={24} lg={canBenefit ? 12 : 24}>
<Card
type="inner"
title="按城市(点击柱联动筛选)"
@@ -639,6 +782,7 @@ export default function DashboardPage() {
/>
</Card>
</Col>
{canBenefit ? (
<Col xs={24} lg={12}>
<Card
type="inner"
@@ -659,6 +803,8 @@ export default function DashboardPage() {
/>
</Card>
</Col>
) : null}
{canPartners && (canStores || canBenefit) ? (
<Col xs={24}>
<Card
type="inner"
@@ -681,10 +827,14 @@ export default function DashboardPage() {
/>
</Card>
</Col>
) : null}
</Row>
</Card>
) : null}
{canFinance || canTickets ? (
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
{canFinance ? (
<Col xs={24} sm={12} lg={8}>
<Card
loading={loading}
@@ -704,6 +854,8 @@ export default function DashboardPage() {
</Typography.Text>
</Card>
</Col>
) : null}
{canFinance ? (
<Col xs={24} sm={12} lg={8}>
<Card
loading={loading}
@@ -717,6 +869,8 @@ export default function DashboardPage() {
/>
</Card>
</Col>
) : null}
{canFinance ? (
<Col xs={24} sm={12} lg={8}>
<Card
loading={loading}
@@ -737,20 +891,28 @@ export default function DashboardPage() {
</Typography.Text>
</Card>
</Col>
) : null}
{canTickets ? (
<Col xs={24} sm={12} lg={8}>
<Card loading={loading} title="待处理工单">
<Statistic value={stats?.openTickets ?? 0} suffix="个" />
</Card>
</Col>
) : null}
</Row>
) : null}
{canUsers || canOrders ? (
<Row gutter={[16, 16]}>
<Col xs={24} lg={12}>
{canUsers ? (
<Col xs={24} lg={canOrders ? 12 : 24}>
<Card title="已合并访客账号" loading={loading}>
<Statistic value={stats?.mergedUsers ?? 0} suffix="个" />
</Card>
</Col>
<Col xs={24} lg={12}>
) : null}
{canOrders ? (
<Col xs={24} lg={canUsers ? 12 : 24}>
<Card title="订单状态分布" loading={loading}>
<Table
size="small"
@@ -768,7 +930,9 @@ export default function DashboardPage() {
/>
</Card>
</Col>
) : null}
</Row>
) : null}
</div>
);
}