252 lines
7.2 KiB
React
252 lines
7.2 KiB
React
|
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|||
|
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|||
|
|
import apiClient from '../utils/apiClient';
|
|||
|
|
import { ArrowLeft, FileText, Tag, Save } from 'lucide-react';
|
|||
|
|
import MDEditor, * as commands from '@uiw/react-md-editor';
|
|||
|
|
import '@uiw/react-md-editor/markdown-editor.css';
|
|||
|
|
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
|
|||
|
|
import TagEditor from '../components/TagEditor';
|
|||
|
|
import './EditKnowledgeBase.css';
|
|||
|
|
|
|||
|
|
const EditKnowledgeBase = ({ user }) => {
|
|||
|
|
const navigate = useNavigate();
|
|||
|
|
const { kb_id } = useParams();
|
|||
|
|
const [formData, setFormData] = useState({
|
|||
|
|
title: '',
|
|||
|
|
content: '',
|
|||
|
|
tags: ''
|
|||
|
|
});
|
|||
|
|
const [isLoading, setIsLoading] = useState(true);
|
|||
|
|
const [isSaving, setIsSaving] = useState(false);
|
|||
|
|
const [error, setError] = useState('');
|
|||
|
|
const [kb, setKb] = useState(null);
|
|||
|
|
|
|||
|
|
const handleContentChange = useCallback((value) => {
|
|||
|
|
setFormData(prev => ({ ...prev, content: value || '' }));
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetchKbData();
|
|||
|
|
}, [kb_id]);
|
|||
|
|
|
|||
|
|
const fetchKbData = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.DETAIL(kb_id)));
|
|||
|
|
const kbData = response.data;
|
|||
|
|
|
|||
|
|
// Check if current user is the creator
|
|||
|
|
if (kbData.creator_id !== user.user_id) {
|
|||
|
|
navigate('/knowledge-base');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setKb(kbData);
|
|||
|
|
setFormData({
|
|||
|
|
title: kbData.title,
|
|||
|
|
content: kbData.content || '',
|
|||
|
|
tags: kbData.tags ? kbData.tags.map(t => t.name).join(', ') : ''
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
setError('无法加载知识库信息');
|
|||
|
|
console.error('Error fetching knowledge base:', err);
|
|||
|
|
} finally {
|
|||
|
|
setIsLoading(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleInputChange = (e) => {
|
|||
|
|
const { name, value } = e.target;
|
|||
|
|
setFormData(prev => ({
|
|||
|
|
...prev,
|
|||
|
|
[name]: value
|
|||
|
|
}));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSubmit = async (e) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
if (!formData.title.trim()) {
|
|||
|
|
setError('请输入知识库标题');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setIsSaving(true);
|
|||
|
|
setError('');
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const updateData = {
|
|||
|
|
title: formData.title,
|
|||
|
|
content: formData.content,
|
|||
|
|
tags: formData.tags
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
await apiClient.put(buildApiUrl(API_ENDPOINTS.KNOWLEDGE_BASE.UPDATE(kb_id)), updateData);
|
|||
|
|
navigate('/knowledge-base');
|
|||
|
|
} catch (err) {
|
|||
|
|
setError(err.response?.data?.message || '更新知识库失败,请重试');
|
|||
|
|
} finally {
|
|||
|
|
setIsSaving(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 自定义工具栏命令配置
|
|||
|
|
const customCommands = [
|
|||
|
|
commands.bold,
|
|||
|
|
commands.italic,
|
|||
|
|
commands.strikethrough,
|
|||
|
|
commands.hr,
|
|||
|
|
commands.group([
|
|||
|
|
commands.title1,
|
|||
|
|
commands.title2,
|
|||
|
|
commands.title3,
|
|||
|
|
commands.title4,
|
|||
|
|
commands.title5,
|
|||
|
|
commands.title6,
|
|||
|
|
], {
|
|||
|
|
name: 'title',
|
|||
|
|
groupName: 'title',
|
|||
|
|
buttonProps: { 'aria-label': '插入标题', title: '插入标题' }
|
|||
|
|
}),
|
|||
|
|
commands.divider,
|
|||
|
|
commands.link,
|
|||
|
|
commands.quote,
|
|||
|
|
commands.code,
|
|||
|
|
commands.codeBlock,
|
|||
|
|
commands.image,
|
|||
|
|
commands.divider,
|
|||
|
|
commands.unorderedListCommand,
|
|||
|
|
commands.orderedListCommand,
|
|||
|
|
commands.checkedListCommand,
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// 右侧额外命令(预览、全屏等)
|
|||
|
|
const customExtraCommands = [
|
|||
|
|
commands.codeEdit,
|
|||
|
|
commands.codeLive,
|
|||
|
|
commands.codePreview,
|
|||
|
|
commands.divider,
|
|||
|
|
commands.fullscreen,
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
if (isLoading) {
|
|||
|
|
return (
|
|||
|
|
<div className="edit-kb-page">
|
|||
|
|
<div className="loading-container">
|
|||
|
|
<div className="loading-spinner"></div>
|
|||
|
|
<p>加载中...</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="edit-kb-page">
|
|||
|
|
<div className="edit-header">
|
|||
|
|
<Link to="/knowledge-base">
|
|||
|
|
<span className="back-link">
|
|||
|
|
<ArrowLeft size={20} />
|
|||
|
|
<span>返回知识库</span>
|
|||
|
|
</span>
|
|||
|
|
</Link>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="edit-content">
|
|||
|
|
<div className="edit-card">
|
|||
|
|
<header className="edit-card-header">
|
|||
|
|
<h1>编辑知识库</h1>
|
|||
|
|
<p>修改知识库标题、标签和内容摘要</p>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<form onSubmit={handleSubmit} className="edit-form">
|
|||
|
|
<div className="form-group">
|
|||
|
|
<label htmlFor="title">
|
|||
|
|
<FileText size={18} />
|
|||
|
|
知识库标题 *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
id="title"
|
|||
|
|
name="title"
|
|||
|
|
value={formData.title}
|
|||
|
|
onChange={handleInputChange}
|
|||
|
|
placeholder="请输入知识库标题"
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="form-group">
|
|||
|
|
<label htmlFor="tags">
|
|||
|
|
<Tag size={18} />
|
|||
|
|
标签
|
|||
|
|
</label>
|
|||
|
|
<TagEditor
|
|||
|
|
value={formData.tags}
|
|||
|
|
onChange={(value) => setFormData(prev => ({ ...prev, tags: value }))}
|
|||
|
|
placeholder="输入标签,按回车或逗号分隔"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="form-group">
|
|||
|
|
<div className="content-header">
|
|||
|
|
<label htmlFor="content">
|
|||
|
|
<FileText size={18} />
|
|||
|
|
内容总结
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
<div className="markdown-editor-container">
|
|||
|
|
<MDEditor
|
|||
|
|
key="content-editor"
|
|||
|
|
value={formData.content}
|
|||
|
|
onChange={handleContentChange}
|
|||
|
|
data-color-mode="light"
|
|||
|
|
height={500}
|
|||
|
|
preview="edit"
|
|||
|
|
hideToolbar={false}
|
|||
|
|
toolbarBottom={false}
|
|||
|
|
commands={customCommands}
|
|||
|
|
extraCommands={customExtraCommands}
|
|||
|
|
autoFocus={false}
|
|||
|
|
textareaProps={{
|
|||
|
|
placeholder: '在这里编写知识库内容摘要...',
|
|||
|
|
style: {
|
|||
|
|
fontSize: '14px',
|
|||
|
|
lineHeight: '1.5',
|
|||
|
|
fontFamily: 'inherit'
|
|||
|
|
},
|
|||
|
|
spellCheck: false,
|
|||
|
|
autoComplete: 'off',
|
|||
|
|
autoCapitalize: 'off',
|
|||
|
|
autoCorrect: 'off'
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
<div className="markdown-hint">
|
|||
|
|
<small>使用Markdown格式编写知识库内容,支持**粗体**、*斜体*、# 标题、- 列表等格式。</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{error && (
|
|||
|
|
<div className="error-message">{error}</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<div className="form-actions">
|
|||
|
|
<Link to="/knowledge-base">
|
|||
|
|
<span className="btn-cancel">取消</span>
|
|||
|
|
</Link>
|
|||
|
|
<button
|
|||
|
|
type="submit"
|
|||
|
|
className="btn-submit"
|
|||
|
|
disabled={isSaving}
|
|||
|
|
>
|
|||
|
|
<Save size={16} />
|
|||
|
|
{isSaving ? '保存中...' : '保存更改'}
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default EditKnowledgeBase;
|