mirror of
https://github.com/Funkoala14/KnowledgeBase_OOIN.git
synced 2025-06-08 05:49:44 +08:00
Compare commits
3 Commits
5960ce7a28
...
d816b1125d
Author | SHA1 | Date | |
---|---|---|---|
d816b1125d | |||
a85154fbe1 | |||
8f92e25258 |
@ -61,15 +61,15 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: '公共知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'leader', label: 'Leader 级知识库' },
|
{ value: 'leader', label: '组长级知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: '组内知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
{ value: 'secret', label: '保密知识库' },
|
{ value: 'secret', label: '私密知识库' },
|
||||||
];
|
];
|
||||||
} else if (isLeader) {
|
} else if (isLeader) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: '公共知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: '组内知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
@ -180,7 +180,7 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 仅当不是私有知识库时才显示部门选项 */}
|
{/* 仅当不是私有知识库时才显示部门选项 */}
|
||||||
{formData.type !== 'private' && (
|
{formData.type === 'member' && (
|
||||||
<div className='mb-3'>
|
<div className='mb-3'>
|
||||||
<label htmlFor='department' className='form-label'>
|
<label htmlFor='department' className='form-label'>
|
||||||
部门 {isAdmin && needSelectGroup && <span className='text-danger'>*</span>}
|
部门 {isAdmin && needSelectGroup && <span className='text-danger'>*</span>}
|
||||||
@ -220,7 +220,7 @@ const CreateKnowledgeBaseModal = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 仅当不是私有知识库时才显示组别选项 */}
|
{/* 仅当不是私有知识库时才显示组别选项 */}
|
||||||
{formData.type !== 'private' && (
|
{formData.type === 'member' && (
|
||||||
<div className='mb-3'>
|
<div className='mb-3'>
|
||||||
<label htmlFor='group' className='form-label'>
|
<label htmlFor='group' className='form-label'>
|
||||||
组别 {needSelectGroup && <span className='text-danger'>*</span>}
|
组别 {needSelectGroup && <span className='text-danger'>*</span>}
|
||||||
|
@ -47,7 +47,10 @@ export default function Chat() {
|
|||||||
|
|
||||||
// If we have a knowledgeBaseId but no chatId, check if we have an existing chat or create a new one
|
// If we have a knowledgeBaseId but no chatId, check if we have an existing chat or create a new one
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (knowledgeBaseId && !chatId) {
|
// 只有当 knowledgeBaseId 存在但 chatId 不存在,且聊天历史已加载完成时才执行
|
||||||
|
if (knowledgeBaseId && !chatId && status === 'succeeded' && !status.includes('loading')) {
|
||||||
|
console.log('Chat.jsx: 检查是否需要创建聊天...');
|
||||||
|
|
||||||
// 检查是否存在包含此知识库的聊天记录
|
// 检查是否存在包含此知识库的聊天记录
|
||||||
const existingChat = chatHistory.find((chat) => {
|
const existingChat = chatHistory.find((chat) => {
|
||||||
// 检查知识库ID是否匹配
|
// 检查知识库ID是否匹配
|
||||||
@ -60,12 +63,16 @@ export default function Chat() {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
console.log('existingChat', existingChat);
|
console.log('Chat.jsx: existingChat', existingChat);
|
||||||
|
|
||||||
if (existingChat) {
|
if (existingChat) {
|
||||||
|
console.log(
|
||||||
|
`Chat.jsx: 找到现有聊天记录,导航到 /chat/${knowledgeBaseId}/${existingChat.conversation_id}`
|
||||||
|
);
|
||||||
// 找到现有聊天记录,导航到该聊天页面
|
// 找到现有聊天记录,导航到该聊天页面
|
||||||
navigate(`/chat/${knowledgeBaseId}/${existingChat.conversation_id}`);
|
navigate(`/chat/${knowledgeBaseId}/${existingChat.conversation_id}`);
|
||||||
} else {
|
} else {
|
||||||
|
console.log('Chat.jsx: 创建新聊天...');
|
||||||
// 创建新聊天
|
// 创建新聊天
|
||||||
dispatch(
|
dispatch(
|
||||||
createChatRecord({
|
createChatRecord({
|
||||||
@ -77,9 +84,13 @@ export default function Chat() {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
// 创建成功,使用返回的conversation_id导航
|
// 创建成功,使用返回的conversation_id导航
|
||||||
if (response && response.conversation_id) {
|
if (response && response.conversation_id) {
|
||||||
|
console.log(
|
||||||
|
`Chat.jsx: 创建成功,导航到 /chat/${knowledgeBaseId}/${response.conversation_id}`
|
||||||
|
);
|
||||||
navigate(`/chat/${knowledgeBaseId}/${response.conversation_id}`);
|
navigate(`/chat/${knowledgeBaseId}/${response.conversation_id}`);
|
||||||
} else {
|
} else {
|
||||||
// 错误处理
|
// 错误处理
|
||||||
|
console.error('Chat.jsx: 创建失败,未能获取会话ID');
|
||||||
dispatch(
|
dispatch(
|
||||||
showNotification({
|
showNotification({
|
||||||
message: '创建聊天失败:未能获取会话ID',
|
message: '创建聊天失败:未能获取会话ID',
|
||||||
@ -89,6 +100,7 @@ export default function Chat() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
console.error('Chat.jsx: 创建失败', error);
|
||||||
dispatch(
|
dispatch(
|
||||||
showNotification({
|
showNotification({
|
||||||
message: `创建聊天失败: ${error}`,
|
message: `创建聊天失败: ${error}`,
|
||||||
@ -98,7 +110,7 @@ export default function Chat() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [knowledgeBaseId, chatId, chatHistory, navigate, dispatch]);
|
}, [knowledgeBaseId, chatId, chatHistory, status, navigate, dispatch]);
|
||||||
|
|
||||||
const handleDeleteChat = (id) => {
|
const handleDeleteChat = (id) => {
|
||||||
// 调用 Redux action 删除聊天
|
// 调用 Redux action 删除聊天
|
||||||
|
@ -97,16 +97,6 @@ export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading
|
|||||||
<div className='text-truncate fw-medium'>
|
<div className='text-truncate fw-medium'>
|
||||||
{chat.datasets?.map((ds) => ds.name).join(', ') || '未命名知识库'}
|
{chat.datasets?.map((ds) => ds.name).join(', ') || '未命名知识库'}
|
||||||
</div>
|
</div>
|
||||||
<div className='small text-muted text-truncate' style={{ maxWidth: '160px' }}>
|
|
||||||
{chat.last_message
|
|
||||||
? chat.last_message.length > 30
|
|
||||||
? chat.last_message.substring(0, 30) + '...'
|
|
||||||
: chat.last_message
|
|
||||||
: '新对话'}
|
|
||||||
</div>
|
|
||||||
<div className='x-small text-muted mt-1'>
|
|
||||||
{chat.last_time && new Date(chat.last_time).toLocaleDateString()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div
|
<div
|
||||||
|
@ -235,7 +235,7 @@ export default function ChatWindow({ chatId, knowledgeBaseId }) {
|
|||||||
<div
|
<div
|
||||||
key={message.id}
|
key={message.id}
|
||||||
className={`d-flex ${
|
className={`d-flex ${
|
||||||
message.role === 'user' ? 'align-item-end' : 'align-item-start'
|
message.role === 'user' ? 'align-items-end' : 'align-items-start'
|
||||||
} mb-3 flex-column`}
|
} mb-3 flex-column`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -8,6 +8,8 @@ import SvgIcon from '../../components/SvgIcon';
|
|||||||
export default function NewChat() {
|
export default function NewChat() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const [selectedDatasetId, setSelectedDatasetId] = useState(null);
|
||||||
|
const [isNavigating, setIsNavigating] = useState(false);
|
||||||
|
|
||||||
// 从 Redux store 获取可用知识库数据
|
// 从 Redux store 获取可用知识库数据
|
||||||
const datasets = useSelector((state) => state.chat.availableDatasets.items || []);
|
const datasets = useSelector((state) => state.chat.availableDatasets.items || []);
|
||||||
@ -17,6 +19,7 @@ export default function NewChat() {
|
|||||||
// 获取聊天历史记录
|
// 获取聊天历史记录
|
||||||
const chatHistory = useSelector((state) => state.chat.history.items || []);
|
const chatHistory = useSelector((state) => state.chat.history.items || []);
|
||||||
const chatHistoryLoading = useSelector((state) => state.chat.history.status === 'loading');
|
const chatHistoryLoading = useSelector((state) => state.chat.history.status === 'loading');
|
||||||
|
const chatCreationStatus = useSelector((state) => state.chat.sendMessage?.status);
|
||||||
|
|
||||||
// 获取可用知识库列表和聊天历史
|
// 获取可用知识库列表和聊天历史
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -37,26 +40,59 @@ export default function NewChat() {
|
|||||||
}, [error, dispatch]);
|
}, [error, dispatch]);
|
||||||
|
|
||||||
// 处理知识库选择
|
// 处理知识库选择
|
||||||
const handleSelectKnowledgeBase = (dataset) => {
|
const handleSelectKnowledgeBase = async (dataset) => {
|
||||||
// 判断聊天历史中是否已存在该知识库的聊天记录
|
if (isNavigating) return; // 如果正在导航中,阻止重复点击
|
||||||
const existingChat = chatHistory.find((chat) => {
|
|
||||||
// 检查知识库ID是否匹配
|
|
||||||
if (chat.datasets && Array.isArray(chat.datasets)) {
|
|
||||||
return chat.datasets.some((ds) => ds.id === dataset.id);
|
|
||||||
}
|
|
||||||
// 兼容旧格式
|
|
||||||
if (chat.dataset_id_list && Array.isArray(chat.dataset_id_list)) {
|
|
||||||
return chat.dataset_id_list.includes(dataset.id.replace(/-/g, ''));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingChat) {
|
try {
|
||||||
// 找到现有聊天记录,导航到该聊天页面
|
setSelectedDatasetId(dataset.id);
|
||||||
navigate(`/chat/${dataset.id}/${existingChat.conversation_id}`);
|
setIsNavigating(true);
|
||||||
} else {
|
|
||||||
// 没有找到现有聊天记录,创建新的聊天
|
// 判断聊天历史中是否已存在该知识库的聊天记录
|
||||||
navigate(`/chat/${dataset.id}`);
|
const existingChat = chatHistory.find((chat) => {
|
||||||
|
// 检查知识库ID是否匹配
|
||||||
|
if (chat.datasets && Array.isArray(chat.datasets)) {
|
||||||
|
return chat.datasets.some((ds) => ds.id === dataset.id);
|
||||||
|
}
|
||||||
|
// 兼容旧格式
|
||||||
|
if (chat.dataset_id_list && Array.isArray(chat.dataset_id_list)) {
|
||||||
|
return chat.dataset_id_list.includes(dataset.id.replace(/-/g, ''));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingChat) {
|
||||||
|
// 找到现有聊天记录,导航到该聊天页面
|
||||||
|
console.log(`找到现有聊天记录,直接导航到 /chat/${dataset.id}/${existingChat.conversation_id}`);
|
||||||
|
navigate(`/chat/${dataset.id}/${existingChat.conversation_id}`);
|
||||||
|
} else {
|
||||||
|
// 没有找到现有聊天记录,直接创建新的聊天(而不是导航到 /chat/${dataset.id})
|
||||||
|
console.log(`未找到现有聊天记录,直接创建新的聊天,知识库ID: ${dataset.id}`);
|
||||||
|
|
||||||
|
// 直接在这里创建聊天记录,避免在 Chat.jsx 中再次创建
|
||||||
|
const response = await dispatch(
|
||||||
|
createChatRecord({
|
||||||
|
dataset_id_list: [dataset.id.replace(/-/g, '')],
|
||||||
|
question: '选择当前知识库,创建聊天',
|
||||||
|
})
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
if (response && response.conversation_id) {
|
||||||
|
console.log(`创建成功,导航到 /chat/${dataset.id}/${response.conversation_id}`);
|
||||||
|
navigate(`/chat/${dataset.id}/${response.conversation_id}`);
|
||||||
|
} else {
|
||||||
|
throw new Error('未能获取会话ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('导航或创建聊天失败:', error);
|
||||||
|
dispatch(
|
||||||
|
showNotification({
|
||||||
|
message: `创建聊天失败: ${error.message || '请重试'}`,
|
||||||
|
type: 'danger',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setIsNavigating(false);
|
||||||
|
setSelectedDatasetId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,17 +109,48 @@ export default function NewChat() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container-fluid px-4 py-5'>
|
<div className='container-fluid px-4 py-5'>
|
||||||
|
{/* 导航中的遮罩层 */}
|
||||||
|
{isNavigating && (
|
||||||
|
<div
|
||||||
|
className='position-fixed top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center'
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.7)',
|
||||||
|
zIndex: 1050,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='text-center'>
|
||||||
|
<div className='spinner-border mb-2' role='status'>
|
||||||
|
<span className='visually-hidden'>加载中...</span>
|
||||||
|
</div>
|
||||||
|
<div>正在加载聊天界面...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<h4 className='mb-4'>选择知识库开始聊天</h4>
|
<h4 className='mb-4'>选择知识库开始聊天</h4>
|
||||||
<div className='row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 justify-content-center'>
|
<div className='row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 justify-content-center'>
|
||||||
{datasets.length > 0 ? (
|
{datasets.length > 0 ? (
|
||||||
datasets.map((dataset) => (
|
datasets.map((dataset) => (
|
||||||
<div key={dataset.id} className='col'>
|
<div key={dataset.id} className='col'>
|
||||||
<div
|
<div
|
||||||
className='card h-100 shadow-sm border-0 cursor-pointer'
|
className={`card h-100 shadow-sm border-0 ${!isNavigating ? 'cursor-pointer' : ''} ${
|
||||||
|
selectedDatasetId === dataset.id ? 'border-primary' : ''
|
||||||
|
}`}
|
||||||
onClick={() => handleSelectKnowledgeBase(dataset)}
|
onClick={() => handleSelectKnowledgeBase(dataset)}
|
||||||
|
style={{ opacity: isNavigating && selectedDatasetId !== dataset.id ? 0.6 : 1 }}
|
||||||
>
|
>
|
||||||
<div className='card-body'>
|
<div className='card-body'>
|
||||||
<h5 className='card-title'>{dataset.name}</h5>
|
<h5 className='card-title d-flex justify-content-between align-items-center'>
|
||||||
|
{dataset.name}
|
||||||
|
{selectedDatasetId === dataset.id && isNavigating && (
|
||||||
|
<div
|
||||||
|
className='spinner-border spinner-border-sm text-primary'
|
||||||
|
role='status'
|
||||||
|
>
|
||||||
|
<span className='visually-hidden'>加载中...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</h5>
|
||||||
<p className='card-text text-muted'>{dataset.desc || dataset.description || ''}</p>
|
<p className='card-text text-muted'>{dataset.desc || dataset.description || ''}</p>
|
||||||
<div className='text-muted small d-flex align-items-center gap-2'>
|
<div className='text-muted small d-flex align-items-center gap-2'>
|
||||||
<span className='d-flex align-items-center gap-1'>
|
<span className='d-flex align-items-center gap-1'>
|
||||||
|
@ -89,10 +89,10 @@ export default function SettingsTab({ knowledgeBase }) {
|
|||||||
allowed = ['admin', 'leader', 'member', 'private', 'secret'].includes(value);
|
allowed = ['admin', 'leader', 'member', 'private', 'secret'].includes(value);
|
||||||
} else if (role === 'leader') {
|
} else if (role === 'leader') {
|
||||||
// 组长只能选择 member 和 private
|
// 组长只能选择 member 和 private
|
||||||
allowed = ['member', 'private'].includes(value);
|
allowed = ['admin', 'member', 'private'].includes(value);
|
||||||
} else {
|
} else {
|
||||||
// 普通成员只能选择 private
|
// 普通成员只能选择公共和private
|
||||||
allowed = value === 'private';
|
allowed = ['admin', 'private'].includes(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
@ -299,6 +299,7 @@ export default function SettingsTab({ knowledgeBase }) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
// Navigate back to knowledge base list
|
// Navigate back to knowledge base list
|
||||||
|
// Redux store 已在 reducer 中更新,不需要重新调用接口获取知识库列表
|
||||||
navigate('/knowledge-base');
|
navigate('/knowledge-base');
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -27,15 +27,15 @@ const KnowledgeBaseForm = ({
|
|||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: '公共知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'leader', label: 'Leader 级知识库' },
|
{ value: 'leader', label: '组长级知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: '组内知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
{ value: 'secret', label: '保密知识库' },
|
{ value: 'secret', label: '私密知识库' },
|
||||||
];
|
];
|
||||||
} else if (isLeader) {
|
} else if (isLeader) {
|
||||||
return [
|
return [
|
||||||
{ value: 'admin', label: '公共知识库' },
|
{ value: 'admin', label: '公共知识库' },
|
||||||
{ value: 'member', label: 'Member 级知识库' },
|
{ value: 'member', label: '组内知识库' },
|
||||||
{ value: 'private', label: '私有知识库' },
|
{ value: 'private', label: '私有知识库' },
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
@ -125,7 +125,7 @@ const KnowledgeBaseForm = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 仅当不是私有知识库时才显示部门选项 */}
|
{/* 仅当不是私有知识库时才显示部门选项 */}
|
||||||
{formData.type !== 'private' && (
|
{formData.type === 'member' && (
|
||||||
<div className='mb-3'>
|
<div className='mb-3'>
|
||||||
<label htmlFor='department' className='form-label'>
|
<label htmlFor='department' className='form-label'>
|
||||||
部门 {isAdmin && <span className='text-danger'>*</span>}
|
部门 {isAdmin && <span className='text-danger'>*</span>}
|
||||||
@ -161,14 +161,13 @@ const KnowledgeBaseForm = ({
|
|||||||
value={formData.department || ''}
|
value={formData.department || ''}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<small className='text-muted'>部门信息根据知识库创建者自动填写</small>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 仅当不是私有知识库时才显示组别选项 */}
|
{/* 仅当不是私有知识库时才显示组别选项 */}
|
||||||
{formData.type !== 'private' && (
|
{formData.type === 'member' && (
|
||||||
<div className='mb-3'>
|
<div className='mb-3'>
|
||||||
<label htmlFor='group' className='form-label'>
|
<label htmlFor='group' className='form-label'>
|
||||||
组别 {isAdmin && <span className='text-danger'>*</span>}
|
组别 {isAdmin && <span className='text-danger'>*</span>}
|
||||||
@ -205,7 +204,6 @@ const KnowledgeBaseForm = ({
|
|||||||
value={formData.group || ''}
|
value={formData.group || ''}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<small className='text-muted'>组别信息根据知识库创建者自动填写</small>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,6 +108,7 @@ export default function KnowledgeBase() {
|
|||||||
// Handle search input change
|
// Handle search input change
|
||||||
const handleSearchInputChange = (e) => {
|
const handleSearchInputChange = (e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
|
console.log('搜索框输入值:', value);
|
||||||
setSearchKeyword(value);
|
setSearchKeyword(value);
|
||||||
|
|
||||||
// 如果搜索框清空,关闭下拉框
|
// 如果搜索框清空,关闭下拉框
|
||||||
@ -206,10 +207,10 @@ export default function KnowledgeBase() {
|
|||||||
allowed = ['admin', 'leader', 'member', 'private', 'secret'].includes(value);
|
allowed = ['admin', 'leader', 'member', 'private', 'secret'].includes(value);
|
||||||
} else if (role === 'leader') {
|
} else if (role === 'leader') {
|
||||||
// 组长只能选择 member 和 private
|
// 组长只能选择 member 和 private
|
||||||
allowed = ['member', 'private'].includes(value);
|
allowed = ['admin', 'member', 'private'].includes(value);
|
||||||
} else {
|
} else {
|
||||||
// 普通成员只能选择 private
|
// 普通成员只能选择 private
|
||||||
allowed = value === 'private';
|
allowed = ['admin', 'private'].includes(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
@ -401,6 +402,7 @@ export default function KnowledgeBase() {
|
|||||||
const handleDelete = (e, id) => {
|
const handleDelete = (e, id) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
// Dispatch delete knowledge base action
|
// Dispatch delete knowledge base action
|
||||||
dispatch(deleteKnowledgeBase(id))
|
dispatch(deleteKnowledgeBase(id))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -411,6 +413,7 @@ export default function KnowledgeBase() {
|
|||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
// 不需要重新获取知识库列表,Redux store 已经在 reducer 中更新
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
|
@ -94,13 +94,14 @@ export default function KnowledgeCard({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{access === 'full' || access === 'read' ? (
|
{access === 'full' || access === 'read' ? (
|
||||||
<button
|
<></>
|
||||||
className='btn btn-outline-dark btn-sm d-flex align-items-center gap-1'
|
// <button
|
||||||
onClick={handleNewChat}
|
// className='btn btn-outline-dark btn-sm d-flex align-items-center gap-1'
|
||||||
>
|
// onClick={handleNewChat}
|
||||||
<SvgIcon className={'chat-dot'} />
|
// >
|
||||||
新聊天
|
// <SvgIcon className={'chat-dot'} />
|
||||||
</button>
|
// 新聊天
|
||||||
|
// </button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
className='btn btn-outline-dark btn-sm d-flex align-items-center gap-1'
|
className='btn btn-outline-dark btn-sm d-flex align-items-center gap-1'
|
||||||
|
@ -222,6 +222,24 @@ export default function PendingRequests() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取知识库类型的中文描述
|
||||||
|
const getKnowledgeBaseTypeText = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'admin':
|
||||||
|
return '公共知识库';
|
||||||
|
case 'member':
|
||||||
|
return '组内知识库';
|
||||||
|
case 'private':
|
||||||
|
return '私人知识库';
|
||||||
|
case 'leader':
|
||||||
|
return '组长级知识库';
|
||||||
|
case 'secret':
|
||||||
|
return '私密知识库';
|
||||||
|
default:
|
||||||
|
return '未知类型';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 渲染加载状态
|
// 渲染加载状态
|
||||||
if (fetchStatus === 'loading' && permissionRequests.length === 0) {
|
if (fetchStatus === 'loading' && permissionRequests.length === 0) {
|
||||||
return (
|
return (
|
||||||
@ -275,7 +293,13 @@ export default function PendingRequests() {
|
|||||||
|
|
||||||
<div className='request-content'>
|
<div className='request-content'>
|
||||||
<div className='d-flex justify-content-between align-items-start mb-2'>
|
<div className='d-flex justify-content-between align-items-start mb-2'>
|
||||||
<p className='mb-0'>申请访问:{request.knowledge_base.name}</p>
|
<div>
|
||||||
|
<p className='mb-0'>申请访问:{request.knowledge_base.name}</p>
|
||||||
|
<small className='text-muted'>
|
||||||
|
类型:{getKnowledgeBaseTypeText(request.knowledge_base.type)}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
{renderStatusBadge(request.status)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{request.permissions.can_edit ? (
|
{request.permissions.can_edit ? (
|
||||||
|
@ -21,6 +21,24 @@ export default function RequestDetailSlideOver({
|
|||||||
const knowledgeBaseId = request.knowledge_base?.id || '';
|
const knowledgeBaseId = request.knowledge_base?.id || '';
|
||||||
const knowledgeBaseType = request.knowledge_base?.type || '';
|
const knowledgeBaseType = request.knowledge_base?.type || '';
|
||||||
|
|
||||||
|
// 获取知识库类型的中文描述
|
||||||
|
const getKnowledgeBaseTypeText = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'admin':
|
||||||
|
return '公共知识库';
|
||||||
|
case 'member':
|
||||||
|
return '组内知识库';
|
||||||
|
case 'private':
|
||||||
|
return '私人知识库';
|
||||||
|
case 'leader':
|
||||||
|
return '组长级知识库';
|
||||||
|
case 'secret':
|
||||||
|
return '私密知识库';
|
||||||
|
default:
|
||||||
|
return '未知类型';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`slide-over-backdrop ${show ? 'show' : ''}`} onClick={onClose}></div>
|
<div className={`slide-over-backdrop ${show ? 'show' : ''}`} onClick={onClose}></div>
|
||||||
@ -52,7 +70,7 @@ export default function RequestDetailSlideOver({
|
|||||||
</p>
|
</p>
|
||||||
{knowledgeBaseType && (
|
{knowledgeBaseType && (
|
||||||
<p className='mb-1'>
|
<p className='mb-1'>
|
||||||
<strong>类型:</strong> {knowledgeBaseType}
|
<strong>类型:</strong> {getKnowledgeBaseTypeText(knowledgeBaseType)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -112,7 +130,8 @@ export default function RequestDetailSlideOver({
|
|||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
<h6 className='text-muted mb-2'>审批人</h6>
|
<h6 className='text-muted mb-2'>审批人</h6>
|
||||||
<p className='mb-1'>
|
<p className='mb-1'>
|
||||||
{request.approver.name || request.approver.username} ({request.approver.department || '未分配部门'})
|
{request.approver.name || request.approver.username} (
|
||||||
|
{request.approver.department || '未分配部门'})
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -126,9 +145,7 @@ export default function RequestDetailSlideOver({
|
|||||||
|
|
||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
<h6 className='text-muted mb-2'>申请理由</h6>
|
<h6 className='text-muted mb-2'>申请理由</h6>
|
||||||
<div className='p-3 bg-light rounded'>
|
<div className='p-3 bg-light rounded'>{request.reason || '无申请理由'}</div>
|
||||||
{request.reason || '无申请理由'}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{request.status === 'pending' && (
|
{request.status === 'pending' && (
|
||||||
|
@ -104,7 +104,12 @@ const knowledgeBaseSlice = createSlice({
|
|||||||
})
|
})
|
||||||
.addCase(deleteKnowledgeBase.fulfilled, (state, action) => {
|
.addCase(deleteKnowledgeBase.fulfilled, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.knowledgeBases = state.knowledgeBases.filter((kb) => kb.id !== action.meta.arg.knowledgeBaseId);
|
const deletedId = action.payload;
|
||||||
|
state.knowledgeBases = state.knowledgeBases.filter((kb) => kb.id !== deletedId);
|
||||||
|
if (state.pagination.total > 0) {
|
||||||
|
state.pagination.total -= 1;
|
||||||
|
state.pagination.total_pages = Math.ceil(state.pagination.total / state.pagination.page_size);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.addCase(deleteKnowledgeBase.rejected, (state, action) => {
|
.addCase(deleteKnowledgeBase.rejected, (state, action) => {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user