diff --git a/web/src/api/documentRevision.ts b/web/src/api/documentRevision.ts index bad7f6ea..53aca539 100644 --- a/web/src/api/documentRevision.ts +++ b/web/src/api/documentRevision.ts @@ -99,10 +99,3 @@ export function decideAgentCandidate( { method: 'POST', body: JSON.stringify(body) }, ); } - -export function patchNotification(id: number, status: 'read' | 'acted' | 'dismissed') { - return api<{ ok: boolean }>(`/api/v1/notifications/${id}`, { - method: 'PATCH', - body: JSON.stringify({ status }), - }); -} diff --git a/web/src/api/projectWorkflow.ts b/web/src/api/projectWorkflow.ts index 433ca7ae..721d9315 100644 --- a/web/src/api/projectWorkflow.ts +++ b/web/src/api/projectWorkflow.ts @@ -4,8 +4,6 @@ import { WorkflowDefinition, DocumentLineage, CreateProjectInput, - ActivateWorkflowInput, - ActivateWorkflowResult, } from '../types/projectWorkflow'; import { getBearerAuthHeaders } from './authHeaders'; @@ -68,29 +66,6 @@ export async function fetchProjectWorkflows(projectId: number): Promise { - const res = await fetch(`${API_BASE}/api/v1/projects/${projectId}/active-workflow`, { - method: 'PUT', - headers: getBearerAuthHeaders('application/json'), - body: JSON.stringify(input), - }); - if (!res.ok) { - const text = await res.text(); - let msg = text; - try { - const parsed = JSON.parse(text); - if (parsed.message) msg = parsed.message; - } catch { - // ignore - } - throw new Error(msg || `切换激活工作流失败: ${res.status}`); - } - return res.json(); -} - export async function fetchWorkflowCatalog(): Promise { const res = await fetch(`${API_BASE}/api/v1/workflows`, { headers: getBearerAuthHeaders(), diff --git a/web/src/components/notifications/DocumentNotificationList.tsx b/web/src/components/notifications/DocumentNotificationList.tsx deleted file mode 100644 index 7bdf6e42..00000000 --- a/web/src/components/notifications/DocumentNotificationList.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { patchNotification } from '../../api/documentRevision'; - -export type DocNotificationItem = { - id: number; - type: string; - title: string; - body: string; - status: string; - created_at?: string; -}; - -export function DocumentNotificationList(props: { - items: DocNotificationItem[]; - onChanged?: () => void; -}) { - return ( - - ); -} diff --git a/web/src/components/user/SettingsModal.tsx b/web/src/components/user/SettingsModal.tsx deleted file mode 100644 index bd085967..00000000 --- a/web/src/components/user/SettingsModal.tsx +++ /dev/null @@ -1,826 +0,0 @@ -import { FC, useState, useEffect } from 'react'; -import { - X, - Settings, - Shield, - Plus, - Trash2, - Check, - Users, - Key, - Info, - CheckCircle2, -} from 'lucide-react'; -import { RoleDefinition, PermissionKey } from '../../types/rbac'; -import { UserProfile } from '../../types/docgov'; - -export interface SettingsModalProps { - isOpen: boolean; - onClose: () => void; - roles: RoleDefinition[]; - onSaveRoles: (roles: RoleDefinition[]) => void; - users: UserProfile[]; - currentUser: UserProfile; - onUpdateUserRole: (userId: number, roleId: string) => void; -} - -const ALL_PERMISSION_ITEMS: { - key: PermissionKey; - label: string; - desc: string; - category: '项目与流程' | '权限与安全' | '文档与导出'; -}[] = [ - { - key: 'project:create', - label: '创建新项目/立项', - desc: '允许在左侧栏发起新研发项目与阶段模板克隆', - category: '项目与流程', - }, - { - key: 'project:assign_lead', - label: '设置/变更文档责任人', - desc: '允许指派或变更文档的法定编制人、审核人与批准人', - category: '项目与流程', - }, - { - key: 'perm:manage', - label: '系统权限与角色管理', - desc: '超级管理特权,可增删改角色库并配置全局权限', - category: '权限与安全', - }, - { - key: 'workflow:config_visibility', - label: '配置工作流默认可见性', - desc: '允许配置整个研发阶段或工作流的基线可见性规则', - category: '权限与安全', - }, - { - key: 'doc:override_visibility', - label: '配置单文档可见性覆盖', - desc: '允许对敏感或涉密文档设置独立可见性(覆盖工作流默认)', - category: '权限与安全', - }, - { - key: 'workflow:view_all', - label: '查阅常规受控工作流文档', - desc: '默认是否可浏览项目内的受控工作流核心交付物', - category: '文档与导出', - }, - { - key: 'doc:export', - label: '导出受控成果物 (DOCX/XLSX)', - desc: '允许将交付物编译导出为合规 DOCX 报告或测算 XLSX 表格', - category: '文档与导出', - }, -]; - -export const SettingsModal: FC = ({ - isOpen, - onClose, - roles, - onSaveRoles, - users, - currentUser, - onUpdateUserRole, -}) => { - const [activeTab, setActiveTab] = useState<'roles' | 'users'>('roles'); - const [selectedRoleId, setSelectedRoleId] = useState(roles[0]?.id || 'role-admin'); - const [editRoles, setEditRoles] = useState(roles); - const [isCreatingRole, setIsCreatingRole] = useState(false); - const [newRoleName, setNewRoleName] = useState(''); - const [newRoleCode, setNewRoleCode] = useState(''); - const [newRoleDesc, setNewRoleDesc] = useState(''); - const [saveSuccessTip, setSaveSuccessTip] = useState(false); - - // 同步外部 roles - useEffect(() => { - setEditRoles(roles); - if (!roles.some((r) => r.id === selectedRoleId) && roles.length > 0) { - setSelectedRoleId(roles[0].id); - } - }, [roles]); - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape' && isOpen) { - onClose(); - } - }; - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [isOpen, onClose]); - - if (!isOpen) return null; - - const currentSelectedRole = - editRoles.find((r) => r.id === selectedRoleId) || editRoles[0]; - - const handleTogglePermission = (permKey: PermissionKey) => { - if (!currentSelectedRole || currentSelectedRole.isSystem) return; - - const exists = currentSelectedRole.permissions.includes(permKey); - const updatedPermissions = exists - ? currentSelectedRole.permissions.filter((k) => k !== permKey) - : [...currentSelectedRole.permissions, permKey]; - - const updatedRoles = editRoles.map((r) => - r.id === currentSelectedRole.id - ? { ...r, permissions: updatedPermissions, updatedAt: new Date().toLocaleString() } - : r - ); - - setEditRoles(updatedRoles); - onSaveRoles(updatedRoles); - showSavedNotification(); - }; - - const handleCreateRole = () => { - if (!newRoleName.trim() || !newRoleCode.trim()) return; - - const newId = `role-custom-${Date.now()}`; - const newRole: RoleDefinition = { - id: newId, - name: newRoleName.trim(), - code: newRoleCode.trim().toUpperCase(), - description: newRoleDesc.trim() || '自定义业务角色', - isSystem: false, - permissions: ['workflow:view_all', 'doc:export'], - createdAt: new Date().toLocaleString(), - updatedAt: new Date().toLocaleString(), - }; - - const nextRoles = [...editRoles, newRole]; - setEditRoles(nextRoles); - onSaveRoles(nextRoles); - setSelectedRoleId(newId); - setIsCreatingRole(false); - setNewRoleName(''); - setNewRoleCode(''); - setNewRoleDesc(''); - showSavedNotification(); - }; - - const handleDeleteRole = (roleId: string) => { - const roleToDelete = editRoles.find((r) => r.id === roleId); - if (!roleToDelete || roleToDelete.isSystem) return; - - if (window.confirm(`确定要删除角色【${roleToDelete.name}】吗?删除后该角色用户将自动退回普通工程师。`)) { - const nextRoles = editRoles.filter((r) => r.id !== roleId); - setEditRoles(nextRoles); - onSaveRoles(nextRoles); - setSelectedRoleId(nextRoles[0]?.id || 'role-admin'); - showSavedNotification(); - } - }; - - const showSavedNotification = () => { - setSaveSuccessTip(true); - setTimeout(() => setSaveSuccessTip(false), 2000); - }; - - return ( -
{ - if (e.target === e.currentTarget) onClose(); - }} - > -
- {/* 顶部标题栏 */} -
-
-
- -
-
-
- 系统权限与角色管理控制台 -
-
- 全局动态角色 CRUD · 权限配置矩阵 · 人员角色委派与实测切换 -
-
-
- -
- {saveSuccessTip && ( - - 已实时自动保存 - - )} - -
-
- - {/* 标签栏导航 */} -
- - - -
- - {/* 内容区 */} -
- {activeTab === 'roles' ? ( -
- {/* 左侧角色列表 */} -
-
- - 系统全局角色 ({editRoles.length}) - - -
- - {/* 新增角色输入面板 */} - {isCreatingRole && ( -
- setNewRoleName(e.target.value)} - style={{ - padding: '6px 8px', - fontSize: '12px', - borderRadius: '4px', - border: '1px solid var(--border)', - background: 'var(--bg-panel)', - color: 'var(--text-strong)', - }} - /> - setNewRoleCode(e.target.value)} - style={{ - padding: '6px 8px', - fontSize: '12px', - borderRadius: '4px', - border: '1px solid var(--border)', - background: 'var(--bg-panel)', - color: 'var(--text-strong)', - }} - /> -
- - -
-
- )} - - {/* 角色卡片列表 */} -
- {editRoles.map((role) => { - const isSelected = role.id === selectedRoleId; - return ( -
setSelectedRoleId(role.id)} - style={{ - padding: '10px 12px', - borderRadius: '6px', - marginBottom: '6px', - cursor: 'pointer', - backgroundColor: isSelected ? 'var(--bg-active, #1F2837)' : 'transparent', - border: isSelected ? '1px solid var(--brand-500)' : '1px solid transparent', - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - transition: 'all 0.15s ease', - }} - > -
-
- - {role.name} - - {role.isSystem && ( - - SYSTEM - - )} -
-
- 代码: {role.code} · {role.permissions.length} 项权限 -
-
- - {!role.isSystem && ( - - )} -
- ); - })} -
-
- - {/* 右侧权限配置矩阵 */} -
- {currentSelectedRole && ( - <> -
-
-
- {currentSelectedRole.name} ({currentSelectedRole.code}) -
- {currentSelectedRole.isSystem && ( - - 🛡️ 系统内置超级管理员,默认具备所有权限且防误删 - - )} -
-
- {currentSelectedRole.description} -
-
- -
-
- - 默认赋予的原子功能权限项 (勾选即刻生效): -
- -
- {ALL_PERMISSION_ITEMS.map((item) => { - const isChecked = - currentSelectedRole.isSystem || - currentSelectedRole.permissions.includes(item.key); - const isDisabled = currentSelectedRole.isSystem; - - return ( -
{ - if (!isDisabled) handleTogglePermission(item.key); - }} - style={{ - padding: '10px 14px', - borderRadius: '8px', - border: '1px solid var(--border)', - backgroundColor: isChecked ? 'rgba(79, 107, 255, 0.05)' : 'var(--bg-subtle)', - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - cursor: isDisabled ? 'default' : 'pointer', - transition: 'all 0.15s ease', - }} - > -
-
- - {item.label} - - - {item.category} - -
-
- {item.key} · {item.desc} -
-
- -
- {isChecked && } -
-
- ); - })} -
-
- - )} -
-
- ) : ( - /* Tab 2: 人员角色委派与当前登录人切换 */ -
-
- - - 在此为研发中心人员委派全局角色。支持一键将当前会话登录人切换为不同角色身份,快速实测“新建项目”、“指派负责人”及“单文档可见性覆盖”的受控拦截效果。 - -
- -
- {users.map((u) => { - const isCurrent = u.id === currentUser.id; - const userRoleId = u.roleId || 'role-engineer'; - - return ( -
-
-
- {u.name.slice(0, 1)} -
-
-
- - {u.name} - - - 工号: {u.employeeId} - - {isCurrent && ( - - 当前登录身份 - - )} -
-
- {u.department} · {u.position} -
-
-
- -
- {/* 角色委派下拉框 */} -
- 委派全局角色: - -
- - {isCurrent ? ( - - ✓ 当前登录身份 - - ) : null} -
-
- ); - })} -
-
- )} -
- - {/* 底部按钮栏 */} -
- - 水木东方医疗器械智能协同工作台 · 全生命周期权限治理 - - -
-
-
- ); -}; diff --git a/web/src/components/workflow/WorkflowPrerequisiteBanner.tsx b/web/src/components/workflow/WorkflowPrerequisiteBanner.tsx deleted file mode 100644 index ffe842ef..00000000 --- a/web/src/components/workflow/WorkflowPrerequisiteBanner.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { AlertTriangle, ArrowRight } from 'lucide-react'; - -interface WorkflowPrerequisiteBannerProps { - workflowKey: string; - hasApprovedSystemReqs: boolean; - onSwitchToProductManager: () => void; -} - -export const WorkflowPrerequisiteBanner: React.FC = ({ - workflowKey, - hasApprovedSystemReqs, - onSwitchToProductManager, -}) => { - if (workflowKey !== 'test_manager' || hasApprovedSystemReqs) { - return null; - } - - return ( -
-
- - - 前置依赖未就绪: - 智能测试经理工作流强依赖同项目已批准的《系统需求规格书 (RD-SR)》。请先由产品经理完成编制并经三级审批批准。 - -
- -
- ); -}; diff --git a/web/src/data/storage.ts b/web/src/data/storage.ts index 85babdf9..80b6aa18 100644 --- a/web/src/data/storage.ts +++ b/web/src/data/storage.ts @@ -137,11 +137,6 @@ export function loadMessages(): ChatMessage[] { return enrichMessagesWithCategories(result); } -/** @deprecated 全量 MESSAGES 持久化已废弃,请使用 saveSessionWindowCache */ -export function saveMessages(_messages: ChatMessage[]): void { - // no-op: 权威历史来自服务端窗口接口 -} - // ================================================================================== // P0: 会话窗口 LRU 缓存与输入草稿持久化 (替代原全量 MESSAGES 持久化) // ================================================================================== diff --git a/web/src/types/projectWorkflow.ts b/web/src/types/projectWorkflow.ts index c625b1cf..a807b34b 100644 --- a/web/src/types/projectWorkflow.ts +++ b/web/src/types/projectWorkflow.ts @@ -75,17 +75,3 @@ export interface CreateProjectInput { /** @deprecated Accepted by API for catalog checks only; create dialog must not send this. */ initial_workflow_key?: string; } - -export interface ActivateWorkflowInput { - workflow_key: string; - expected_version: number; - reason?: string; -} - -export interface ActivateWorkflowResult { - project_id: number; - active_workflow_key: string; - version: number; - changed_at: string; - changed_by: number; -}