- Split monolithic 922-line TokensTable.js into modular components: * useTokensData.js: Custom hook for centralized state and logic management * TokensColumnDefs.js: Column definitions and rendering functions * TokensTable.jsx: Pure table component for rendering * TokensActions.jsx: Actions area (add, copy, delete tokens) * TokensFilters.jsx: Search form component with keyword and token filters * TokensDescription.jsx: Description area with compact mode toggle * index.jsx: Main orchestrator component - Features preserved: * Token status management with switch controls * Quota progress bars and visual indicators * Model limitations display with vendor avatars * IP restrictions handling and display * Chat integrations with dropdown menu * Batch operations (copy, delete) with confirmations * Key visibility toggle and copy functionality * Compact mode for responsive layouts * Search and filtering capabilities * Pagination and loading states - Improvements: * Better separation of concerns * Enhanced reusability and testability * Simplified maintenance and debugging * Consistent modular architecture pattern * Performance optimizations with useMemo * Backward compatibility maintained This refactoring follows the same successful pattern used for LogsTable, MjLogsTable, and TaskLogsTable, significantly improving code maintainability while preserving all existing functionality.
280 lines
7.6 KiB
JavaScript
280 lines
7.6 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Modal } from '@douyinfe/semi-ui';
|
|
import {
|
|
API,
|
|
copy,
|
|
isAdmin,
|
|
showError,
|
|
showSuccess,
|
|
timestamp2string
|
|
} from '../../helpers';
|
|
import { ITEMS_PER_PAGE } from '../../constants';
|
|
import { useTableCompactMode } from '../common/useTableCompactMode';
|
|
|
|
export const useTaskLogsData = () => {
|
|
const { t } = useTranslation();
|
|
|
|
// Define column keys for selection
|
|
const COLUMN_KEYS = {
|
|
SUBMIT_TIME: 'submit_time',
|
|
FINISH_TIME: 'finish_time',
|
|
DURATION: 'duration',
|
|
CHANNEL: 'channel',
|
|
PLATFORM: 'platform',
|
|
TYPE: 'type',
|
|
TASK_ID: 'task_id',
|
|
TASK_STATUS: 'task_status',
|
|
PROGRESS: 'progress',
|
|
FAIL_REASON: 'fail_reason',
|
|
RESULT_URL: 'result_url',
|
|
};
|
|
|
|
// Basic state
|
|
const [logs, setLogs] = useState([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [activePage, setActivePage] = useState(1);
|
|
const [logCount, setLogCount] = useState(0);
|
|
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
|
|
|
// User and admin
|
|
const isAdminUser = isAdmin();
|
|
|
|
// Modal state
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [modalContent, setModalContent] = useState('');
|
|
|
|
// Form state
|
|
const [formApi, setFormApi] = useState(null);
|
|
let now = new Date();
|
|
let zeroNow = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
|
|
const formInitValues = {
|
|
channel_id: '',
|
|
task_id: '',
|
|
dateRange: [
|
|
timestamp2string(zeroNow.getTime() / 1000),
|
|
timestamp2string(now.getTime() / 1000 + 3600)
|
|
],
|
|
};
|
|
|
|
// Column visibility state
|
|
const [visibleColumns, setVisibleColumns] = useState({});
|
|
const [showColumnSelector, setShowColumnSelector] = useState(false);
|
|
|
|
// Compact mode
|
|
const [compactMode, setCompactMode] = useTableCompactMode('taskLogs');
|
|
|
|
// Load saved column preferences from localStorage
|
|
useEffect(() => {
|
|
const savedColumns = localStorage.getItem('task-logs-table-columns');
|
|
if (savedColumns) {
|
|
try {
|
|
const parsed = JSON.parse(savedColumns);
|
|
const defaults = getDefaultColumnVisibility();
|
|
const merged = { ...defaults, ...parsed };
|
|
setVisibleColumns(merged);
|
|
} catch (e) {
|
|
console.error('Failed to parse saved column preferences', e);
|
|
initDefaultColumns();
|
|
}
|
|
} else {
|
|
initDefaultColumns();
|
|
}
|
|
}, []);
|
|
|
|
// Get default column visibility based on user role
|
|
const getDefaultColumnVisibility = () => {
|
|
return {
|
|
[COLUMN_KEYS.SUBMIT_TIME]: true,
|
|
[COLUMN_KEYS.FINISH_TIME]: true,
|
|
[COLUMN_KEYS.DURATION]: true,
|
|
[COLUMN_KEYS.CHANNEL]: isAdminUser,
|
|
[COLUMN_KEYS.PLATFORM]: true,
|
|
[COLUMN_KEYS.TYPE]: true,
|
|
[COLUMN_KEYS.TASK_ID]: true,
|
|
[COLUMN_KEYS.TASK_STATUS]: true,
|
|
[COLUMN_KEYS.PROGRESS]: true,
|
|
[COLUMN_KEYS.FAIL_REASON]: true,
|
|
[COLUMN_KEYS.RESULT_URL]: true,
|
|
};
|
|
};
|
|
|
|
// Initialize default column visibility
|
|
const initDefaultColumns = () => {
|
|
const defaults = getDefaultColumnVisibility();
|
|
setVisibleColumns(defaults);
|
|
localStorage.setItem('task-logs-table-columns', JSON.stringify(defaults));
|
|
};
|
|
|
|
// Handle column visibility change
|
|
const handleColumnVisibilityChange = (columnKey, checked) => {
|
|
const updatedColumns = { ...visibleColumns, [columnKey]: checked };
|
|
setVisibleColumns(updatedColumns);
|
|
};
|
|
|
|
// Handle "Select All" checkbox
|
|
const handleSelectAll = (checked) => {
|
|
const allKeys = Object.keys(COLUMN_KEYS).map((key) => COLUMN_KEYS[key]);
|
|
const updatedColumns = {};
|
|
|
|
allKeys.forEach((key) => {
|
|
if (key === COLUMN_KEYS.CHANNEL && !isAdminUser) {
|
|
updatedColumns[key] = false;
|
|
} else {
|
|
updatedColumns[key] = checked;
|
|
}
|
|
});
|
|
|
|
setVisibleColumns(updatedColumns);
|
|
};
|
|
|
|
// Update table when column visibility changes
|
|
useEffect(() => {
|
|
if (Object.keys(visibleColumns).length > 0) {
|
|
localStorage.setItem('task-logs-table-columns', JSON.stringify(visibleColumns));
|
|
}
|
|
}, [visibleColumns]);
|
|
|
|
// Get form values helper function
|
|
const getFormValues = () => {
|
|
const formValues = formApi ? formApi.getValues() : {};
|
|
|
|
// 处理时间范围
|
|
let start_timestamp = timestamp2string(zeroNow.getTime() / 1000);
|
|
let end_timestamp = timestamp2string(now.getTime() / 1000 + 3600);
|
|
|
|
if (formValues.dateRange && Array.isArray(formValues.dateRange) && formValues.dateRange.length === 2) {
|
|
start_timestamp = formValues.dateRange[0];
|
|
end_timestamp = formValues.dateRange[1];
|
|
}
|
|
|
|
return {
|
|
channel_id: formValues.channel_id || '',
|
|
task_id: formValues.task_id || '',
|
|
start_timestamp,
|
|
end_timestamp,
|
|
};
|
|
};
|
|
|
|
// Enrich logs data
|
|
const enrichLogs = (items) => {
|
|
return items.map((log) => ({
|
|
...log,
|
|
timestamp2string: timestamp2string(log.created_at),
|
|
key: '' + log.id,
|
|
}));
|
|
};
|
|
|
|
// Sync page data
|
|
const syncPageData = (payload) => {
|
|
const items = enrichLogs(payload.items || []);
|
|
setLogs(items);
|
|
setLogCount(payload.total || 0);
|
|
setActivePage(payload.page || 1);
|
|
setPageSize(payload.page_size || pageSize);
|
|
};
|
|
|
|
// Load logs function
|
|
const loadLogs = async (page = 1, size = pageSize) => {
|
|
setLoading(true);
|
|
const { channel_id, task_id, start_timestamp, end_timestamp } = getFormValues();
|
|
let localStartTimestamp = parseInt(Date.parse(start_timestamp) / 1000);
|
|
let localEndTimestamp = parseInt(Date.parse(end_timestamp) / 1000);
|
|
let url = isAdminUser
|
|
? `/api/task/?p=${page}&page_size=${size}&channel_id=${channel_id}&task_id=${task_id}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`
|
|
: `/api/task/self?p=${page}&page_size=${size}&task_id=${task_id}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
|
|
const res = await API.get(url);
|
|
const { success, message, data } = res.data;
|
|
if (success) {
|
|
syncPageData(data);
|
|
} else {
|
|
showError(message);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
// Page handlers
|
|
const handlePageChange = (page) => {
|
|
loadLogs(page, pageSize).then();
|
|
};
|
|
|
|
const handlePageSizeChange = async (size) => {
|
|
localStorage.setItem('task-page-size', size + '');
|
|
await loadLogs(1, size);
|
|
};
|
|
|
|
// Refresh function
|
|
const refresh = async () => {
|
|
await loadLogs(1, pageSize);
|
|
};
|
|
|
|
// Copy text function
|
|
const copyText = async (text) => {
|
|
if (await copy(text)) {
|
|
showSuccess(t('已复制:') + text);
|
|
} else {
|
|
Modal.error({ title: t('无法复制到剪贴板,请手动复制'), content: text });
|
|
}
|
|
};
|
|
|
|
// Modal handlers
|
|
const openContentModal = (content) => {
|
|
setModalContent(content);
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
// Initialize data
|
|
useEffect(() => {
|
|
const localPageSize = parseInt(localStorage.getItem('task-page-size')) || ITEMS_PER_PAGE;
|
|
setPageSize(localPageSize);
|
|
loadLogs(1, localPageSize).then();
|
|
}, []);
|
|
|
|
return {
|
|
// Basic state
|
|
logs,
|
|
loading,
|
|
activePage,
|
|
logCount,
|
|
pageSize,
|
|
isAdminUser,
|
|
|
|
// Modal state
|
|
isModalOpen,
|
|
setIsModalOpen,
|
|
modalContent,
|
|
|
|
// Form state
|
|
formApi,
|
|
setFormApi,
|
|
formInitValues,
|
|
getFormValues,
|
|
|
|
// Column visibility
|
|
visibleColumns,
|
|
showColumnSelector,
|
|
setShowColumnSelector,
|
|
handleColumnVisibilityChange,
|
|
handleSelectAll,
|
|
initDefaultColumns,
|
|
COLUMN_KEYS,
|
|
|
|
// Compact mode
|
|
compactMode,
|
|
setCompactMode,
|
|
|
|
// Functions
|
|
loadLogs,
|
|
handlePageChange,
|
|
handlePageSizeChange,
|
|
refresh,
|
|
copyText,
|
|
openContentModal,
|
|
enrichLogs,
|
|
syncPageData,
|
|
|
|
// Translation
|
|
t,
|
|
};
|
|
};
|