|
|
|
@@ -1,12 +1,13 @@
|
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
|
|
import type { MouseEvent } from 'react';
|
|
|
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
|
import {
|
|
|
|
|
Button, Card, Input, InputNumber, Modal, QRCode, Select, Space, Table, Tag, Tooltip, Typography,
|
|
|
|
|
} from 'antd';
|
|
|
|
|
import type { TableProps } from 'antd';
|
|
|
|
|
import {
|
|
|
|
|
CheckCircleOutlined, ColumnWidthOutlined, CopyOutlined, GiftOutlined, ImportOutlined, QrcodeOutlined,
|
|
|
|
|
ReloadOutlined, SearchOutlined, SettingOutlined, ShoppingOutlined, StopOutlined, ZoomInOutlined,
|
|
|
|
|
ReloadOutlined, SearchOutlined, SettingOutlined, ShoppingOutlined, StopOutlined, SwapOutlined, ZoomInOutlined,
|
|
|
|
|
} from '@ant-design/icons';
|
|
|
|
|
import { huyaApi, type HuyaAccountItem, type HuyaConfig, type HuyaGoodsItem, type HuyaRechargeGoodsItem, type HuyaTaskItem } from '../api/modules';
|
|
|
|
|
import HuyaExternalCkImportModal from '../components/HuyaExternalCkImportModal';
|
|
|
|
@@ -169,9 +170,11 @@ function taskQrUrl(task: HuyaTaskItem | undefined): string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function HuyaElitePage() {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const { can } = usePermissions();
|
|
|
|
|
const canConfig = can('huya:config');
|
|
|
|
|
const canImport = can('huya:import') || can('huya:elite:import_ck');
|
|
|
|
|
const canTransferCollab = can('huya:collab:edit');
|
|
|
|
|
const [pool, setPool] = useState<HuyaAccountItem[]>([]);
|
|
|
|
|
const [accounts, setAccounts] = useState<HuyaAccountItem[]>([]);
|
|
|
|
|
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
|
|
|
@@ -188,6 +191,7 @@ export default function HuyaElitePage() {
|
|
|
|
|
const [pageSize, setPageSize] = useState(savedPageSize);
|
|
|
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [transferring, setTransferring] = useState(false);
|
|
|
|
|
const [starting, setStarting] = useState(false);
|
|
|
|
|
const [concurrency, setConcurrency] = useState(3);
|
|
|
|
|
const [refreshInterval, setRefreshInterval] = useState(savedInterval);
|
|
|
|
@@ -272,6 +276,31 @@ export default function HuyaElitePage() {
|
|
|
|
|
setSelectedIds((prev) => prev.filter((id) => saved.account_ids.includes(id)));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const transferToCollab = async () => {
|
|
|
|
|
if (selectedIds.length !== 1) {
|
|
|
|
|
message.warning('一次只能选择一个账号转入外部单协作');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const accountId = selectedIds[0];
|
|
|
|
|
const account = accounts.find((item) => item.id === accountId);
|
|
|
|
|
if (account && account.has_cookie === false) {
|
|
|
|
|
message.warning('该账号没有可用 Cookie,不能转入外部单协作');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setTransferring(true);
|
|
|
|
|
try {
|
|
|
|
|
const result = await huyaApi.transferWorkbenchToCollab(accountId);
|
|
|
|
|
message.success('已从精英宝典工作台移除,正在打开外部单协作');
|
|
|
|
|
navigate('/huya/collab', {
|
|
|
|
|
replace: true,
|
|
|
|
|
state: { transferCk: result.ck_text },
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
setTransferring(false);
|
|
|
|
|
message.error(getErrorMessage(error));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 开通商品分组:精英宝典(限购主商品,单独一块)与其他商品(币/徽章/令…,带数量那一块)
|
|
|
|
|
const rechargeGoodsSorted = [...rechargeGoods].sort(byRechargeOrder);
|
|
|
|
|
const eliteRechargeGoods = rechargeGoodsSorted.filter(isEliteHandbookGood);
|
|
|
|
@@ -478,7 +507,7 @@ export default function HuyaElitePage() {
|
|
|
|
|
</Space>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const accountsBody = <><Space style={{ marginBottom: 8, width: '100%', justifyContent: 'space-between' }} wrap><Input size="small" allowClear prefix={<SearchOutlined />} placeholder="搜索账号/昵称/游戏名/手机号" value={search} onChange={(event) => { setSearch(event.target.value); setCurrentPage(1); }} style={{ width: 240 }} /><Space><Button size="small" icon={<ImportOutlined />} onClick={() => setImportOpen(true)}>导入账号</Button>{canImport && <Button size="small" icon={<ImportOutlined />} onClick={() => setCkImportOpen(true)}>导入外部 CK</Button>}{selectedIds.length > 0 && <Button size="small" danger onClick={() => void saveAccounts(accounts.map((item) => item.id).filter((id) => !selectedIds.includes(id)))}>移出选中</Button>}</Space></Space><div className="huya-account-table"><Table rowKey="id" size="small" loading={loading} tableLayout="fixed" rowSelection={{ selectedRowKeys: selectedIds, onChange: (keys) => setSelectedIds(keys.map(Number)), preserveSelectedRowKeys: true }} columns={accountColumns} dataSource={filteredAccounts} locale={{ emptyText: '工作台暂无账号,点击右上角「导入账号」添加' }} pagination={{ current: currentPage, pageSize, total: filteredAccounts.length, showSizeChanger: true, showTotal: (total) => `共 ${total} 条`, onChange: (page, size) => { setCurrentPage(page); if (size !== pageSize) { setPageSize(size); localStorage.setItem(PAGE_SIZE_KEY, String(size)); setCurrentPage(1); } } }} scroll={{ y: 'calc(100vh - 300px)' }} onRow={(row) => ({ onClick: () => { const task = latestTask(tasks, row.id); if (!task?.result) return; if (task.task_type === 'get_bind_qr') void openQrTask(task); else if (task.task_type === 'create_recharge_order') openPayTasks(task.batch_id); }, onContextMenu: (event) => handleRowContextMenu(row, event), style: { cursor: 'context-menu' }, title: '右键打开账号动作' })} /></div></>;
|
|
|
|
|
const accountsBody = <><Space style={{ marginBottom: 8, width: '100%', justifyContent: 'space-between' }} wrap><Input size="small" allowClear prefix={<SearchOutlined />} placeholder="搜索账号/昵称/游戏名/手机号" value={search} onChange={(event) => { setSearch(event.target.value); setCurrentPage(1); }} style={{ width: 240 }} /><Space><Button size="small" icon={<ImportOutlined />} onClick={() => setImportOpen(true)}>导入账号</Button>{canImport && <Button size="small" icon={<ImportOutlined />} onClick={() => setCkImportOpen(true)}>导入外部 CK</Button>}{selectedIds.length > 0 && <Button size="small" danger onClick={() => void saveAccounts(accounts.map((item) => item.id).filter((id) => !selectedIds.includes(id)))}>移出选中</Button>}{canTransferCollab && selectedIds.length === 1 && <Button size="small" icon={<SwapOutlined />} loading={transferring} onClick={() => void transferToCollab()}>转入外部单协作</Button>}</Space></Space><div className="huya-account-table"><Table rowKey="id" size="small" loading={loading} tableLayout="fixed" rowSelection={{ selectedRowKeys: selectedIds, onChange: (keys) => setSelectedIds(keys.map(Number)), preserveSelectedRowKeys: true }} columns={accountColumns} dataSource={filteredAccounts} locale={{ emptyText: '工作台暂无账号,点击右上角「导入账号」添加' }} pagination={{ current: currentPage, pageSize, total: filteredAccounts.length, showSizeChanger: true, showTotal: (total) => `共 ${total} 条`, onChange: (page, size) => { setCurrentPage(page); if (size !== pageSize) { setPageSize(size); localStorage.setItem(PAGE_SIZE_KEY, String(size)); setCurrentPage(1); } } }} scroll={{ y: 'calc(100vh - 300px)' }} onRow={(row) => ({ onClick: () => { const task = latestTask(tasks, row.id); if (!task?.result) return; if (task.task_type === 'get_bind_qr') void openQrTask(task); else if (task.task_type === 'create_recharge_order') openPayTasks(task.batch_id); }, onContextMenu: (event) => handleRowContextMenu(row, event), style: { cursor: 'context-menu' }, title: '右键打开账号动作' })} /></div></>;
|
|
|
|
|
|
|
|
|
|
const configField = (key: Exclude<keyof HuyaConfig, 'updated_at'>, label: string) => configDraft && <Space key={key} style={{ width: '100%', justifyContent: 'space-between' }}><Text>{label}</Text><Input value={configDraft[key] || ''} onChange={(event) => setConfigDraft({ ...configDraft, [key]: event.target.value })} style={{ width: 300 }} /></Space>;
|
|
|
|
|
const contextMenuStyle = contextMenu ? {
|
|
|
|
|