'use client';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
Box,
Paper,
Typography,
Divider,
CircularProgress,
Tabs,
Tab,
List,
ListItem,
ListItemText,
Collapse,
IconButton,
TextField,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Tooltip,
Menu,
MenuItem
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
import TabPanel from './components/TabPanel';
import ReactMarkdown from 'react-markdown';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import axios from 'axios';
import { toast } from 'sonner';
import 'github-markdown-css/github-markdown-light.css';
/**
* 领域分析组件
* @param {Object} props
* @param {string} props.projectId - 项目ID
* @param {Array} props.toc - 目录结构数组
* @param {Array} props.tags - 标签树数组
* @param {boolean} props.loading - 是否加载中
* @param {Function} props.onTagsUpdate - 标签更新回调
*/
// 领域树节点组件
function TreeNode({ node, level = 0, onEdit, onDelete, onAddChild }) {
const [open, setOpen] = useState(true);
const theme = useTheme();
const hasChildren = node.child && node.child.length > 0;
const [anchorEl, setAnchorEl] = useState(null);
const menuOpen = Boolean(anchorEl);
const { t } = useTranslation();
const handleClick = () => {
if (hasChildren) {
setOpen(!open);
}
};
const handleMenuOpen = event => {
event.stopPropagation();
setAnchorEl(event.currentTarget);
};
const handleMenuClose = event => {
if (event) event.stopPropagation();
setAnchorEl(null);
};
const handleEdit = event => {
event.stopPropagation();
onEdit(node);
handleMenuClose();
};
const handleDelete = event => {
event.stopPropagation();
onDelete(node);
handleMenuClose();
};
const handleAddChild = event => {
event.stopPropagation();
onAddChild(node);
handleMenuClose();
};
return (
<>
{hasChildren && (open ? : )}
{hasChildren && (
{node.child.map((childNode, index) => (
))}
)}
>
);
}
// 领域树组件
function DomainTree({ tags, onEdit, onDelete, onAddChild }) {
return (
{tags.map((node, index) => (
))}
);
}
export default function DomainAnalysis({ projectId, toc = '', loading = false }) {
const theme = useTheme();
const { t } = useTranslation();
const [activeTab, setActiveTab] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [currentNode, setCurrentNode] = useState(null);
const [parentNode, setParentNode] = useState('');
const [dialogMode, setDialogMode] = useState('add');
const [labelValue, setLabelValue] = useState({});
const [saving, setSaving] = useState(false);
const [tags, setTags] = useState([]);
const [snackbar, setSnackbar] = useState({
open: false,
message: '',
severity: 'success'
});
const handleCloseSnackbar = () => {
setSnackbar(prev => ({ ...prev, open: false }));
};
useEffect(() => {
getTags();
}, []);
const getTags = async () => {
const response = await axios.get(`/api/projects/${projectId}/tags`);
setTags(response.data.tags);
};
// 处理标签切换
const handleTabChange = (event, newValue) => {
setActiveTab(newValue);
};
// 打开添加标签对话框
const handleAddTag = () => {
setDialogMode('add');
setCurrentNode(null);
setParentNode(null);
setLabelValue({});
setDialogOpen(true);
};
// 打开编辑标签对话框
const handleEditTag = node => {
setDialogMode('edit');
setCurrentNode({ id: node.id, label: node.label });
setLabelValue({ id: node.id, label: node.label });
setDialogOpen(true);
};
// 打开添加子标签对话框
const handleAddChildTag = parentNode => {
setDialogMode('addChild');
setParentNode(parentNode.label);
setLabelValue({ parentId: parentNode.id });
setDialogOpen(true);
};
// 打开删除标签对话框
const handleDeleteTag = node => {
setCurrentNode(node);
setDeleteDialogOpen(true);
};
// 关闭对话框
const handleCloseDialog = () => {
setDialogOpen(false);
setDeleteDialogOpen(false);
};
// 查找并更新节点
const findAndUpdateNode = (nodes, targetNode, newLabel) => {
return nodes.map(node => {
if (node === targetNode) {
return { ...node, label: newLabel };
}
if (node.child && node.child.length > 0) {
return { ...node, child: findAndUpdateNode(node.child, targetNode, newLabel) };
}
return node;
});
};
// 查找并删除节点
const findAndDeleteNode = (nodes, targetNode) => {
return nodes
.filter(node => node !== targetNode)
.map(node => {
if (node.child && node.child.length > 0) {
return { ...node, child: findAndDeleteNode(node.child, targetNode) };
}
return node;
});
};
// 查找并添加子节点
const findAndAddChildNode = (nodes, parentNode, childLabel) => {
return nodes.map(node => {
if (node === parentNode) {
const childArray = node.child || [];
return {
...node,
child: [...childArray, { label: childLabel, child: [] }]
};
}
if (node.child && node.child.length > 0) {
return { ...node, child: findAndAddChildNode(node.child, parentNode, childLabel) };
}
return node;
});
};
// 保存标签更改
const saveTagChanges = async updatedTags => {
console.log('保存标签更改:', updatedTags);
setSaving(true);
try {
const response = await fetch(`/api/projects/${projectId}/tags`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ tags: updatedTags })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || t('domain.errors.saveFailed'));
}
getTags();
setSnackbar({
open: true,
message: t('domain.messages.updateSuccess'),
severity: 'success'
});
} catch (error) {
console.error('保存标签失败:', error);
setSnackbar({
open: true,
message: error.message || '保存标签失败',
severity: 'error'
});
} finally {
setSaving(false);
}
};
// 提交表单
const handleSubmit = async () => {
if (!labelValue.label.trim()) {
setSnackbar({
open: true,
message: '标签名称不能为空',
severity: 'error'
});
return;
}
await saveTagChanges(labelValue);
handleCloseDialog();
};
const handleConfirmDelete = async () => {
if (!currentNode) return;
const res = await axios.delete(`/api/projects/${projectId}/tags?id=${currentNode.id}`);
if (res.status === 200) {
toast.success('删除成功');
getTags();
}
setDeleteDialogOpen(false);
};
if (loading) {
return (
);
}
if (toc.length === 0) {
return (
{t('domain.noToc')}
);
}
return (
{t('domain.tabs.tree')}
} onClick={handleAddTag}>
{t('domain.addRootTag')}
{tags && tags.length > 0 ? (
) : (
{t('domain.noTags')}
}
onClick={handleAddTag}
sx={{ mt: 1 }}
>
{t('domain.addFirstTag')}
)}
{t('domain.docStructure')}
(
{children}
)
}}
>
{toc}
{/* 添加/编辑标签对话框 */}
{/* 删除确认对话框 */}
);
}