🚀 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.
This commit is contained in:
t0ng7u
2025-07-18 21:05:36 +08:00
parent f43c695527
commit 6799daacd1
48 changed files with 3489 additions and 3031 deletions

View File

@@ -0,0 +1,109 @@
import { useCallback, useState, useRef } from 'react';
import { Toast, Modal } from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
import { getTextContent, buildApiPayload, createLoadingAssistantMessage } from '../../helpers';
import { MESSAGE_ROLES } from '../../constants/playground.constants';
export const useMessageEdit = (
setMessage,
inputs,
parameterEnabled,
sendRequest,
saveMessages
) => {
const { t } = useTranslation();
const [editingMessageId, setEditingMessageId] = useState(null);
const [editValue, setEditValue] = useState('');
const editingMessageRef = useRef(null);
const handleMessageEdit = useCallback((targetMessage) => {
const editableContent = getTextContent(targetMessage);
setEditingMessageId(targetMessage.id);
editingMessageRef.current = targetMessage;
setEditValue(editableContent);
}, []);
const handleEditSave = useCallback(() => {
if (!editingMessageId || !editValue.trim()) return;
setMessage(prevMessages => {
let messageIndex = prevMessages.findIndex(msg => msg === editingMessageRef.current);
if (messageIndex === -1) {
messageIndex = prevMessages.findIndex(msg => msg.id === editingMessageId);
}
const targetMessage = prevMessages[messageIndex];
let newContent;
if (Array.isArray(targetMessage.content)) {
newContent = targetMessage.content.map(item =>
item.type === 'text' ? { ...item, text: editValue.trim() } : item
);
} else {
newContent = editValue.trim();
}
const updatedMessages = prevMessages.map(msg =>
msg.id === editingMessageId ? { ...msg, content: newContent } : msg
);
// 处理用户消息编辑后的重新生成
if (targetMessage.role === MESSAGE_ROLES.USER) {
const hasSubsequentAssistantReply = messageIndex < prevMessages.length - 1 &&
prevMessages[messageIndex + 1].role === MESSAGE_ROLES.ASSISTANT;
if (hasSubsequentAssistantReply) {
Modal.confirm({
title: t('消息已编辑'),
content: t('检测到该消息后有AI回复是否删除后续回复并重新生成'),
okText: t('重新生成'),
cancelText: t('仅保存'),
onOk: () => {
const messagesUntilUser = updatedMessages.slice(0, messageIndex + 1);
setMessage(messagesUntilUser);
// 编辑后保存(重新生成的情况),传入更新后的消息列表
setTimeout(() => saveMessages(messagesUntilUser), 0);
setTimeout(() => {
const payload = buildApiPayload(messagesUntilUser, null, inputs, parameterEnabled);
setMessage(prevMsg => [...prevMsg, createLoadingAssistantMessage()]);
sendRequest(payload, inputs.stream);
}, 100);
},
onCancel: () => {
setMessage(updatedMessages);
// 编辑后保存(仅保存的情况),传入更新后的消息列表
setTimeout(() => saveMessages(updatedMessages), 0);
}
});
return prevMessages;
}
}
// 编辑后保存(普通情况),传入更新后的消息列表
setTimeout(() => saveMessages(updatedMessages), 0);
return updatedMessages;
});
setEditingMessageId(null);
editingMessageRef.current = null;
setEditValue('');
Toast.success({ content: t('消息已更新'), duration: 2 });
}, [editingMessageId, editValue, t, inputs, parameterEnabled, sendRequest, setMessage, saveMessages]);
const handleEditCancel = useCallback(() => {
setEditingMessageId(null);
editingMessageRef.current = null;
setEditValue('');
}, []);
return {
editingMessageId,
editValue,
setEditValue,
handleMessageEdit,
handleEditSave,
handleEditCancel
};
};