2025-05-30 22:29:02 +08:00
|
|
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
🚀 feat(web/channels): Deep modular refactor of Channels table
1. Split monolithic `ChannelsTable` (2200+ LOC) into focused components
• `channels/index.jsx` – composition entry
• `ChannelsTable.jsx` – pure `<Table>` rendering
• `ChannelsActions.jsx` – bulk & settings toolbar
• `ChannelsFilters.jsx` – search / create / column-settings form
• `ChannelsTabs.jsx` – type tabs
• `ChannelsColumnDefs.js` – column definitions & render helpers
• `modals/` – BatchTag, ColumnSelector, ModelTest modals
2. Extract domain hook
• Moved `useChannelsData.js` → `src/hooks/channels/useChannelsData.js`
– centralises state, API calls, pagination, filters, batch ops
– now exports `setActivePage`, fixing tab / status switch errors
3. Update wiring
• All sub-components consume data via `useChannelsData` props
• Adjusted import paths after hook relocation
4. Clean legacy file
• Legacy `components/table/ChannelsTable.js` now re-exports new module
5. Bug fixes
• Tab switching, status filter & tag aggregation restored
• Column selector & batch actions operate via unified hook
This commit completes the first phase of modularising the Channels feature, laying groundwork for consistent, maintainable table architecture across the app.
2025-07-18 21:05:36 +08:00
|
|
|
import { DEFAULT_MESSAGES, DEFAULT_CONFIG, DEBUG_TABS, MESSAGE_STATUS } from '../../constants/playground.constants';
|
|
|
|
|
import { loadConfig, saveConfig, loadMessages, saveMessages } from '../../components/playground/configStorage';
|
|
|
|
|
import { processIncompleteThinkTags } from '../../helpers';
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
export const usePlaygroundState = () => {
|
2025-06-02 21:21:46 +08:00
|
|
|
// 使用惰性初始化,确保只在组件首次挂载时加载配置和消息
|
|
|
|
|
const [savedConfig] = useState(() => loadConfig());
|
|
|
|
|
const [initialMessages] = useState(() => loadMessages() || DEFAULT_MESSAGES);
|
2025-06-01 17:07:36 +08:00
|
|
|
|
2025-05-30 22:14:44 +08:00
|
|
|
// 基础配置状态
|
|
|
|
|
const [inputs, setInputs] = useState(savedConfig.inputs || DEFAULT_CONFIG.inputs);
|
|
|
|
|
const [parameterEnabled, setParameterEnabled] = useState(
|
|
|
|
|
savedConfig.parameterEnabled || DEFAULT_CONFIG.parameterEnabled
|
|
|
|
|
);
|
|
|
|
|
const [showDebugPanel, setShowDebugPanel] = useState(
|
|
|
|
|
savedConfig.showDebugPanel || DEFAULT_CONFIG.showDebugPanel
|
|
|
|
|
);
|
2025-06-01 17:07:36 +08:00
|
|
|
const [customRequestMode, setCustomRequestMode] = useState(
|
|
|
|
|
savedConfig.customRequestMode || DEFAULT_CONFIG.customRequestMode
|
|
|
|
|
);
|
|
|
|
|
const [customRequestBody, setCustomRequestBody] = useState(
|
|
|
|
|
savedConfig.customRequestBody || DEFAULT_CONFIG.customRequestBody
|
|
|
|
|
);
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
// UI状态
|
|
|
|
|
const [showSettings, setShowSettings] = useState(false);
|
|
|
|
|
const [models, setModels] = useState([]);
|
|
|
|
|
const [groups, setGroups] = useState([]);
|
|
|
|
|
const [status, setStatus] = useState({});
|
|
|
|
|
|
2025-06-01 17:07:36 +08:00
|
|
|
// 消息相关状态 - 使用加载的消息初始化
|
|
|
|
|
const [message, setMessage] = useState(initialMessages);
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
// 调试状态
|
|
|
|
|
const [debugData, setDebugData] = useState({
|
|
|
|
|
request: null,
|
|
|
|
|
response: null,
|
|
|
|
|
timestamp: null,
|
|
|
|
|
previewRequest: null,
|
|
|
|
|
previewTimestamp: null
|
|
|
|
|
});
|
|
|
|
|
const [activeDebugTab, setActiveDebugTab] = useState(DEBUG_TABS.PREVIEW);
|
|
|
|
|
const [previewPayload, setPreviewPayload] = useState(null);
|
|
|
|
|
|
|
|
|
|
// 编辑状态
|
|
|
|
|
const [editingMessageId, setEditingMessageId] = useState(null);
|
|
|
|
|
const [editValue, setEditValue] = useState('');
|
|
|
|
|
|
|
|
|
|
// Refs
|
|
|
|
|
const sseSourceRef = useRef(null);
|
|
|
|
|
const chatRef = useRef(null);
|
|
|
|
|
const saveConfigTimeoutRef = useRef(null);
|
2025-06-01 17:07:36 +08:00
|
|
|
const saveMessagesTimeoutRef = useRef(null);
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
// 配置更新函数
|
|
|
|
|
const handleInputChange = useCallback((name, value) => {
|
|
|
|
|
setInputs(prev => ({ ...prev, [name]: value }));
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleParameterToggle = useCallback((paramName) => {
|
|
|
|
|
setParameterEnabled(prev => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[paramName]: !prev[paramName]
|
|
|
|
|
}));
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-06-02 21:39:51 +08:00
|
|
|
// 消息保存函数 - 改为立即保存,可以接受参数
|
|
|
|
|
const saveMessagesImmediately = useCallback((messagesToSave) => {
|
|
|
|
|
// 如果提供了参数,使用参数;否则使用当前状态
|
|
|
|
|
saveMessages(messagesToSave || message);
|
2025-06-01 17:07:36 +08:00
|
|
|
}, [message]);
|
|
|
|
|
|
2025-05-30 22:14:44 +08:00
|
|
|
// 配置保存
|
|
|
|
|
const debouncedSaveConfig = useCallback(() => {
|
|
|
|
|
if (saveConfigTimeoutRef.current) {
|
|
|
|
|
clearTimeout(saveConfigTimeoutRef.current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
saveConfigTimeoutRef.current = setTimeout(() => {
|
|
|
|
|
const configToSave = {
|
|
|
|
|
inputs,
|
|
|
|
|
parameterEnabled,
|
|
|
|
|
showDebugPanel,
|
2025-06-01 17:07:36 +08:00
|
|
|
customRequestMode,
|
|
|
|
|
customRequestBody,
|
2025-05-30 22:14:44 +08:00
|
|
|
};
|
|
|
|
|
saveConfig(configToSave);
|
|
|
|
|
}, 1000);
|
2025-06-01 17:07:36 +08:00
|
|
|
}, [inputs, parameterEnabled, showDebugPanel, customRequestMode, customRequestBody]);
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
// 配置导入/重置
|
|
|
|
|
const handleConfigImport = useCallback((importedConfig) => {
|
|
|
|
|
if (importedConfig.inputs) {
|
|
|
|
|
setInputs(prev => ({ ...prev, ...importedConfig.inputs }));
|
|
|
|
|
}
|
|
|
|
|
if (importedConfig.parameterEnabled) {
|
|
|
|
|
setParameterEnabled(prev => ({ ...prev, ...importedConfig.parameterEnabled }));
|
|
|
|
|
}
|
|
|
|
|
if (typeof importedConfig.showDebugPanel === 'boolean') {
|
|
|
|
|
setShowDebugPanel(importedConfig.showDebugPanel);
|
|
|
|
|
}
|
2025-06-01 17:07:36 +08:00
|
|
|
if (importedConfig.customRequestMode) {
|
|
|
|
|
setCustomRequestMode(importedConfig.customRequestMode);
|
|
|
|
|
}
|
|
|
|
|
if (importedConfig.customRequestBody) {
|
|
|
|
|
setCustomRequestBody(importedConfig.customRequestBody);
|
|
|
|
|
}
|
|
|
|
|
// 如果导入的配置包含消息,也恢复消息
|
|
|
|
|
if (importedConfig.messages && Array.isArray(importedConfig.messages)) {
|
|
|
|
|
setMessage(importedConfig.messages);
|
|
|
|
|
}
|
2025-05-30 22:14:44 +08:00
|
|
|
}, []);
|
|
|
|
|
|
2025-06-01 17:07:36 +08:00
|
|
|
const handleConfigReset = useCallback((options = {}) => {
|
|
|
|
|
const { resetMessages = false } = options;
|
|
|
|
|
|
2025-05-30 22:29:02 +08:00
|
|
|
setInputs(DEFAULT_CONFIG.inputs);
|
|
|
|
|
setParameterEnabled(DEFAULT_CONFIG.parameterEnabled);
|
|
|
|
|
setShowDebugPanel(DEFAULT_CONFIG.showDebugPanel);
|
2025-06-01 17:07:36 +08:00
|
|
|
setCustomRequestMode(DEFAULT_CONFIG.customRequestMode);
|
|
|
|
|
setCustomRequestBody(DEFAULT_CONFIG.customRequestBody);
|
|
|
|
|
|
|
|
|
|
// 只有在明确指定时才重置消息
|
|
|
|
|
if (resetMessages) {
|
2025-06-02 23:24:50 +08:00
|
|
|
setMessage([]);
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
setMessage(DEFAULT_MESSAGES);
|
|
|
|
|
}, 0);
|
2025-06-01 17:07:36 +08:00
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 清理定时器
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return () => {
|
|
|
|
|
if (saveConfigTimeoutRef.current) {
|
|
|
|
|
clearTimeout(saveConfigTimeoutRef.current);
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-05-30 22:14:44 +08:00
|
|
|
}, []);
|
|
|
|
|
|
2025-06-02 23:56:58 +08:00
|
|
|
// 页面首次加载时,若最后一条消息仍处于 LOADING/INCOMPLETE 状态,自动修复
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!Array.isArray(message) || message.length === 0) return;
|
|
|
|
|
|
|
|
|
|
const lastMsg = message[message.length - 1];
|
|
|
|
|
if (lastMsg.status === MESSAGE_STATUS.LOADING || lastMsg.status === MESSAGE_STATUS.INCOMPLETE) {
|
|
|
|
|
const processed = processIncompleteThinkTags(
|
|
|
|
|
lastMsg.content || '',
|
|
|
|
|
lastMsg.reasoningContent || ''
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const fixedLastMsg = {
|
|
|
|
|
...lastMsg,
|
|
|
|
|
status: MESSAGE_STATUS.COMPLETE,
|
|
|
|
|
content: processed.content,
|
|
|
|
|
reasoningContent: processed.reasoningContent || null,
|
|
|
|
|
isThinkingComplete: true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const updatedMessages = [...message.slice(0, -1), fixedLastMsg];
|
|
|
|
|
setMessage(updatedMessages);
|
|
|
|
|
|
|
|
|
|
// 保存修复后的消息列表
|
|
|
|
|
setTimeout(() => saveMessagesImmediately(updatedMessages), 0);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-05-30 22:14:44 +08:00
|
|
|
return {
|
|
|
|
|
// 配置状态
|
|
|
|
|
inputs,
|
|
|
|
|
parameterEnabled,
|
|
|
|
|
showDebugPanel,
|
2025-06-01 17:07:36 +08:00
|
|
|
customRequestMode,
|
|
|
|
|
customRequestBody,
|
2025-05-30 22:14:44 +08:00
|
|
|
|
|
|
|
|
// UI状态
|
|
|
|
|
showSettings,
|
|
|
|
|
models,
|
|
|
|
|
groups,
|
|
|
|
|
status,
|
|
|
|
|
|
|
|
|
|
// 消息状态
|
|
|
|
|
message,
|
|
|
|
|
|
|
|
|
|
// 调试状态
|
|
|
|
|
debugData,
|
|
|
|
|
activeDebugTab,
|
|
|
|
|
previewPayload,
|
|
|
|
|
|
|
|
|
|
// 编辑状态
|
|
|
|
|
editingMessageId,
|
|
|
|
|
editValue,
|
|
|
|
|
|
|
|
|
|
// Refs
|
|
|
|
|
sseSourceRef,
|
|
|
|
|
chatRef,
|
|
|
|
|
saveConfigTimeoutRef,
|
|
|
|
|
|
|
|
|
|
// 更新函数
|
|
|
|
|
setInputs,
|
|
|
|
|
setParameterEnabled,
|
|
|
|
|
setShowDebugPanel,
|
2025-06-01 17:07:36 +08:00
|
|
|
setCustomRequestMode,
|
|
|
|
|
setCustomRequestBody,
|
2025-05-30 22:14:44 +08:00
|
|
|
setShowSettings,
|
|
|
|
|
setModels,
|
|
|
|
|
setGroups,
|
|
|
|
|
setStatus,
|
|
|
|
|
setMessage,
|
|
|
|
|
setDebugData,
|
|
|
|
|
setActiveDebugTab,
|
|
|
|
|
setPreviewPayload,
|
|
|
|
|
setEditingMessageId,
|
|
|
|
|
setEditValue,
|
|
|
|
|
|
|
|
|
|
// 处理函数
|
|
|
|
|
handleInputChange,
|
|
|
|
|
handleParameterToggle,
|
|
|
|
|
debouncedSaveConfig,
|
2025-06-02 21:39:51 +08:00
|
|
|
saveMessagesImmediately,
|
2025-05-30 22:14:44 +08:00
|
|
|
handleConfigImport,
|
|
|
|
|
handleConfigReset,
|
|
|
|
|
};
|
|
|
|
|
};
|