feat(app): 管理端 Web 基座→暖记品牌迁移 + 日记管理页面
Phase 1 — 品牌替换:
- BRAND_DEFAULTS 回退值改为暖记品牌 (themes.ts)
- 登录页/侧边栏/底部回退文字 → 暖记 (Login, MainLayout)
- index.html title/meta/favicon → 暖记
- localStorage key → nuanji-theme, 默认主题 → warm
- 4 套主题色适配暖记设计系统 (珊瑚 #E07A5F / 蓝 / 深色 / 鼠尾草绿)
- 品牌信息通过系统设置配置,不硬编码
Phase 2 — 清理 HMS 模块:
- 删除 health/ai 页面 (~55+2)、API (~30+9)、组件、stores、hooks
- 重写 Home.tsx 为暖记 Dashboard
- 重写 NotificationPanel/MediaPicker 移除 health 依赖
- 清理 routeConfig 移除所有 health/ai 路由权限
Phase 3 — 暖记管理页面:
- API 层: api/diary/{types,journals,classes,topics,comments,stickers}.ts
- 班级管理: 班级列表+创建+成员查看+班级码复制 (ClassList)
- 日记审核: 日记列表+筛选+详情+老师点评 (JournalList)
- 主题管理: 班级选择+主题卡片+创建+过期标记 (TopicList)
- 贴纸管理: 贴纸包卡片+贴纸详情网格 (StickerPackList)
- 路由注册: /diary/classes, /diary/journals, /diary/topics, /diary/stickers
验证: tsc 0 error, vite build ✓, vitest 226/226 pass
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useState, useEffect, useMemo } from 'react';
|
||||
import { Layout, Avatar, Space, Dropdown, Tooltip, Spin, theme, Menu } from 'antd';
|
||||
import { Layout, Avatar, Space, Dropdown, Tooltip, Spin, theme, Menu, message } from 'antd';
|
||||
import type { MenuItemType, SubMenuType } from 'antd/es/menu/hooks/useItems';
|
||||
import {
|
||||
MenuFoldOutlined,
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
SearchOutlined,
|
||||
AppstoreOutlined,
|
||||
UserOutlined,
|
||||
RobotOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAppStore } from '../stores/app';
|
||||
@@ -19,7 +18,6 @@ import { getMenusForUser, type MenuInfo } from '../api/menus';
|
||||
import { getIcon } from '../utils/iconRegistry';
|
||||
import NotificationPanel from '../components/NotificationPanel';
|
||||
import ThemeSwitcher from '../components/ThemeSwitcher';
|
||||
import AiSidebar from '../components/ai/AiSidebar';
|
||||
|
||||
const { Header, Sider, Content, Footer } = Layout;
|
||||
|
||||
@@ -27,20 +25,9 @@ const { Header, Sider, Content, Footer } = Layout;
|
||||
// 1. 动态参数路由(:id/:id/edit)— 菜单表不会存储这些路径
|
||||
// 2. 无后端菜单记录的静态页面路由
|
||||
const routeTitleFallback: Record<string, string> = {
|
||||
// 动态参数路由
|
||||
'/health/patients/:id': '患者详情',
|
||||
'/health/consultations/:id': '咨询详情',
|
||||
'/health/articles/new': '新建文章',
|
||||
'/health/articles/:id/edit': '编辑文章',
|
||||
'/health/care-plans/:id': '护理计划详情',
|
||||
'/health/shifts/:id': '班次详情',
|
||||
'/health/ble-gateways/:id': '网关详情',
|
||||
// 无后端菜单的静态路由
|
||||
'/health/follow-up-records': '随访记录',
|
||||
'/health/article-categories': '分类管理',
|
||||
'/health/article-tags': '标签管理',
|
||||
'/health/schedules': '排班管理',
|
||||
'/health/appointments': '预约管理',
|
||||
// 暖记管理端 — 动态参数路由
|
||||
'/diary/journals/:id': '日记详情',
|
||||
'/diary/classes/:id': '班级详情',
|
||||
};
|
||||
|
||||
function getTitleFromMenus(path: string, menus: MenuInfo[]): string | undefined {
|
||||
@@ -127,7 +114,6 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
// 动态菜单状态
|
||||
const [dynamicMenus, setDynamicMenus] = useState<MenuInfo[]>([]);
|
||||
const [menuLoading, setMenuLoading] = useState(true);
|
||||
const [aiSidebarOpen, setAiSidebarOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -252,9 +238,9 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
>
|
||||
{/* Logo 区域 */}
|
||||
<div className="erp-sidebar-logo" onClick={() => navigate('/')}>
|
||||
<div className="erp-sidebar-logo-icon">H</div>
|
||||
<div className="erp-sidebar-logo-icon">N</div>
|
||||
{!sidebarCollapsed && (
|
||||
<span className="erp-sidebar-logo-text">{themeConfig?.brand_name || 'HMS 健康'}</span>
|
||||
<span className="erp-sidebar-logo-text">{themeConfig?.brand_name || '暖记 Nuanji'}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -333,14 +319,14 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
|
||||
{/* 底部 */}
|
||||
<Footer className="erp-footer">
|
||||
{themeConfig?.brand_copyright || 'HMS 健康管理平台'}
|
||||
{themeConfig?.brand_copyright || '© 暖记 Nuanji'}
|
||||
</Footer>
|
||||
</Layout>
|
||||
|
||||
{/* AI 助手浮动按钮 + 侧边栏 */}
|
||||
<Tooltip title="AI 健康助手" placement="left">
|
||||
{/* AI 助手浮动按钮(暖记未来功能) */}
|
||||
<Tooltip title="AI 助手" placement="left">
|
||||
<div
|
||||
onClick={() => setAiSidebarOpen(true)}
|
||||
onClick={() => message.info('AI 助手功能即将上线')}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
right: 24,
|
||||
@@ -348,28 +334,27 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: '50%',
|
||||
background: 'linear-gradient(135deg, #1677ff 0%, #722ed1 100%)',
|
||||
background: 'linear-gradient(135deg, #E07A5F 0%, #81B29A 100%)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0 4px 12px rgba(22, 119, 255, 0.4)',
|
||||
boxShadow: '0 4px 12px rgba(224, 122, 95, 0.4)',
|
||||
zIndex: 1000,
|
||||
transition: 'transform 0.2s, box-shadow 0.2s',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1.1)';
|
||||
e.currentTarget.style.boxShadow = '0 6px 16px rgba(22, 119, 255, 0.6)';
|
||||
e.currentTarget.style.boxShadow = '0 6px 16px rgba(224, 122, 95, 0.6)';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1)';
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(22, 119, 255, 0.4)';
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(224, 122, 95, 0.4)';
|
||||
}}
|
||||
>
|
||||
<RobotOutlined style={{ color: '#fff', fontSize: 22 }} />
|
||||
<span style={{ color: '#fff', fontSize: 22 }}>🤖</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<AiSidebar open={aiSidebarOpen} onClose={() => setAiSidebarOpen(false)} />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user