import React, { useState, useEffect, useRef, Suspense } from 'react' import axios from 'axios' import { MoonStarsIcon, SunIcon, TuneIcon, CheckIcon, CloseIcon, SparkIcon, RefreshIcon, BuildingIcon } from './Icons' import { useAuth, hasPermission } from '../auth' import { useBranding } from '../branding' /* 这两块只有系统管理账号会看,按需加载:设置本身在外壳里是静态引入的, 跟着首屏一起下发等于给所有人多下载十几 KB。 */ const AIConfigSettings = React.lazy(() => import('./AIConfigSettings')) const OrgBrandingSettings = React.lazy(() => import('./OrgBrandingSettings')) /* 设置分类。带 permission 的项只对有该权限点的人显示 —— 智能配置里有接口地址与密钥,组织品牌是系统管理动作,都不该摆在业务员眼前。 数据导入导出已从设置里移出,做成独立页面「数据迁移」(侧栏 · 系统管理), 因为那里要放类别勾选和逐表明细,塞进这个 1000px 的模态框里看不清。 */ const CATEGORIES = [ { key: 'appearance', label: '外观与主题', icon: TuneIcon }, { key: 'organization', label: '组织与品牌', icon: BuildingIcon, permission: 'branding.manage' }, { key: 'ai-config', label: '智能配置', icon: SparkIcon, permission: 'ai.manage' }, { key: 'system-update', label: '系统与更新', icon: RefreshIcon } ] function Settings({ isOpen, onClose, theme, onThemeChange, reducedMotion, onReducedMotionChange }) { const { user } = useAuth() const [activeCategory, setActiveCategory] = useState('appearance') const visibleCategories = CATEGORIES.filter(item => !item.permission || hasPermission(user, item.permission)) /* 打开着设置时权限被收回(换了角色),当前分类可能已经不可见,退回第一项 */ const current = visibleCategories.some(item => item.key === activeCategory) ? activeCategory : visibleCategories[0]?.key const modalRef = useRef(null) useEffect(() => { if (!isOpen) return const previousFocus = document.activeElement const previousOverflow = document.body.style.overflow document.body.style.overflow = 'hidden' const modal = modalRef.current const focusable = () => [...modal.querySelectorAll('button, input, select, textarea, a[href], [tabindex="0"]')].filter(el => !el.disabled && el.getClientRects().length) modal.querySelector('.close-btn')?.focus() const onKeyDown = event => { if (event.key === 'Escape') { event.preventDefault(); onClose() } if (event.key === 'Tab') { const items = focusable() const first = items[0], last = items[items.length - 1] if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last?.focus() } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first?.focus() } } } modal.addEventListener('keydown', onKeyDown) return () => { document.body.style.overflow = previousOverflow modal.removeEventListener('keydown', onKeyDown) previousFocus?.focus() } }, [isOpen, onClose]) if (!isOpen) return null return (
e.stopPropagation()}>

正在载入…

}> {current === 'appearance' && } {current === 'organization' && } {current === 'ai-config' && } {current === 'system-update' && }
) } function AppearanceSettings({ theme, onThemeChange, reducedMotion, onReducedMotionChange }) { return (

外观与主题

选择适合工作环境的显示模式,设置会自动保存在本机。

显示偏好

清晰优先两种主题均保留高对比度文字与明确的状态标识。默认启用