增加斗鱼自定义CK筛选功能

This commit is contained in:
yml2213
2026-08-08 19:03:23 +08:00
parent 39ea0d2184
commit 65271124c0
4 changed files with 121 additions and 7 deletions
+8 -2
View File
@@ -3,11 +3,17 @@ import type { BasicSummary, CookieCheckResult, CookieItem, LoginTaskItem, PagePa
export const cookieApi = {
list: () => api.get<CookieItem[], CookieItem[]>('/cookies'),
listPaged: (params: PageParams & { include_cookie?: boolean }) =>
listPaged: (params: PageParams & { include_cookie?: boolean; account_names?: string }) =>
api.get<PaginatedResponse<CookieItem>, PaginatedResponse<CookieItem>>('/cookies', { params }),
summary: () => api.get<BasicSummary, BasicSummary>('/cookies/summary'),
get: (id: number) => api.get<CookieItem, CookieItem>(`/cookies/${id}`),
exportCsv: (format?: string) => api.get<Blob, Blob>('/cookies/export', { responseType: 'blob', params: format ? { format } : {} }),
exportCsv: (format?: string, accountNames?: string) => api.get<Blob, Blob>('/cookies/export', {
responseType: 'blob',
params: {
...(format ? { format } : {}),
...(accountNames ? { account_names: accountNames } : {}),
},
}),
check: (ids: number[]) =>
api.post<{ results: CookieCheckResult[] }, { results: CookieCheckResult[] }>('/cookies/check', null, { params: { ids: ids.join(',') } }),
relogin: (ids: number[]) =>
+1
View File
@@ -21,6 +21,7 @@ export interface PageParams {
page: number;
page_size: number;
search?: string;
account_names?: string;
}
export interface AccountBulkSelection {
+82 -4
View File
@@ -1,7 +1,7 @@
import { useEffect, useState, useCallback } from 'react';
import { Table, Button, Card, Row, Col, Statistic, Tag, Popconfirm, Space, Typography, Input, theme, Dropdown, Tooltip } from 'antd';
import { message } from '../utils/antdMessage';
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined, LoadingOutlined, ReloadOutlined } from '@ant-design/icons';
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined, LoadingOutlined, ReloadOutlined, FilterOutlined } from '@ant-design/icons';
import { cookieApi, type BasicSummary, type CookieCheckResult, type CookieItem } from '../api/modules';
import { usePermissions } from '../hooks/usePermissions';
import { formatTime } from '../utils/time';
@@ -37,12 +37,24 @@ function copyToClipboard(text: string): Promise<void> {
});
}
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 CookiePage() {
const { token } = theme.useToken();
const [cookies, setCookies] = useState<CookieItem[]>([]);
const [loading, setLoading] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [searchText, setSearchText] = useState('');
const [customNamesText, setCustomNamesText] = useState('');
const [customAccountNames, setCustomAccountNames] = useState<string[]>([]);
const [total, setTotal] = useState(0);
const [summary, setSummary] = useState<BasicSummary>({ total: 0, assigned_count: 0, unassigned_count: 0 });
const [pageSize, setPageSize] = useState(() => {
@@ -148,6 +160,7 @@ export default function CookiePage() {
page: currentPage,
page_size: pageSize,
search: searchText.trim() || undefined,
account_names: customAccountNames.length > 0 ? customAccountNames.join('\n') : undefined,
include_cookie: false,
});
setCookies(data.items);
@@ -174,7 +187,7 @@ export default function CookiePage() {
} finally {
setLoading(false);
}
}, [currentPage, pageSize, searchText]);
}, [currentPage, pageSize, searchText, customAccountNames]);
const loadSummary = useCallback(async () => {
try {
@@ -195,19 +208,60 @@ export default function CookiePage() {
const handleExport = async (format: string = 'csv') => {
try {
const blob = await cookieApi.exportCsv(format);
const blob = await cookieApi.exportCsv(
format,
customAccountNames.length > 0 ? customAccountNames.join('\n') : undefined,
);
const url = URL.createObjectURL(blob instanceof Blob ? blob : new Blob([blob]));
const a = document.createElement('a');
a.href = url;
a.download = format === 'custom' ? 'cookies_custom.txt' : 'cookies.csv';
a.click();
URL.revokeObjectURL(url);
message.success('已导出');
message.success(customAccountNames.length > 0 ? '已导出匹配的 Cookie' : '已导出');
} catch (e: unknown) {
message.error(getErrorMessage(e));
}
};
const handleApplyCustomFilter = () => {
const names = parseAccountNames(customNamesText);
if (names.length === 0) {
message.warning('请先粘贴斗鱼账户名,每行一个');
return;
}
setCustomAccountNames(names);
setSelectedRowKeys([]);
setCurrentPage(1);
message.success(`已载入 ${names.length} 个账户名,正在筛选 Cookie`);
};
const handleClearCustomFilter = () => {
setCustomNamesText('');
setCustomAccountNames([]);
setSelectedRowKeys([]);
setCurrentPage(1);
};
const handleCopyCustomCookies = async () => {
if (customAccountNames.length === 0) {
message.warning('请先筛选自定义账户名');
return;
}
try {
const blob = await cookieApi.exportCsv('custom', customAccountNames.join('\n'));
const text = blob instanceof Blob ? await blob.text() : String(blob);
if (!text.trim()) {
message.warning('没有找到对应的 Cookie');
return;
}
await copyToClipboard(text);
message.success(`已复制 ${total} 条匹配的自定义 CK`);
} catch (e: unknown) {
message.error(getErrorMessage(e) || '复制失败');
}
};
const handleCopyCookie = async (record: CookieItem) => {
try {
const detail = await cookieApi.get(record.id);
@@ -475,6 +529,30 @@ export default function CookiePage() {
</Card>
</Col>
</Row>
<Card size="small" title="获取自定义 CK" style={{ marginBottom: 16 }}>
<Space direction="vertical" style={{ width: '100%' }} size={8}>
<Input.TextArea
value={customNamesText}
onChange={(e) => setCustomNamesText(e.target.value)}
placeholder="从 Excel 复制斗鱼账户名后粘贴到这里,每行一个"
autoSize={{ minRows: 3, maxRows: 7 }}
/>
<Space wrap>
<Button type="primary" icon={<FilterOutlined />} onClick={handleApplyCustomFilter}>
CK
</Button>
<Button icon={<CopyOutlined />} onClick={handleCopyCustomCookies} disabled={customAccountNames.length === 0}>
CK
</Button>
<Button onClick={handleClearCustomFilter} disabled={!customNamesText && customAccountNames.length === 0}>
</Button>
{customAccountNames.length > 0 && (
<Text type="secondary"> {customAccountNames.length} {total} </Text>
)}
</Space>
</Space>
</Card>
<div style={{ marginBottom: 12 }}>
<Input.Search
placeholder="搜索账号或分配客服"