[dev]add mock data

This commit is contained in:
susie-laptop 2025-03-19 20:22:02 -04:00
parent b4a0874a4d
commit 523c474001
12 changed files with 363 additions and 462 deletions

View File

@ -21,7 +21,8 @@ export default function HeaderWithNav() {
const isActive = (path) => {
return location.pathname.startsWith(path);
};
console.log('user', user);
// leader admin
const hasManagePermission = user && (user.role === 'leader' || user.role === 'admin');

View File

@ -4,6 +4,7 @@ import { fetchMessages, sendMessage } from '../../store/chat/chat.messages.thunk
import { resetMessages, resetSendMessageStatus } from '../../store/chat/chat.slice';
import { showNotification } from '../../store/notification.slice';
import SvgIcon from '../../components/SvgIcon';
import { fetchKnowledgeBases } from '../../store/knowledgeBase/knowledgeBase.thunks';
export default function ChatWindow({ chatId, knowledgeBaseId }) {
const dispatch = useDispatch();
@ -18,8 +19,9 @@ export default function ChatWindow({ chatId, knowledgeBaseId }) {
} = useSelector((state) => state.chat.messages);
const { status: sendStatus, error: sendError } = useSelector((state) => state.chat.sendMessage);
const knowledgeBase = useSelector((state) =>
state.knowledgeBase.list.items.find((kb) => kb.id === knowledgeBaseId)
state.knowledgeBase.list.data?.items?.find((kb) => kb.id === knowledgeBaseId)
);
const isLoadingKnowledgeBases = useSelector((state) => state.knowledgeBase.list.isLoading);
//
useEffect(() => {
@ -51,6 +53,13 @@ export default function ChatWindow({ chatId, knowledgeBaseId }) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// Redux store
useEffect(() => {
if (!knowledgeBase && !isLoadingKnowledgeBases) {
dispatch(fetchKnowledgeBases());
}
}, [dispatch, knowledgeBase, isLoadingKnowledgeBases]);
const handleSendMessage = (e) => {
e.preventDefault();

View File

@ -1,87 +1,92 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { showNotification } from '../../store/notification.slice';
import { get } from '../../services/api';
import { fetchKnowledgeBases } from '../../store/knowledgeBase/knowledgeBase.thunks';
import SvgIcon from '../../components/SvgIcon';
export default function NewChat() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [knowledgeBases, setKnowledgeBases] = useState([]);
const [loading, setLoading] = useState(true);
// Redux store
const { data, status, error } = useSelector((state) => state.knowledgeBase.list);
const knowledgeBases = data?.items || [];
const isLoading = status === 'loading';
//
useEffect(() => {
const fetchKnowledgeBases = async () => {
try {
setLoading(true);
const response = await get('/knowledge-bases/');
if (!data?.items?.length && status !== 'loading') {
dispatch(fetchKnowledgeBases());
}
}, [dispatch, data, status]);
// can_read
const readableKnowledgeBases = response.data.items.filter(
(kb) => kb.permissions && kb.permissions.can_read === true
);
//
useEffect(() => {
if (status === 'failed' && error) {
dispatch(
showNotification({
message: `获取知识库列表失败: ${error.message || error}`,
type: 'danger',
})
);
}
}, [status, error, dispatch]);
setKnowledgeBases(readableKnowledgeBases);
} catch (error) {
console.error('获取知识库列表失败:', error);
dispatch(
showNotification({
message: '获取知识库列表失败,请稍后重试',
type: 'danger',
})
);
} finally {
setLoading(false);
}
};
fetchKnowledgeBases();
}, [dispatch]);
// can_read
const readableKnowledgeBases = knowledgeBases.filter((kb) => kb.permissions && kb.permissions.can_read === true);
const handleSelectKnowledgeBase = (knowledgeBaseId) => {
//
navigate(`/chat/${knowledgeBaseId}`);
};
return (
<div className='new-chat container py-4'>
<div className='text-center mb-5'>
<h2 className='mb-4'>选择知识库开始聊天</h2>
//
if (isLoading) {
return (
<div className='container-fluid px-4 py-5 text-center'>
<div className='spinner-border' role='status'>
<span className='visually-hidden'>加载中...</span>
</div>
</div>
);
}
{loading ? (
<div className='d-flex justify-content-center my-5'>
<div className='spinner-border' role='status'>
<span className='visually-hidden'>加载中...</span>
</div>
</div>
) : knowledgeBases.length === 0 ? (
<div className='text-center my-5'>
<p className='text-muted'>没有可用的知识库请联系管理员获取权限</p>
</div>
) : (
<div className='row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 justify-content-center'>
{knowledgeBases.map((kb) => (
return (
<div className='container-fluid px-4 py-5'>
<h4 className='mb-4'>选择知识库开始聊天</h4>
<div className='row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4'>
{readableKnowledgeBases.length > 0 ? (
readableKnowledgeBases.map((kb) => (
<div key={kb.id} className='col'>
<div
className='card h-100 bg-light border-0 cursor-pointer'
className='card h-100 shadow-sm border-0 cursor-pointer'
onClick={() => handleSelectKnowledgeBase(kb.id)}
style={{ cursor: 'pointer' }}
>
<div className='card-body py-4'>
<p className='card-title h5'>{kb.name}</p>
<p className='card-text text-muted'>{kb.description}</p>
<div className='d-flex justify-content-between align-items-center mt-3'>
<small className='text-muted'>文档数: {kb.document_count || 0}</small>
<div className='card-body'>
<h5 className='card-title'>{kb.name}</h5>
<p className='card-text text-muted'>{kb.desc || kb.description || ''}</p>
<div className='text-muted small d-flex align-items-center gap-2'>
<span className='d-flex align-items-center gap-1'>
<SvgIcon className='file' />
{kb.document_count} 文档
</span>
<span className='d-flex align-items-center gap-1'>
<SvgIcon className='clock' />
{new Date(kb.create_time).toLocaleDateString()}
</span>
</div>
</div>
</div>
</div>
))}
</div>
)}
))
) : (
<div className='col-12'>
<div className='alert alert-warning'>暂无可访问的知识库请先申请知识库访问权限</div>
</div>
)}
</div>
</div>
);
}

View File

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { showNotification } from '../../../store/notification.slice';
import { fetchKnowledgeBases } from '../../../store/knowledgeBase/knowledgeBase.thunks';
import SvgIcon from '../../../components/SvgIcon';
import DatasetTab from './DatasetTab';
import SettingsTab from './SettingsTab';
@ -13,8 +14,16 @@ export default function KnowledgeBaseDetail() {
const [activeTab, setActiveTab] = useState(tab === 'settings' ? 'settings' : 'datasets');
// Get knowledge base details from Redux store
const { items: knowledgeBases } = useSelector((state) => state.knowledgeBase.list);
const knowledgeBase = knowledgeBases.find((kb) => kb.id === id);
const { data, status } = useSelector((state) => state.knowledgeBase.list);
const knowledgeBase = data?.items?.find((kb) => kb.id === id);
const isLoading = status === 'loading';
// Fetch knowledge bases if not available
useEffect(() => {
if (!data?.items?.length && status !== 'loading') {
dispatch(fetchKnowledgeBases());
}
}, [dispatch, data, status]);
// Update active tab when URL changes
useEffect(() => {
@ -25,7 +34,7 @@ export default function KnowledgeBaseDetail() {
// If knowledge base not found in Redux store, show notification and redirect
useEffect(() => {
if (!knowledgeBase && knowledgeBases.length > 0) {
if (!knowledgeBase && data?.items?.length > 0 && !isLoading) {
dispatch(
showNotification({
message: '未找到知识库,请返回知识库列表',
@ -34,7 +43,7 @@ export default function KnowledgeBaseDetail() {
);
navigate('/knowledge-base');
}
}, [knowledgeBase, knowledgeBases, dispatch, navigate]);
}, [knowledgeBase, data, isLoading, dispatch, navigate]);
// Handle tab change
const handleTabChange = (tab) => {
@ -43,7 +52,7 @@ export default function KnowledgeBaseDetail() {
};
// Show loading state if knowledge base not loaded yet
if (!knowledgeBase) {
if (isLoading || !knowledgeBase) {
return (
<div className='container-fluid px-4 py-5 text-center'>
<div className='spinner-border' role='status'>
@ -61,7 +70,7 @@ export default function KnowledgeBaseDetail() {
<div className='py-4'>
<div className='h4 mb-3 text-center'>{knowledgeBase.name}</div>
<p className='text-center text-muted small mb-4'>
{knowledgeBase.desc || knowledgeBase.description || ''}
{knowledgeBase.desc || ''}
</p>
<hr />

View File

@ -5,7 +5,7 @@ import KnowledgeCard from './KnowledgeCard';
* 知识库列表组件
*/
const KnowledgeBaseList = ({ knowledgeBases, isSearching, onCardClick, onRequestAccess, onDelete }) => {
if (knowledgeBases.length === 0) {
if (!knowledgeBases?.length) {
return (
<div className='alert alert-warning'>
{isSearching ? '没有找到匹配的知识库' : '暂无知识库,请创建新的知识库'}
@ -19,15 +19,18 @@ const KnowledgeBaseList = ({ knowledgeBases, isSearching, onCardClick, onRequest
<React.Fragment key={item.id}>
<KnowledgeCard
id={item.id}
title={item.name}
description={item.description || item.desc || ''}
documents={item.document_count || 0}
date={new Date(item.create_time || item.created_at).toLocaleDateString()}
title={item.highlighted_name || item.name}
description={item.desc || ''}
documents={item.document_count}
date={new Date(item.create_time).toLocaleDateString()}
permissions={item.permissions}
access={item.permissions?.can_edit ? 'full' : item.permissions?.can_read ? 'read' : 'none'}
onClick={() => onCardClick(item.id, item.permissions)}
onRequestAccess={onRequestAccess}
onDelete={(e) => onDelete(e, item.id)}
type={item.type}
department={item.department}
group={item.group}
/>
</React.Fragment>
))}

View File

@ -13,6 +13,9 @@ export default function KnowledgeCard({
onClick,
onRequestAccess,
onDelete,
type,
department,
group,
}) {
const navigate = useNavigate();
@ -41,7 +44,7 @@ export default function KnowledgeCard({
return (
<div className='knowledge-card card shadow border-0 p-0 col' onClick={onClick}>
<div className='card-body'>
<h5 className='card-title'>{title}</h5>
<h5 className='card-title' dangerouslySetInnerHTML={{ __html: title }} />
{permissions && permissions.can_delete && (
<div className='hoverdown position-absolute end-0 top-0'>
<button type='button' className='detail-btn btn'>
@ -66,6 +69,13 @@ export default function KnowledgeCard({
{date}
</span>
</div>
{/* <div className='mt-2 d-flex flex-wrap gap-2'>
<span className='badge bg-secondary-subtle text-secondary'>
{type === 'private' ? '私有' : '公开'}
</span>
{department && <span className='badge bg-info-subtle text-info'>{department}</span>}
{group && <span className='badge bg-primary-subtle text-primary'>{group}</span>}
</div> */}
<div className='mt-3 d-flex justify-content-between align-items-end'>
{access === 'full' ? (
<span className='badge bg-success-subtle text-success d-flex align-items-center gap-1'>

View File

@ -10,136 +10,6 @@ import { resetApproveRejectStatus } from '../../../store/permissions/permissions
import './PendingRequests.css'; // CSS
import SvgIcon from '../../../components/SvgIcon';
//
const mockPendingRequests = [
{
id: 1,
applicant: {
name: '王五',
department: '达人组',
},
knowledge_base: {
name: '达人直播数据报告',
},
permissions: {
can_read: true,
can_edit: true,
can_delete: false,
},
reason: '需要查看和编辑直播数据报告',
created_at: '2024-01-07T10:30:00Z',
expires_at: null,
},
{
id: 2,
applicant: {
name: '赵六',
department: '直播组',
},
knowledge_base: {
name: '人力资源政策文件',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要了解最新的人力资源政策',
created_at: '2024-01-06T14:20:00Z',
expires_at: '2025-01-06T14:20:00Z',
},
{
id: 3,
applicant: {
name: '钱七',
department: '市场部',
},
knowledge_base: {
name: '市场分析报告',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要了解市场趋势',
created_at: '2024-01-05T09:15:00Z',
expires_at: '2024-07-05T09:15:00Z',
},
{
id: 4,
applicant: {
name: '孙八',
department: '技术部',
},
knowledge_base: {
name: '技术架构文档',
},
permissions: {
can_read: true,
can_edit: true,
can_delete: true,
},
reason: '需要进行技术架构更新',
created_at: '2024-01-04T16:45:00Z',
expires_at: null,
},
{
id: 5,
applicant: {
name: '周九',
department: '产品部',
},
knowledge_base: {
name: '产品规划文档',
},
permissions: {
can_read: true,
can_edit: true,
can_delete: false,
},
reason: '需要参与产品规划讨论',
created_at: '2024-01-03T11:30:00Z',
expires_at: '2024-12-31T23:59:59Z',
},
{
id: 6,
applicant: {
name: '吴十',
department: '设计部',
},
knowledge_base: {
name: '设计规范文档',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要参考设计规范',
created_at: '2024-01-02T14:20:00Z',
expires_at: '2024-06-30T23:59:59Z',
},
{
id: 7,
applicant: {
name: '郑十一',
department: '财务部',
},
knowledge_base: {
name: '财务报表',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要查看财务数据',
created_at: '2024-01-01T09:00:00Z',
expires_at: null,
},
];
//
const PAGE_SIZE = 5;
@ -173,31 +43,16 @@ export default function PendingRequests() {
const fetchData = async () => {
try {
setFetchStatus('loading');
// API
const result = await dispatch(fetchPermissionsThunk());
// API
if (result && result.payload && result.payload.length > 0) {
// 使API
if (result.payload) {
setPendingRequests(result.payload);
setTotalPages(Math.ceil(result.payload.length / PAGE_SIZE));
console.log('使用API返回的待处理申请数据');
} else {
// API使
console.log('API返回的待处理申请数据为空使用模拟数据');
setPendingRequests(mockPendingRequests);
setTotalPages(Math.ceil(mockPendingRequests.length / PAGE_SIZE));
}
setFetchStatus('succeeded');
} catch (error) {
console.error('获取待处理申请失败:', error);
setFetchError('获取待处理申请失败');
console.error('获取待处理申请列表失败:', error);
setFetchError(error.message || '获取待处理申请列表失败');
setFetchStatus('failed');
// API使
console.log('API请求失败使用模拟数据作为后备');
setPendingRequests(mockPendingRequests);
setTotalPages(Math.ceil(mockPendingRequests.length / PAGE_SIZE));
}
};
@ -318,7 +173,7 @@ export default function PendingRequests() {
const getCurrentPageData = () => {
const startIndex = (currentPage - 1) * PAGE_SIZE;
const endIndex = startIndex + PAGE_SIZE;
return pendingRequests.slice(startIndex, endIndex);
return Array.isArray(pendingRequests) ? pendingRequests.slice(startIndex, endIndex) : [];
};
//
@ -401,14 +256,13 @@ export default function PendingRequests() {
<div key={request.id} className='pending-request-item' onClick={() => handleRowClick(request)}>
<div className='request-header'>
<div className='user-info'>
<h6 className='mb-0'>{request.applicant.name}</h6>
<p className='department'>{request.applicant.department}</p>
<h6 className='mb-0'>{request.applicant}</h6>
</div>
<div className='request-date'>{new Date(request.created_at).toLocaleDateString()}</div>
</div>
<div className='request-content'>
<p className='mb-2'>申请访问{request.knowledge_base.name}</p>
<p className='mb-2'>申请访问{request.knowledge_base}</p>
{request.permissions.can_edit ? (
<span
@ -477,15 +331,10 @@ export default function PendingRequests() {
<h6 className='text-muted mb-2'>申请人信息</h6>
<div className='d-flex align-items-center mb-3'>
<div className='avatar-placeholder me-3 bg-dark'>
{(selectedRequest.applicant.name || selectedRequest.applicant).charAt(0)}
{selectedRequest.applicant.charAt(0)}
</div>
<div>
<h5 className='mb-1'>
{selectedRequest.applicant.name || selectedRequest.applicant}
</h5>
<p className='text-muted mb-0'>
{selectedRequest.applicant.department || '无归属部门'}
</p>
<h5 className='mb-1'>{selectedRequest.applicant}</h5>
</div>
</div>
</div>
@ -493,8 +342,7 @@ export default function PendingRequests() {
<div className='mb-4'>
<h6 className='text-muted mb-2'>知识库信息</h6>
<p className='mb-1'>
<strong>名称</strong>{' '}
{selectedRequest.knowledge_base.name || selectedRequest.knowledge_base}
<strong>ID</strong> {selectedRequest.knowledge_base}
</p>
</div>

View File

@ -295,235 +295,230 @@ const mockDeleteChat = (id) => {
// 模拟聊天消息数据
const chatMessages = {};
// 模拟待处理权限申请
// 权限申请列表的 mock 数据
const mockPendingRequests = [
{
id: 1,
applicant: {
name: '王五',
department: '达人组',
},
knowledge_base: {
name: '达人直播数据报告',
},
knowledge_base: 'f13c4bdb-eb03-4ce2-b83c-30917351fb72',
applicant: 'f2799611-7a3d-436d-b3fa-3789bdd877e2',
permissions: {
can_edit: false,
can_read: true,
can_edit: true,
can_delete: false,
},
reason: '需要查看和编辑直播数据报告',
created_at: '2024-01-07T10:30:00Z',
expires_at: null,
status: 'pending',
reason: '需要访问知识库进行学习',
response_message: null,
expires_at: '2025-03-19T00:17:43.781000Z',
created_at: '2025-03-12T00:17:44.044351Z',
updated_at: '2025-03-12T00:17:44.044369Z',
},
{
id: 2,
applicant: {
name: '赵六',
department: '直播组',
},
knowledge_base: {
name: '人力资源政策文件',
},
knowledge_base: 'f13c4bdb-eb03-4ce2-b83c-30917351fb73',
applicant: 'f2799611-7a3d-436d-b3fa-3789bdd877e3',
permissions: {
can_edit: true,
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要了解最新的人力资源政策',
created_at: '2024-01-06T14:20:00Z',
expires_at: '2025-01-06T14:20:00Z',
status: 'pending',
reason: '需要编辑和更新文档',
response_message: null,
expires_at: '2025-03-20T00:17:43.781000Z',
created_at: '2025-03-12T00:17:44.044351Z',
updated_at: '2025-03-12T00:17:44.044369Z',
},
];
// 用户权限列表的 mock 数据
const mockUserPermissions = [
{
id: 3,
applicant: {
name: '钱七',
department: '市场部',
id: 'perm-001',
user: {
id: 'user-001',
username: 'johndoe',
name: 'John Doe',
email: 'john@example.com',
department: '研发部',
group: '前端开发组',
},
knowledge_base: {
name: '市场分析报告',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要了解市场趋势',
created_at: '2024-01-05T09:15:00Z',
expires_at: '2024-07-05T09:15:00Z',
},
{
id: 4,
applicant: {
name: '孙八',
department: '技术部',
},
knowledge_base: {
name: '技术架构文档',
id: 'kb-001',
name: 'Frontend Development Guide',
},
permissions: {
can_read: true,
can_edit: true,
can_delete: true,
can_manage: true,
},
granted_at: '2024-01-15T10:00:00Z',
granted_by: {
id: 'user-admin',
username: 'admin',
name: 'System Admin',
},
reason: '需要进行技术架构更新',
created_at: '2024-01-04T16:45:00Z',
expires_at: null,
},
{
id: 5,
applicant: {
name: '周九',
department: '产品部',
id: 'perm-002',
user: {
id: 'user-002',
username: 'janedoe',
name: 'Jane Doe',
email: 'jane@example.com',
department: '研发部',
group: '前端开发组',
},
knowledge_base: {
name: '产品规划文档',
id: 'kb-001',
name: 'Frontend Development Guide',
},
permissions: {
can_read: true,
can_edit: true,
can_delete: false,
can_manage: false,
},
granted_at: '2024-01-20T14:30:00Z',
granted_by: {
id: 'user-001',
username: 'johndoe',
name: 'John Doe',
},
reason: '需要参与产品规划讨论',
created_at: '2024-01-03T11:30:00Z',
expires_at: '2024-12-31T23:59:59Z',
},
{
id: 6,
applicant: {
name: '吴十',
department: '设计部',
id: 'perm-003',
user: {
id: 'user-003',
username: 'alexsmith',
name: 'Alex Smith',
email: 'alex@example.com',
department: '研发部',
group: '后端开发组',
},
knowledge_base: {
name: '设计规范文档',
id: 'kb-001',
name: 'Frontend Development Guide',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
can_manage: false,
},
reason: '需要参考设计规范',
created_at: '2024-01-02T14:20:00Z',
expires_at: '2024-06-30T23:59:59Z',
},
{
id: 7,
applicant: {
name: '郑十一',
department: '财务部',
granted_at: '2024-02-01T09:15:00Z',
granted_by: {
id: 'user-001',
username: 'johndoe',
name: 'John Doe',
},
knowledge_base: {
name: '财务报表',
},
permissions: {
can_read: true,
can_edit: false,
can_delete: false,
},
reason: '需要查看财务数据',
created_at: '2024-01-01T09:00:00Z',
expires_at: null,
},
];
// 模拟用户权限详情
const mockUserPermissions = {
'user-001': [
{
knowledge_base: {
id: 'kb-001',
name: '达人直播数据报告',
department: '达人组',
// Mock API handlers for permissions
const mockPermissionApi = {
// 获取待处理的权限申请列表
getPendingRequests: () => {
return {
code: 200,
message: 'success',
data: {
items: mockPendingRequests,
total: mockPendingRequests.length,
},
permission: {
can_read: true,
can_edit: true,
can_admin: false,
};
},
// 获取用户权限列表
getUserPermissions: (knowledgeBaseId) => {
const permissions = mockUserPermissions.filter((perm) => perm.knowledge_base.id === knowledgeBaseId);
return {
code: 200,
message: 'success',
data: {
items: permissions,
total: permissions.length,
},
last_access_time: '2024-03-10T14:30:00Z',
},
{
knowledge_base: {
id: 'kb-002',
name: '人力资源政策文件',
department: '人力资源组',
},
permission: {
can_read: true,
can_edit: false,
can_admin: false,
},
last_access_time: '2024-03-08T09:15:00Z',
},
{
knowledge_base: {
id: 'kb-003',
name: '市场分析报告',
department: '市场部',
},
permission: {
can_read: true,
can_edit: false,
can_admin: false,
},
last_access_time: null,
},
],
'user-002': [
{
knowledge_base: {
id: 'kb-001',
name: '达人直播数据报告',
department: '达人组',
},
permission: {
can_read: true,
can_edit: false,
can_admin: false,
},
last_access_time: '2024-03-05T10:20:00Z',
},
{
knowledge_base: {
id: 'kb-004',
name: '产品规划文档',
department: '产品部',
},
permission: {
can_read: true,
can_edit: true,
can_admin: true,
},
last_access_time: '2024-03-15T11:20:00Z',
},
],
'user-003': [
{
knowledge_base: {
id: 'kb-003',
name: '市场分析报告',
department: '市场部',
},
permission: {
can_read: true,
can_edit: true,
can_admin: false,
},
last_access_time: '2024-03-12T15:40:00Z',
},
{
knowledge_base: {
id: 'kb-005',
name: 'UI/UX设计指南',
department: '设计部',
},
permission: {
can_read: true,
can_edit: false,
can_admin: false,
},
last_access_time: '2024-03-01T09:10:00Z',
},
],
};
},
// 处理权限申请
handlePermissionRequest: (requestId, action) => {
const request = mockPendingRequests.find((req) => req.id === requestId);
if (!request) {
return {
code: 404,
message: 'Permission request not found',
};
}
request.status = action === 'approve' ? 'approved' : 'rejected';
if (action === 'approve') {
// 如果批准,添加新的权限记录
const newPermission = {
id: `perm-${Date.now()}`,
user: request.user,
knowledge_base: request.knowledge_base,
permissions: {
can_read: true,
can_edit: request.request_type === 'edit',
can_delete: false,
can_manage: false,
},
granted_at: new Date().toISOString(),
granted_by: mockCurrentUser,
};
mockUserPermissions.push(newPermission);
}
return {
code: 200,
message: 'success',
data: request,
};
},
// 更新用户权限
updateUserPermission: (permissionId, permissions) => {
const permission = mockUserPermissions.find((perm) => perm.id === permissionId);
if (!permission) {
return {
code: 404,
message: 'Permission not found',
};
}
permission.permissions = {
...permission.permissions,
...permissions,
};
return {
code: 200,
message: 'success',
data: permission,
};
},
// 删除用户权限
deleteUserPermission: (permissionId) => {
const index = mockUserPermissions.findIndex((perm) => perm.id === permissionId);
if (index === -1) {
return {
code: 404,
message: 'Permission not found',
};
}
mockUserPermissions.splice(index, 1);
return {
code: 200,
message: 'success',
};
},
};
// Mock API functions
@ -536,13 +531,7 @@ export const mockGet = async (url, config = {}) => {
// Get current user
if (url === '/users/me/') {
return {
data: {
code: 200,
message: 'success',
data: {
user: mockUsers[0], // 默认返回第一个用户
},
},
user: mockUsers[0], // 默认返回第一个用户
};
}
@ -550,7 +539,7 @@ export const mockGet = async (url, config = {}) => {
if (url === '/knowledge-bases/') {
const params = config.params || { page: 1, page_size: 10 };
const result = paginate(knowledgeBases, params.page_size, params.page);
return {
data: {
code: 200,
@ -670,7 +659,8 @@ export const mockGet = async (url, config = {}) => {
code: 200,
message: 'success',
data: {
pending_requests: mockPendingRequests,
items: mockPendingRequests,
total: mockPendingRequests.length,
},
},
};
@ -721,22 +711,18 @@ export const mockPost = async (url, data) => {
const token = `mock-jwt-token-${uuidv4()}`;
return {
code: 200,
message: '登录成功',
data: {
code: 200,
message: '登录成功',
data: {
token,
user: {
id: user.id,
username: user.username,
email: user.email,
name: user.name,
department: user.department,
group: user.group,
role: user.role,
avatar: user.avatar,
},
},
token,
id: user.id,
username: user.username,
email: user.email,
name: user.name,
department: user.department,
group: user.group,
role: user.role,
avatar: user.avatar,
},
};
}
@ -1003,3 +989,18 @@ export const mockDelete = async (url) => {
export const resetMockData = () => {
knowledgeBases = [...mockKnowledgeBases];
};
// 添加权限相关的 API 处理
export const mockApi = {
// ... existing api handlers ...
// 权限管理相关的 API
'GET /api/permissions/pending': () => mockPermissionApi.getPendingRequests(),
'GET /api/permissions/users/:knowledgeBaseId': (params) =>
mockPermissionApi.getUserPermissions(params.knowledgeBaseId),
'POST /api/permissions/handle/:requestId': (params, body) =>
mockPermissionApi.handlePermissionRequest(params.requestId, body.action),
'PUT /api/permissions/:permissionId': (params, body) =>
mockPermissionApi.updateUserPermission(params.permissionId, body.permissions),
'DELETE /api/permissions/:permissionId': (params) => mockPermissionApi.deleteUserPermission(params.permissionId),
};

View File

@ -11,6 +11,8 @@ export const loginThunk = createAsyncThunk(
async ({ username, password }, { rejectWithValue, dispatch }) => {
try {
const { message, data } = await post('/auth/login/', { username, password });
console.log('data', data);
if (!data) {
throw new Error(message || 'Something went wrong');
}

View File

@ -11,20 +11,24 @@ import {
const initialState = {
// List state
list: {
items: [],
total: 0,
page: 1,
page_size: 10,
data: {
items: [],
total: 0,
page: 1,
page_size: 10,
},
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
},
// Search state
search: {
items: [],
total: 0,
page: 1,
page_size: 10,
keyword: '',
data: {
items: [],
total: 0,
page: 1,
page_size: 10,
keyword: '',
},
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
},
@ -62,11 +66,13 @@ const knowledgeBaseSlice = createSlice({
},
resetSearchState: (state) => {
state.search = {
items: [],
total: 0,
page: 1,
page_size: 10,
keyword: '',
data: {
items: [],
total: 0,
page: 1,
page_size: 10,
keyword: '',
},
status: 'idle',
error: null,
};
@ -93,15 +99,12 @@ const knowledgeBaseSlice = createSlice({
state.search.status = 'loading';
// Store the keyword for reference
if (action.meta.arg.keyword) {
state.search.keyword = action.meta.arg.keyword;
state.search.data.keyword = action.meta.arg.keyword;
}
})
.addCase(searchKnowledgeBases.fulfilled, (state, action) => {
state.search.status = 'succeeded';
state.search.items = action.payload.items;
state.search.total = action.payload.total;
state.search.page = action.payload.page;
state.search.page_size = action.payload.page_size;
state.search.data = action.payload;
state.search.error = null;
})
.addCase(searchKnowledgeBases.rejected, (state, action) => {
@ -146,14 +149,14 @@ const knowledgeBaseSlice = createSlice({
.addCase(updateKnowledgeBase.fulfilled, (state, action) => {
state.operations.status = 'succeeded';
// Update in list if present
const index = state.list.items.findIndex((item) => item.id === action.payload.id);
const index = state.list.data.items.findIndex((item) => item.id === action.payload.id);
if (index !== -1) {
state.list.items[index] = action.payload;
state.list.data.items[index] = action.payload;
}
// Update in search results if present
const searchIndex = state.search.items.findIndex((item) => item.id === action.payload.id);
const searchIndex = state.search.data.items.findIndex((item) => item.id === action.payload.id);
if (searchIndex !== -1) {
state.search.items[searchIndex] = action.payload;
state.search.data.items[searchIndex] = action.payload;
}
// Update current if it's the same knowledge base
if (state.current.data && state.current.data.id === action.payload.id) {
@ -174,9 +177,9 @@ const knowledgeBaseSlice = createSlice({
.addCase(deleteKnowledgeBase.fulfilled, (state, action) => {
state.operations.status = 'succeeded';
// Remove from list if present
state.list.items = state.list.items.filter((item) => item.id !== action.payload);
state.list.data.items = state.list.data.items.filter((item) => item.id !== action.payload);
// Remove from search results if present
state.search.items = state.search.items.filter((item) => item.id !== action.payload);
state.search.data.items = state.search.data.items.filter((item) => item.id !== action.payload);
// Reset current if it's the same knowledge base
if (state.current.data && state.current.data.id === action.payload) {
state.current.data = null;

View File

@ -35,10 +35,13 @@ export const searchKnowledgeBases = createAsyncThunk(
async ({ keyword, page = 1, page_size = 10 }, { rejectWithValue }) => {
try {
const response = await get('/knowledge-bases/search/', {
keyword,
page,
page_size,
params: { keyword, page, page_size },
});
// 处理新的返回格式
if (response.data && response.data.code === 200) {
return response.data.data;
}
return response.data;
} catch (error) {
return rejectWithValue(error.response?.data || 'Failed to search knowledge bases');
@ -75,6 +78,10 @@ export const getKnowledgeBaseById = createAsyncThunk(
async (id, { rejectWithValue }) => {
try {
const response = await get(`/knowledge-bases/${id}/`);
// 处理新的返回格式
if (response.data && response.data.code === 200) {
return response.data.data.knowledge_base;
}
return response.data;
} catch (error) {
return rejectWithValue(error.response?.data || 'Failed to get knowledge base details');

View File

@ -6,8 +6,11 @@ export const fetchPermissionsThunk = createAsyncThunk(
'permissions/fetchPermissions',
async (_, { rejectWithValue }) => {
try {
const response = await get('/permissions/');
return response || [];
const response = await get('/permissions/pending/');
if (response?.data?.code === 200) {
return response.data.data.items || [];
}
return rejectWithValue('获取权限申请列表失败');
} catch (error) {
console.error('获取权限申请列表失败:', error);
return rejectWithValue('获取权限申请列表失败');