feat(app): 管理端 Web 基座→暖记品牌迁移 + 日记管理页面
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

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:
iven
2026-06-02 12:16:44 +08:00
parent 0a9e5b1cb3
commit 78018a9a64
204 changed files with 2573 additions and 35241 deletions

View File

@@ -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>
);
}