feat(douyu): 精英宝典导入支持按多行账户名筛选
对齐 Cookie 自定义 CK 的粘贴导入方式,按账户名精确匹配并保持粘贴顺序。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,7 +19,7 @@ export const douyuApi = {
|
||||
api.get<DouyuTaskAccountItem[], DouyuTaskAccountItem[]>('/douyu/accounts', { params }),
|
||||
listAccountsPaged: (params: PageParams & { ids?: string; tag?: string }) =>
|
||||
api.get<PaginatedResponse<DouyuTaskAccountItem>, PaginatedResponse<DouyuTaskAccountItem>>('/douyu/accounts', { params }),
|
||||
listAccountIds: (params?: { search?: string; tag?: string }) =>
|
||||
listAccountIds: (params?: { search?: string; tag?: string; account_names?: string }) =>
|
||||
api.get<{ account_ids: number[]; total: number }, { account_ids: number[]; total: number }>('/douyu/accounts/ids', { params }),
|
||||
getConfig: () => api.get<DouyuConfig, DouyuConfig>('/douyu/config'),
|
||||
getRechargeChannel: () => api.get<DouyuRechargeChannel, DouyuRechargeChannel>('/douyu/recharge-channel'),
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from 'antd';
|
||||
import type { TableProps } from 'antd';
|
||||
import {
|
||||
CheckCircleOutlined, ColumnWidthOutlined, CopyOutlined, CreditCardOutlined, ExclamationCircleOutlined, FieldTimeOutlined, GiftOutlined,
|
||||
CheckCircleOutlined, ColumnWidthOutlined, CopyOutlined, CreditCardOutlined, ExclamationCircleOutlined, FieldTimeOutlined, FilterOutlined, GiftOutlined,
|
||||
ImportOutlined, QrcodeOutlined, ReloadOutlined,
|
||||
SearchOutlined, SettingOutlined, ShoppingOutlined, StopOutlined,
|
||||
} from '@ant-design/icons';
|
||||
@@ -61,6 +61,16 @@ import {
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
function parseAccountNames(text: string): string[] {
|
||||
const seen = new Set<string>();
|
||||
return text
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
.split('\n')
|
||||
.map((name) => name.trim())
|
||||
.filter((name) => name.length > 0 && !seen.has(name) && seen.add(name));
|
||||
}
|
||||
|
||||
export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind }) {
|
||||
const isPeaceHandbook = handbook === 'peace';
|
||||
const isEsportsHandbook = handbook === 'esports';
|
||||
@@ -110,6 +120,8 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
const [importAllLoading, setImportAllLoading] = useState(false);
|
||||
const [importPage, setImportPage] = useState(1);
|
||||
const [importTotal, setImportTotal] = useState(0);
|
||||
const [importNamesText, setImportNamesText] = useState('');
|
||||
const [importAccountNames, setImportAccountNames] = useState<string[]>([]);
|
||||
const importPageSize = 20;
|
||||
|
||||
// 绑定/支付弹窗以 task 为唯一数据源;二维码弹窗支持多账号并行(Tabs 切换)
|
||||
@@ -783,10 +795,11 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
const loadImportPool = useCallback(async () => {
|
||||
setImportLoading(true);
|
||||
try {
|
||||
// 候选池 = 账号库按搜索/标签过滤,剔除已导入工作台的账号
|
||||
// 候选池 = 账号库按搜索/标签/粘贴账户名过滤,剔除已导入工作台的账号
|
||||
const result = await douyuApi.listAccountsPaged({
|
||||
search: importSearch.trim() || undefined,
|
||||
tag: importTag || undefined,
|
||||
account_names: importAccountNames.length > 0 ? importAccountNames.join('\n') : undefined,
|
||||
page: importPage,
|
||||
page_size: importPageSize,
|
||||
});
|
||||
@@ -795,17 +808,17 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
} finally {
|
||||
setImportLoading(false);
|
||||
}
|
||||
}, [importPage, importSearch, importTag, workbenchIds]);
|
||||
}, [importAccountNames, importPage, importSearch, importTag, workbenchIds]);
|
||||
useEffect(() => { if (importOpen) void loadImportPool(); }, [importOpen, loadImportPool]);
|
||||
useEffect(() => {
|
||||
if (!importOpen) return;
|
||||
accountApi.listTags().then(setImportTags).catch(() => {});
|
||||
}, [importOpen]);
|
||||
// 切换搜索/标签时清空已选,避免计数与可见行不一致
|
||||
// 切换搜索/标签/自定义账户名时清空已选,避免计数与可见行不一致
|
||||
useEffect(() => {
|
||||
setImportPage(1);
|
||||
setImportSelectedIds([]);
|
||||
}, [importSearch, importTag]);
|
||||
}, [importSearch, importTag, importAccountNames]);
|
||||
useEffect(() => { setImportSelectedIds([]); }, [importPage]);
|
||||
|
||||
const saveWorkbenchIds = async (nextIds: number[]) => {
|
||||
@@ -828,25 +841,50 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
return [...prev, ...toImport.filter((a) => !existing.has(a.id))];
|
||||
});
|
||||
setImportSelectedIds([]);
|
||||
setImportOpen(false);
|
||||
closeImportModal();
|
||||
message.success(`已导入 ${toImport.length} 个账号`);
|
||||
};
|
||||
|
||||
const closeImportModal = () => {
|
||||
setImportOpen(false);
|
||||
setImportNamesText('');
|
||||
setImportAccountNames([]);
|
||||
setImportSelectedIds([]);
|
||||
};
|
||||
|
||||
const applyImportNames = () => {
|
||||
const names = parseAccountNames(importNamesText);
|
||||
if (names.length === 0) {
|
||||
message.warning('请先粘贴斗鱼账户名,每行一个');
|
||||
return;
|
||||
}
|
||||
setImportAccountNames(names);
|
||||
message.success(`已载入 ${names.length} 个账户名,正在筛选账号`);
|
||||
};
|
||||
|
||||
const clearImportNames = () => {
|
||||
setImportNamesText('');
|
||||
setImportAccountNames([]);
|
||||
};
|
||||
|
||||
const importAllAccounts = async () => {
|
||||
const names = importAccountNames.length > 0
|
||||
? importAccountNames
|
||||
: parseAccountNames(importNamesText);
|
||||
setImportAllLoading(true);
|
||||
try {
|
||||
const result = await douyuApi.listAccountIds({
|
||||
search: importSearch.trim() || undefined,
|
||||
tag: importTag || undefined,
|
||||
account_names: names.length > 0 ? names.join('\n') : undefined,
|
||||
});
|
||||
const imported = result.account_ids.filter((id) => !workbenchIds.includes(id));
|
||||
if (imported.length === 0) {
|
||||
message.info('当前筛选条件下没有可新增的账号');
|
||||
message.info(names.length > 0 ? '没有匹配到可新增的账号' : '当前筛选条件下没有可新增的账号');
|
||||
return;
|
||||
}
|
||||
await saveWorkbenchIds([...new Set([...workbenchIds, ...imported])]);
|
||||
setImportSelectedIds([]);
|
||||
setImportOpen(false);
|
||||
closeImportModal();
|
||||
message.success(`已一键导入 ${imported.length} 个账号`);
|
||||
} catch (error) {
|
||||
message.error(getErrorMessage(error));
|
||||
@@ -1906,10 +1944,29 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
<Modal
|
||||
title="导入账号到操作台"
|
||||
open={importOpen}
|
||||
onCancel={() => setImportOpen(false)}
|
||||
onCancel={closeImportModal}
|
||||
footer={null}
|
||||
width={700}
|
||||
width={720}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%', marginBottom: 12 }} size={8}>
|
||||
<Input.TextArea
|
||||
value={importNamesText}
|
||||
onChange={(e) => setImportNamesText(e.target.value)}
|
||||
placeholder="从 Excel 复制斗鱼账户名后粘贴到这里,每行一个"
|
||||
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||
/>
|
||||
<Space wrap>
|
||||
<Button type="primary" icon={<FilterOutlined />} onClick={applyImportNames}>
|
||||
按账号名筛选
|
||||
</Button>
|
||||
<Button onClick={clearImportNames} disabled={!importNamesText && importAccountNames.length === 0}>
|
||||
清空
|
||||
</Button>
|
||||
{importAccountNames.length > 0 && (
|
||||
<Text type="secondary">已载入 {importAccountNames.length} 个账户名,匹配结果共 {importTotal} 条</Text>
|
||||
)}
|
||||
</Space>
|
||||
</Space>
|
||||
<div style={{ marginBottom: 12, display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
||||
<Space wrap>
|
||||
<Input
|
||||
@@ -1937,7 +1994,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
导入本页 ({importPool.length})
|
||||
</Button>
|
||||
<Button loading={importAllLoading} onClick={() => void importAllAccounts()}>
|
||||
一键导入全部
|
||||
{(importAccountNames.length > 0 || importNamesText.trim()) ? '导入全部匹配' : '一键导入全部'}
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => void importAccounts(importSelectedIds)} disabled={importSelectedIds.length === 0}>
|
||||
导入选中 ({importSelectedIds.length})
|
||||
|
||||
Reference in New Issue
Block a user