feat(douyu): 精英宝典工作台支持上下/左右布局切换并记忆偏好
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Button, Card, Input, InputNumber, Modal, QRCode, Select, Space, Table, Tag, Typography,
|
||||
Button, Card, Input, InputNumber, Modal, QRCode, Select, Space, Table, Tag, Tooltip, Typography,
|
||||
} from 'antd';
|
||||
import type { TableProps } from 'antd';
|
||||
import {
|
||||
CheckCircleOutlined, CopyOutlined, CreditCardOutlined, ExclamationCircleOutlined, FieldTimeOutlined, GiftOutlined,
|
||||
CheckCircleOutlined, ColumnWidthOutlined, CopyOutlined, CreditCardOutlined, ExclamationCircleOutlined, FieldTimeOutlined, GiftOutlined,
|
||||
ImportOutlined, QrcodeOutlined, ReloadOutlined,
|
||||
SearchOutlined, SettingOutlined, ShoppingOutlined, StopOutlined,
|
||||
} from '@ant-design/icons';
|
||||
@@ -116,6 +116,7 @@ const ESPORTS_TASK_TYPES = new Set([
|
||||
]);
|
||||
const DOUYU_GOLD_AMOUNT_STORAGE_KEY = 'douyu_task_gold_amount';
|
||||
const DOUYU_GIFT_COUNT_STORAGE_KEY = 'douyu_task_gift_count';
|
||||
const DOUYU_LAYOUT_MODE_STORAGE_KEY = 'douyu_task_layout_mode';
|
||||
const CONFIRM_FAIL_TIP_SECONDS = 6;
|
||||
|
||||
function resultText(result: Record<string, unknown> | null | undefined, key: string): string {
|
||||
@@ -284,6 +285,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
const [giftCount, setGiftCount] = useState(() => savedPositiveInteger(DOUYU_GIFT_COUNT_STORAGE_KEY));
|
||||
const [selectedGoodsId, setSelectedGoodsId] = useState('');
|
||||
const [accountSearch, setAccountSearch] = useState('');
|
||||
const [layoutMode, setLayoutMode] = useState<'stack' | 'split'>(() => {
|
||||
const v = localStorage.getItem(DOUYU_LAYOUT_MODE_STORAGE_KEY);
|
||||
return v === 'split' ? 'split' : 'stack';
|
||||
});
|
||||
const accountTableAreaRef = useRef<HTMLDivElement | null>(null);
|
||||
const [accountTableAreaHeight, setAccountTableAreaHeight] = useState(360);
|
||||
const [accountPageSize, setAccountPageSize] = useState<number>(() => {
|
||||
@@ -459,6 +464,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
|
||||
useEffect(() => { loadData(); }, [loadData]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(DOUYU_LAYOUT_MODE_STORAGE_KEY, layoutMode);
|
||||
}, [layoutMode]);
|
||||
|
||||
// 监听账号表格区域高度变化,动态计算 scroll.y 实现表体内部滚动
|
||||
useEffect(() => {
|
||||
const node = accountTableAreaRef.current;
|
||||
@@ -472,7 +481,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(node);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
}, [layoutMode]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(DOUYU_GOLD_AMOUNT_STORAGE_KEY, String(goldAmount));
|
||||
@@ -1236,85 +1245,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
||||
gap: 8,
|
||||
};
|
||||
// 任务记录表:表头固定、表体内部滚动、分页器固定显示在底部
|
||||
const taskRecordTableStyle = `
|
||||
.douyu-task-record-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-spin-nested-loading,
|
||||
.douyu-task-record-table .ant-spin-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto !important;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-thead > tr > th,
|
||||
.douyu-task-record-table .ant-table-tbody > tr > td {
|
||||
padding: 4px 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.douyu-task-record-table .ant-typography {
|
||||
font-size: 12px;
|
||||
}
|
||||
.douyu-task-record-table .ant-tag {
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
margin-inline-end: 0;
|
||||
padding-inline: 5px;
|
||||
}
|
||||
.douyu-task-record-table .ant-pagination {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 6px 4px 2px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
||||
<style>{taskRecordTableStyle}</style>
|
||||
{/* 头部 */}
|
||||
<Space style={{ justifyContent: 'space-between', width: '100%', marginBottom: 8, flexShrink: 0 }}>
|
||||
<div>
|
||||
<h2 style={{ margin: 0 }}>{handbookTitle}</h2>
|
||||
<Text type="secondary">{handbookDescription}</Text>
|
||||
</div>
|
||||
<Space>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadData} loading={loading}>刷新</Button>
|
||||
{canConfig && <Button icon={<SettingOutlined />} onClick={() => setConfigOpen(true)}>配置</Button>}
|
||||
{(activeBatchIds.size > 0 || runningBatchIds.size > 0) && <Button danger icon={<StopOutlined />} onClick={stopBatch}>停止</Button>}
|
||||
</Space>
|
||||
</Space>
|
||||
|
||||
{/* 操作工具栏 */}
|
||||
<Card
|
||||
size="small"
|
||||
title={isEsportsHandbook ? '电竞手册操作' : '精英宝典操作'}
|
||||
extra={
|
||||
const operationExtra = (
|
||||
<Space>
|
||||
<Tag color={selectedIds.length ? 'blue' : 'default'}>{selectedText}</Tag>
|
||||
<Space.Compact>
|
||||
@@ -1329,10 +1260,9 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
/>
|
||||
</Space.Compact>
|
||||
</Space>
|
||||
}
|
||||
style={{ flexShrink: 0, marginBottom: 12 }}
|
||||
styles={{ body: { padding: 8 } }}
|
||||
>
|
||||
);
|
||||
const operationBody = (
|
||||
<>
|
||||
{isEsportsHandbook ? (
|
||||
<div style={compactOperationGridStyle}>
|
||||
<div style={sectionStyle}>
|
||||
@@ -1475,15 +1405,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
勾选账号后可批量执行;账号行右键可执行绑定与查询动作。
|
||||
</Text>
|
||||
</Card>
|
||||
|
||||
{/* 账号 */}
|
||||
<Card
|
||||
size="small" title="账号"
|
||||
extra={<Tag color="blue">已选 {selectedIds.length}/{accounts.length}</Tag>}
|
||||
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}
|
||||
styles={{ body: { flex: 1, minHeight: 0, padding: '4px 6px', overflow: 'hidden', display: 'flex', flexDirection: 'column' } }}
|
||||
>
|
||||
</>
|
||||
);
|
||||
const accountsBody = (
|
||||
<>
|
||||
<Space style={{ marginBottom: 8, width: '100%', justifyContent: 'space-between', flexShrink: 0 }} wrap>
|
||||
<Space>
|
||||
<Input
|
||||
@@ -1534,7 +1459,130 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
// 任务记录表:表头固定、表体内部滚动、分页器固定显示在底部
|
||||
const taskRecordTableStyle = `
|
||||
.douyu-task-record-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-spin-nested-loading,
|
||||
.douyu-task-record-table .ant-spin-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto !important;
|
||||
}
|
||||
.douyu-task-record-table .ant-table-thead > tr > th,
|
||||
.douyu-task-record-table .ant-table-tbody > tr > td {
|
||||
padding: 4px 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.douyu-task-record-table .ant-typography {
|
||||
font-size: 12px;
|
||||
}
|
||||
.douyu-task-record-table .ant-tag {
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
margin-inline-end: 0;
|
||||
padding-inline: 5px;
|
||||
}
|
||||
.douyu-task-record-table .ant-pagination {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 6px 4px 2px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
||||
<style>{taskRecordTableStyle}</style>
|
||||
{/* 头部 */}
|
||||
<Space style={{ justifyContent: 'space-between', width: '100%', marginBottom: 8, flexShrink: 0 }}>
|
||||
<div>
|
||||
<h2 style={{ margin: 0 }}>{handbookTitle}</h2>
|
||||
<Text type="secondary">{handbookDescription}</Text>
|
||||
</div>
|
||||
<Space>
|
||||
<Tooltip title={layoutMode === 'split' ? '切换为上下布局' : '切换为左右布局'}>
|
||||
<Button
|
||||
icon={<ColumnWidthOutlined />}
|
||||
onClick={() => setLayoutMode((m) => (m === 'split' ? 'stack' : 'split'))}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadData} loading={loading}>刷新</Button>
|
||||
{canConfig && <Button icon={<SettingOutlined />} onClick={() => setConfigOpen(true)}>配置</Button>}
|
||||
{(activeBatchIds.size > 0 || runningBatchIds.size > 0) && <Button danger icon={<StopOutlined />} onClick={stopBatch}>停止</Button>}
|
||||
</Space>
|
||||
</Space>
|
||||
|
||||
{/* 操作工具栏 + 账号表格(支持上下/左右布局切换) */}
|
||||
{layoutMode === 'split' ? (
|
||||
<div style={{ flex: 1, minHeight: 0, display: 'flex', gap: 12, overflow: 'hidden' }}>
|
||||
<Card
|
||||
size="small" title="账号"
|
||||
extra={<Tag color="blue">已选 {selectedIds.length}/{accounts.length}</Tag>}
|
||||
style={{ flex: 1, minWidth: 0, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}
|
||||
styles={{ body: { flex: 1, minHeight: 0, padding: '4px 6px', overflow: 'hidden', display: 'flex', flexDirection: 'column' } }}
|
||||
>
|
||||
{accountsBody}
|
||||
</Card>
|
||||
<Card
|
||||
size="small"
|
||||
title={isEsportsHandbook ? '电竞手册操作' : '精英宝典操作'}
|
||||
extra={operationExtra}
|
||||
style={{ width: 340, flexShrink: 0, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}
|
||||
styles={{ body: { padding: 8, flex: 1, minHeight: 0, overflowY: 'auto' } }}
|
||||
>
|
||||
{operationBody}
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Card
|
||||
size="small"
|
||||
title={isEsportsHandbook ? '电竞手册操作' : '精英宝典操作'}
|
||||
extra={operationExtra}
|
||||
style={{ flexShrink: 0, marginBottom: 12 }}
|
||||
styles={{ body: { padding: 8 } }}
|
||||
>
|
||||
{operationBody}
|
||||
</Card>
|
||||
<Card
|
||||
size="small" title="账号"
|
||||
extra={<Tag color="blue">已选 {selectedIds.length}/{accounts.length}</Tag>}
|
||||
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}
|
||||
styles={{ body: { flex: 1, minHeight: 0, padding: '4px 6px', overflow: 'hidden', display: 'flex', flexDirection: 'column' } }}
|
||||
>
|
||||
{accountsBody}
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 右键菜单 */}
|
||||
{contextMenu && (
|
||||
|
||||
Reference in New Issue
Block a user