'use client'; import type { ColumnsType } from 'antd/es/table'; import { App, Button, Popconfirm, Space, Table } from 'antd'; import { api, errMsg } from '@/lib/api'; import { canDo } from '@/lib/auth'; import { formatUtcTime } from '@/lib/format'; import { useCursorList } from '@/lib/useCursorList'; import type { DeviceOnboardingItem } from '@/lib/types'; // completed_at 为 UTC 口径(server_default now()),按北京显示(勿用 new Date().toLocaleString) const dt = (v: string) => formatUtcTime(v); const EMPTY: Record = {}; export default function DevicesPage() { const { message, modal } = App.useApp(); // 后端按设备聚合、全量返回(next_cursor 恒 null),故无筛选/加载更多。 const { items, loading, reload } = useCursorList( '/admin/api/onboarding/devices', EMPTY, ); const canReset = canDo(['operator']); const resetDevice = async (d: DeviceOnboardingItem) => { try { await api.post(`/admin/api/onboarding/devices/${encodeURIComponent(d.device_id)}/reset`); message.success('已重置:该设备所有账号下次打开 App 重走引导'); reload(); } catch (e) { message.error(errMsg(e)); } }; const resetAll = () => { modal.confirm({ title: '确认全部重设新手引导?', content: '清空所有设备的引导完成记录,库里所有用户下次打开 App 都会重走一遍新手引导。此操作不可逆。', okText: '确认全部重设', okButtonProps: { danger: true }, cancelText: '取消', onOk: async () => { try { await api.post('/admin/api/onboarding/reset-all', { confirm: true }); message.success('已全部重设:所有用户下次打开 App 会重走引导'); reload(); } catch (e) { message.error(errMsg(e)); } }, }); }; const columns: ColumnsType = [ { title: '设备(ANDROID_ID)', dataIndex: 'device_id' }, { title: '走过引导的账号数', dataIndex: 'account_count', width: 160 }, { title: '最近完成引导', dataIndex: 'last_completed_at', render: dt, width: 200 }, { title: '操作', key: 'op', width: 120, render: (_: unknown, d: DeviceOnboardingItem) => canReset ? ( resetDevice(d)} okText="确认" cancelText="取消" > 重走引导 ) : ( - ), }, ]; return (

设备管理

列出走过新手引导的设备(按硬件 ANDROID_ID 聚合)。「重走引导」删除该设备的引导完成记录, 使其上所有账号下次打开 App 重走一遍。没走过引导的设备本就会引导,不在此列。

{canReset && ( )} ); }