Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7957e58c0 |
@@ -8,7 +8,7 @@ import dynamic from 'next/dynamic';
|
|||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { Button, Card, Col, Empty, Row, Segmented, Space, Spin, Statistic, Table, message } from 'antd';
|
import { Button, Card, Col, Empty, Row, Segmented, Space, Spin, Statistic, Table, message } from 'antd';
|
||||||
import { api, errMsg } from '@/lib/api';
|
import { api, errMsg } from '@/lib/api';
|
||||||
import { yuan } from '@/lib/format';
|
import { formatUtcTime, yuan } from '@/lib/format';
|
||||||
|
|
||||||
// @ant-design/plots 依赖 DOM,关掉 SSR 仅在客户端渲染(否则 Next 预渲染报 document undefined)。
|
// @ant-design/plots 依赖 DOM,关掉 SSR 仅在客户端渲染(否则 Next 预渲染报 document undefined)。
|
||||||
const Line = dynamic(() => import('@ant-design/plots').then((m) => m.Line), { ssr: false });
|
const Line = dynamic(() => import('@ant-design/plots').then((m) => m.Line), { ssr: false });
|
||||||
@@ -47,6 +47,16 @@ interface DailyResp {
|
|||||||
rows: DailyRow[];
|
rows: DailyRow[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 群内微信用户(落地页授权拿到;nickname/headimgurl 仅 userinfo 授权后有)
|
||||||
|
interface WxUser {
|
||||||
|
openid: string;
|
||||||
|
nickname: string | null;
|
||||||
|
headimgurl: string | null;
|
||||||
|
first_seen: string;
|
||||||
|
visit_count: number;
|
||||||
|
copy_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
const RANGES = [
|
const RANGES = [
|
||||||
{ label: '近 3 天', value: 'd3' },
|
{ label: '近 3 天', value: 'd3' },
|
||||||
{ label: '近 7 天', value: 'd7' },
|
{ label: '近 7 天', value: 'd7' },
|
||||||
@@ -115,6 +125,20 @@ export default function GroupDetailPage() {
|
|||||||
loadDaily();
|
loadDaily();
|
||||||
}, [loadDaily]);
|
}, [loadDaily]);
|
||||||
|
|
||||||
|
// 群内微信用户(独立于时间范围,累计画像)
|
||||||
|
const [wxUsers, setWxUsers] = useState<WxUser[]>([]);
|
||||||
|
const loadWxUsers = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const r = await api.get<{ users: WxUser[] }>(`/admin/api/cps/groups/${id}/wx-users`);
|
||||||
|
setWxUsers(r.data.users || []);
|
||||||
|
} catch (e) {
|
||||||
|
message.error(errMsg(e, '用户列表加载失败'));
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
useEffect(() => {
|
||||||
|
loadWxUsers();
|
||||||
|
}, [loadWxUsers]);
|
||||||
|
|
||||||
const points = useMemo(() => data?.points ?? [], [data]);
|
const points = useMemo(() => data?.points ?? [], [data]);
|
||||||
// 展平成「长表」给折线图:每个 (时间桶 × 指标) 一行,colorField 按指标分 4 条线。
|
// 展平成「长表」给折线图:每个 (时间桶 × 指标) 一行,colorField 按指标分 4 条线。
|
||||||
const chartData = useMemo(
|
const chartData = useMemo(
|
||||||
@@ -202,6 +226,38 @@ export default function GroupDetailPage() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const wxColumns: ColumnsType<WxUser> = [
|
||||||
|
{
|
||||||
|
title: '用户',
|
||||||
|
key: 'user',
|
||||||
|
render: (_, u) => (
|
||||||
|
<Space>
|
||||||
|
{u.headimgurl ? (
|
||||||
|
<img
|
||||||
|
src={u.headimgurl}
|
||||||
|
alt=""
|
||||||
|
style={{ width: 32, height: 32, borderRadius: '50%', objectFit: 'cover' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
style={{ width: 32, height: 32, borderRadius: '50%', background: '#f0f0f0', display: 'inline-block' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>{u.nickname || '(未授权昵称)'}</span>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '领券次数',
|
||||||
|
dataIndex: 'copy_count',
|
||||||
|
width: 90,
|
||||||
|
sorter: (a, b) => a.copy_count - b.copy_count,
|
||||||
|
render: (v: number) => (v ? <b style={{ color: '#ff4d4f' }}>{v}</b> : 0),
|
||||||
|
},
|
||||||
|
{ title: '点击次数', dataIndex: 'visit_count', width: 90 },
|
||||||
|
{ title: '首次进入', dataIndex: 'first_seen', width: 160, render: (v: string) => formatUtcTime(v) },
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Space style={{ marginBottom: 16 }} align="center" wrap>
|
<Space style={{ marginBottom: 16 }} align="center" wrap>
|
||||||
@@ -255,6 +311,21 @@ export default function GroupDetailPage() {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Card title={`群内微信用户(${wxUsers.length})`} style={{ marginTop: 16 }}>
|
||||||
|
<div style={{ fontSize: 12, color: '#999', marginBottom: 8 }}>
|
||||||
|
在微信里打开本群落地页并授权的用户。「领券次数」= 点了几次「复制口令」;昵称/头像需用户点领券时授权补充。
|
||||||
|
</div>
|
||||||
|
<Table<WxUser>
|
||||||
|
rowKey="openid"
|
||||||
|
size="small"
|
||||||
|
columns={wxColumns}
|
||||||
|
dataSource={wxUsers}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: 560 }}
|
||||||
|
locale={{ emptyText: '暂无授权用户' }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user