diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 3ca859c..b2ed4e6 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -265,6 +265,166 @@ --erp-font-heading: 'Noto Sans SC', -apple-system, system-ui, sans-serif; } +/* --- Login Page Tokens --- */ +:root { + --login-gradient-start: #312E81; + --login-gradient-mid: #2563eb; + --login-gradient-end: #60a5fa; + --login-form-bg: #ffffff; + --login-form-text: #0f172a; + --login-form-text-secondary: #475569; + --login-input-icon-color: #94a3b8; +} + +[data-theme='warm'] { + --login-gradient-start: #6B3418; + --login-gradient-mid: #C4623A; + --login-gradient-end: #D4956A; + --login-form-bg: #FAF5F0; + --login-form-text: #2D2A26; + --login-form-text-secondary: #7A756E; + --login-input-icon-color: #A8A29E; +} + +[data-theme='dark'] { + --login-gradient-start: #0F172A; + --login-gradient-mid: #1E293B; + --login-gradient-end: #334155; + --login-form-bg: #1E293B; + --login-form-text: rgba(255,255,255,0.95); + --login-form-text-secondary: #94A3B8; + --login-input-icon-color: #64748B; +} + +[data-theme='emerald'] { + --login-gradient-start: #2D4A2F; + --login-gradient-mid: #5B7A5E; + --login-gradient-end: #8FB092; + --login-form-bg: #F4F7F4; + --login-form-text: #1A2E1A; + --login-form-text-secondary: #5A6E5A; + --login-input-icon-color: #8FA08F; +} + +/* --- Login Page Styles --- */ +.login-root { + display: flex; + min-height: 100vh; +} + +.login-brand-panel { + flex: 1; + background: linear-gradient(135deg, var(--login-gradient-start) 0%, var(--login-gradient-mid) 50%, var(--login-gradient-end) 100%); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 60px; + position: relative; + overflow: hidden; +} + +.login-brand-panel .deco-circle { + position: absolute; + border-radius: 50%; + background: rgba(255, 255, 255, 0.05); +} + +.login-brand-panel .brand-icon { + width: 64px; + height: 64px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.15); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 32px; + backdrop-filter: blur(8px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.login-brand-panel .brand-icon .anticon { + font-size: 32px; + color: #fff; +} + +.login-brand-panel .brand-title { + color: #fff; + font-size: 36px; + font-weight: 800; + margin: 0 0 16px; + letter-spacing: -1px; + line-height: 1.2; +} + +.login-brand-panel .brand-desc { + color: rgba(255, 255, 255, 0.7); + font-size: 16px; + line-height: 1.6; + margin: 0; +} + +.login-brand-panel .brand-sub-desc { + color: rgba(255, 255, 255, 0.5); + font-size: 14px; + line-height: 1.6; + margin-top: 8px; +} + +.login-brand-panel .feature-item-value { + color: rgba(255, 255, 255, 0.9); + font-size: 18px; + font-weight: 700; +} + +.login-brand-panel .feature-item-label { + color: rgba(255, 255, 255, 0.5); + font-size: 12px; + margin-top: 4px; +} + +.login-form-panel { + width: 480px; + display: flex; + flex-direction: column; + justify-content: center; + padding: 60px; + background: var(--login-form-bg); + position: relative; + transition: background var(--erp-transition-slow); +} + +.login-form-panel .form-title { + margin-bottom: 4px; + font-weight: 700; + font-size: 24px; + color: var(--login-form-text); +} + +.login-form-panel .form-subtitle { + font-size: 14px; + color: var(--login-form-text-secondary); +} + +.login-form-panel .form-footer { + margin-top: 32px; + text-align: center; + font-size: 12px; + color: var(--login-form-text-secondary); +} + +.login-theme-switcher { + position: absolute; + top: 20px; + right: 20px; + z-index: 10; +} + +.login-theme-switcher .erp-header-btn { + background: var(--login-form-bg); + border: 1px solid var(--erp-border); + color: var(--login-form-text-secondary); +} /* --- Global Reset & Base --- */ body { diff --git a/apps/web/src/pages/Home.tsx b/apps/web/src/pages/Home.tsx index ad73694..da6f621 100644 --- a/apps/web/src/pages/Home.tsx +++ b/apps/web/src/pages/Home.tsx @@ -1,100 +1,37 @@ -import { useEffect, useState, useCallback, useRef } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { Row, Col, Spin, Empty } from 'antd'; import { UserOutlined, + TeamOutlined, + CalendarOutlined, + HeartOutlined, + MedicineBoxOutlined, SafetyCertificateOutlined, - FileTextOutlined, + MessageOutlined, BellOutlined, - ThunderboltOutlined, - SettingOutlined, + AlertOutlined, + TrophyOutlined, + ShoppingOutlined, + FileTextOutlined, + RightOutlined, PartitionOutlined, ClockCircleOutlined, - ApartmentOutlined, CheckCircleOutlined, - RightOutlined, + ThunderboltOutlined, + SettingOutlined, + ApartmentOutlined, } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; -import client from '../api/client'; import { useThemeMode } from '../hooks/useThemeMode'; +import { useDashboardRole, type DashboardRole } from '../hooks/useDashboardRole'; import { useMessageStore } from '../stores/message'; import { listAuditLogs, type AuditLogItem } from '../api/auditLogs'; import { listPendingTasks, type TaskInfo } from '../api/workflowTasks'; +import { pointsApi, type PersonalStats } from '../api/health/points'; +import { useStatsData } from './health/StatisticsDashboard/useStatsData'; +import { useCountUp } from '../hooks/useCountUp'; -interface DashboardStats { - userCount: number; - roleCount: number; - processInstanceCount: number; - unreadMessages: number; -} - -interface StatCardConfig { - key: string; - title: string; - value: number; - icon: React.ReactNode; - gradient: string; - iconBg: string; - delay: string; - onClick?: () => void; -} - -function useCountUp(end: number, duration = 800) { - const [count, setCount] = useState(0); - const prevEnd = useRef(end); - - useEffect(() => { - if (end === prevEnd.current && count > 0) return; - prevEnd.current = end; - - if (end === 0) { setCount(0); return; } - - const startTime = performance.now(); - const startVal = 0; - - function tick(now: number) { - const elapsed = now - startTime; - const progress = Math.min(elapsed / duration, 1); - const eased = 1 - Math.pow(1 - progress, 3); - setCount(Math.round(startVal + (end - startVal) * eased)); - if (progress < 1) requestAnimationFrame(tick); - } - - requestAnimationFrame(tick); - }, [end, duration]); - - return count; -} - -function StatValue({ value, loading }: { value: number; loading: boolean }) { - const animatedValue = useCountUp(value); - if (loading) return ; - return {animatedValue.toLocaleString()}; -} - -const ACTION_LABELS: Record = { - create: '创建', created: '创建', update: '更新', updated: '更新', delete: '删除', deleted: '删除', - login: '登录', login_failed: '登录失败', user_login: '登录', 'user.login': '登录', - 'user.create': '创建', 'user.update': '更新', 'user.delete': '删除', - 'role.create': '创建', 'role.update': '更新', 'role.delete': '删除', - 'patient.create': '创建', 'patient.update': '更新', - 'appointment.create': '创建', 'appointment.update': '更新', -}; -const RESOURCE_LABELS: Record = { - user: '用户', role: '角色', permission: '权限', - organization: '组织', department: '部门', position: '岗位', - dictionary: '字典', menu: '菜单', setting: '设置', - process_definition: '流程定义', process_instance: '流程实例', - message: '消息', plugin: '插件', - patient: '患者', doctor: '医护', appointment: '预约', - follow_up_task: '随访', consultation_session: '咨询', - auth: '认证', -}; -const RESOURCE_ICONS: Record = { - user: , role: , - organization: , process_definition: , - process_instance: , message: , - patient: , doctor: , -}; +// --- Shared utilities --- function formatTimeAgo(dateStr: string): string { const diff = Date.now() - new Date(dateStr).getTime(); @@ -107,158 +44,174 @@ function formatTimeAgo(dateStr: string): string { return `${days} 天前`; } +const ACTION_LABELS: Record = { + create: '创建', created: '创建', update: '更新', updated: '更新', delete: '删除', deleted: '删除', + login: '登录', 'user.create': '创建', 'user.update': '更新', 'user.delete': '删除', + 'patient.create': '创建', 'patient.update': '更新', 'appointment.create': '创建', +}; +const RESOURCE_LABELS: Record = { + user: '用户', role: '角色', patient: '患者', doctor: '医护', appointment: '预约', + follow_up_task: '随访', consultation_session: '咨询', message: '消息', plugin: '插件', + process_instance: '流程实例', organization: '组织', +}; +const RESOURCE_ICONS: Record = { + user: , role: , + patient: , organization: , + process_instance: , message: , +}; + function formatActionLabel(action: string): string { - if (ACTION_LABELS[action]) return ACTION_LABELS[action]; - const lastPart = action.split('.').pop() || action; - return ACTION_LABELS[lastPart] || lastPart; + return ACTION_LABELS[action] || ACTION_LABELS[action.split('.').pop() || ''] || action; +} +function formatResourceLabel(resource: string): string { + return RESOURCE_LABELS[resource] || RESOURCE_LABELS[resource.split('.').pop() || ''] || resource; } -function formatResourceLabel(resource: string): string { - if (RESOURCE_LABELS[resource]) return RESOURCE_LABELS[resource]; - const lastPart = resource.split('.').pop() || resource; - return RESOURCE_LABELS[lastPart] || lastPart; +// --- Role configs --- + +interface StatCardDef { + key: string; + title: string; + getValue: (p: PersonalStats | null, s: ReturnType) => number; + icon: React.ReactNode; + suffix?: string; + path: string; +} + +interface QuickActionDef { + icon: React.ReactNode; + label: string; + path: string; +} + +const ROLE_WELCOME: Record = { + doctor: { title: '今日工作台', subtitle: '患者概览与待办事项' }, + nurse: { title: '随访监控台', subtitle: '今日随访与体征上报' }, + admin: { title: '管理中心', subtitle: '平台运营数据概览' }, + operator: { title: '运营中心', subtitle: '积分、内容与活动' }, +}; + +const ROLE_STATS: Record = { + doctor: [ + { key: 'my-patients', title: '我的患者', getValue: (p) => p?.my_patients ?? 0, icon: , path: '/health/patients' }, + { key: 'today-appointments', title: '今日预约', getValue: (p) => p?.today_appointments ?? 0, icon: , path: '/health/appointments' }, + { key: 'consultations', title: '本月咨询', getValue: (p) => p?.consultations_this_month ?? 0, icon: , path: '/health/consultations' }, + { key: 'followup-rate', title: '随访完成率', getValue: (p) => p?.follow_up_rate ?? 0, icon: , suffix: '%', path: '/health/follow-ups' }, + ], + nurse: [ + { key: 'today-appointments', title: '今日预约', getValue: (p) => p?.today_appointments ?? 0, icon: , path: '/health/appointments' }, + { key: 'today-followups', title: '今日随访', getValue: (p) => p?.today_follow_ups ?? 0, icon: , path: '/health/follow-ups' }, + { key: 'overdue', title: '逾期随访', getValue: (p) => p?.overdue_follow_ups ?? 0, icon: , path: '/health/follow-ups' }, + { key: 'vital-rate', title: '体征上报率', getValue: (p) => p?.vital_signs_report_rate ?? 0, icon: , suffix: '%', path: '/health/vital-signs' }, + ], + admin: [ + { key: 'patients', title: '患者总数', getValue: (_p, s) => s.patientStats?.total_patients ?? 0, icon: , path: '/health/patients' }, + { key: 'appointments', title: '本月预约', getValue: (_p, s) => s.healthDataStats?.appointments?.this_month ?? 0, icon: , path: '/health/appointments' }, + { key: 'followup-rate', title: '随访完成率', getValue: (_p, s) => s.followUpStats?.completion_rate ?? 0, icon: , suffix: '%', path: '/health/follow-ups' }, + { key: 'vital-rate', title: '体征上报率', getValue: (_p, s) => s.healthDataStats?.vital_signs_report_rate?.report_rate ?? 0, icon: , suffix: '%', path: '/health/vital-signs' }, + ], + operator: [ + { key: 'issued', title: '积分发放', getValue: (_p, s) => s.pointsStats?.total_issued ?? 0, icon: , path: '/health/points' }, + { key: 'spent', title: '积分消费', getValue: (_p, s) => s.pointsStats?.total_spent ?? 0, icon: , path: '/health/mall' }, + { key: 'active', title: '活跃账户', getValue: (_p, s) => s.pointsStats?.active_accounts ?? 0, icon: , path: '/health/points' }, + { key: 'articles', title: '内容发布', getValue: () => 0, icon: , path: '/health/content' }, + ], +}; + +const ROLE_ACTIONS: Record = { + doctor: [ + { icon: , label: '患者管理', path: '/health/patients' }, + { icon: , label: '预约管理', path: '/health/appointments' }, + { icon: , label: '随访管理', path: '/health/follow-ups' }, + { icon: , label: '咨询管理', path: '/health/consultations' }, + { icon: , label: '告警中心', path: '/health/alert-dashboard' }, + { icon: , label: '健康数据', path: '/health/statistics' }, + ], + nurse: [ + { icon: , label: '随访管理', path: '/health/follow-ups' }, + { icon: , label: '健康数据', path: '/health/vital-signs' }, + { icon: , label: '预约管理', path: '/health/appointments' }, + { icon: , label: '告警中心', path: '/health/alert-dashboard' }, + { icon: , label: '患者管理', path: '/health/patients' }, + { icon: , label: '健康统计', path: '/health/statistics' }, + ], + admin: [ + { icon: , label: '患者管理', path: '/health/patients' }, + { icon: , label: '预约管理', path: '/health/appointments' }, + { icon: , label: '随访管理', path: '/health/follow-ups' }, + { icon: , label: '健康数据', path: '/health/vital-signs' }, + { icon: , label: '积分商城', path: '/health/points' }, + { icon: , label: '系统设置', path: '/settings' }, + ], + operator: [ + { icon: , label: '积分管理', path: '/health/points' }, + { icon: , label: '内容管理', path: '/health/content' }, + { icon: , label: '线下活动', path: '/health/events' }, + { icon: , label: '患者管理', path: '/health/patients' }, + { icon: , label: '健康统计', path: '/health/statistics' }, + { icon: , label: '系统设置', path: '/settings' }, + ], +}; + +// --- Components --- + +function StatValue({ value, loading }: { value: number; loading: boolean }) { + const animatedValue = useCountUp(value); + if (loading) return ; + return {animatedValue.toLocaleString()}; } export default function Home() { - const [stats, setStats] = useState({ - userCount: 0, - roleCount: 0, - processInstanceCount: 0, - unreadMessages: 0, - }); - const [loading, setLoading] = useState(true); + const navigate = useNavigate(); + const role = useDashboardRole(); + const isDark = useThemeMode(); + const fetchUnreadCount = useMessageStore((s) => s.fetchUnreadCount); + + const [personalStats, setPersonalStats] = useState(null); + const [personalLoading, setPersonalLoading] = useState(true); const [pendingTasks, setPendingTasks] = useState([]); const [recentActivities, setRecentActivities] = useState([]); const [activitiesLoading, setActivitiesLoading] = useState(true); - const unreadCount = useMessageStore((s) => s.unreadCount); - const fetchUnreadCount = useMessageStore((s) => s.fetchUnreadCount); - const navigate = useNavigate(); - const isDark = useThemeMode(); + const statsData = useStatsData(); + const loading = personalLoading || statsData.loading; + + const welcome = ROLE_WELCOME[role]; + const statDefs = ROLE_STATS[role]; + const quickActions = ROLE_ACTIONS[role]; useEffect(() => { let cancelled = false; - - async function loadStats() { - setLoading(true); - try { - const [usersRes, rolesRes, instancesRes] = await Promise.allSettled([ - client.get('/users', { params: { page: 1, page_size: 1 } }), - client.get('/roles', { params: { page: 1, page_size: 1 } }), - client.get('/workflow/instances', { params: { page: 1, page_size: 1 } }), - ]); - - if (cancelled) return; - - const extractTotal = (res: PromiseSettledResult<{ data: { data?: { total?: number } } }>) => { - if (res.status !== 'fulfilled') return 0; - const body = res.value.data; - if (body && typeof body === 'object' && 'data' in body) { - const inner = (body as { data?: { total?: number } }).data; - return inner?.total ?? 0; - } - return 0; - }; - - setStats({ - userCount: extractTotal(usersRes), - roleCount: extractTotal(rolesRes), - processInstanceCount: extractTotal(instancesRes), - unreadMessages: unreadCount, - }); - } catch { - // 静默处理 - } finally { - if (!cancelled) setLoading(false); - } - } - - async function loadTasks() { - try { - const result = await listPendingTasks(1, 5); - if (!cancelled) setPendingTasks(result.data); - } catch { - // 静默处理 - } - } - - async function loadActivities() { - setActivitiesLoading(true); - try { - const result = await listAuditLogs({ page: 1, page_size: 5 }); - if (!cancelled) setRecentActivities(result.data.filter(a => a.action !== 'login_failed')); - } catch { - // 静默处理 - } finally { - if (!cancelled) setActivitiesLoading(false); - } - } - fetchUnreadCount(); - loadStats(); - loadTasks(); - loadActivities(); + + if (role === 'doctor' || role === 'nurse') { + pointsApi.getPersonalStats() + .then((data) => { if (!cancelled) setPersonalStats(data); }) + .catch(() => {}) + .finally(() => { if (!cancelled) setPersonalLoading(false); }); + } else { + setPersonalLoading(false); + } + + listPendingTasks(1, 5) + .then((result) => { if (!cancelled) setPendingTasks(result.data); }) + .catch(() => {}); + + listAuditLogs({ page: 1, page_size: 5 }) + .then((result) => { + if (!cancelled) setRecentActivities(result.data.filter((a) => a.action !== 'login_failed')); + }) + .catch(() => {}) + .finally(() => { if (!cancelled) setActivitiesLoading(false); }); return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [role]); const handleNavigate = useCallback((path: string) => { navigate(path); }, [navigate]); - const statCards: StatCardConfig[] = [ - { - key: 'users', - title: '用户总数', - value: stats.userCount, - icon: , - gradient: 'linear-gradient(135deg, #2563eb, #60a5fa)', - iconBg: 'rgba(79, 70, 229, 0.12)', - delay: 'erp-fade-in erp-fade-in-delay-1', - onClick: () => handleNavigate('/users'), - }, - { - key: 'roles', - title: '角色数量', - value: stats.roleCount, - icon: , - gradient: 'linear-gradient(135deg, #059669, #10B981)', - iconBg: 'rgba(5, 150, 105, 0.12)', - delay: 'erp-fade-in erp-fade-in-delay-2', - onClick: () => handleNavigate('/roles'), - }, - { - key: 'processes', - title: '流程实例', - value: stats.processInstanceCount, - icon: , - gradient: 'linear-gradient(135deg, #d97706, #F59E0B)', - iconBg: 'rgba(217, 119, 6, 0.12)', - delay: 'erp-fade-in erp-fade-in-delay-3', - onClick: () => handleNavigate('/workflow'), - }, - { - key: 'messages', - title: '未读消息', - value: stats.unreadMessages, - icon: , - gradient: 'linear-gradient(135deg, #E11D48, #F43F5E)', - iconBg: 'rgba(225, 29, 72, 0.12)', - delay: 'erp-fade-in erp-fade-in-delay-4', - onClick: () => handleNavigate('/messages'), - }, - ]; - - const quickActions = [ - { icon: , label: '用户管理', path: '/users', color: '#2563eb' }, - { icon: , label: '权限管理', path: '/roles', color: '#059669' }, - { icon: , label: '组织架构', path: '/organizations', color: '#d97706' }, - { icon: , label: '工作流', path: '/workflow', color: '#7C3AED' }, - { icon: , label: '消息中心', path: '/messages', color: '#E11D48' }, - { icon: , label: '系统设置', path: '/settings', color: '#475569' }, - ]; - return (
{/* 欢迎语 */} @@ -266,57 +219,55 @@ export default function Home() {

- 工作台 + {welcome.title}

-

- 欢迎回来,这是您的系统概览 +

+ {welcome.subtitle}

{/* 统计卡片行 */} - {statCards.map((card) => ( - -
{ if (e.key === 'Enter') card.onClick?.(); }} - > -
-
-
-
{card.title}
-
- + {statDefs.map((def, i) => { + const value = def.getValue(personalStats, statsData); + return ( + +
handleNavigate(def.path)} + role="button" + tabIndex={0} + onKeyDown={(e) => { if (e.key === 'Enter') handleNavigate(def.path); }} + > +
+
+
+
{def.title}
+
+ + {def.suffix && {def.suffix}} +
+
{def.icon}
-
{card.icon}
-
- - ))} + + ); + })} {/* 待办任务 + 最近活动 */} - {/* 待办任务 */}
- + 待办任务 - + {pendingTasks.length} 项待处理
@@ -328,7 +279,7 @@ export default function Home() {
handleNavigate('/workflow')} role="button" tabIndex={0} @@ -343,7 +294,7 @@ export default function Home() {
一般 - +
)) )} @@ -351,11 +302,10 @@ export default function Home() {
- {/* 最近活动 */}
- + 最近动态
@@ -383,9 +333,9 @@ export default function Home() { - {/* 快捷入口 + 系统信息 */} + {/* 快捷入口 */} - +
@@ -393,10 +343,10 @@ export default function Home() {
{quickActions.map((action) => ( - +
handleNavigate(action.path)} role="button" tabIndex={0} @@ -410,30 +360,6 @@ export default function Home() {
- - -
-
- - 系统信息 -
-
- {[ - { label: '系统版本', value: 'v0.1.0' }, - { label: '后端框架', value: 'Axum 0.8 + Tokio' }, - { label: '数据库', value: 'PostgreSQL 18' }, - { label: '缓存', value: 'Redis 7' }, - { label: '前端框架', value: 'React 19 + Ant Design 6' }, - { label: '模块数量', value: '6 个业务模块' }, - ].map((item) => ( -
- {item.label} - {item.value} -
- ))} -
-
-
); diff --git a/apps/web/src/pages/Login.tsx b/apps/web/src/pages/Login.tsx index ee17fbd..60ef6ea 100644 --- a/apps/web/src/pages/Login.tsx +++ b/apps/web/src/pages/Login.tsx @@ -2,6 +2,7 @@ import { useNavigate } from 'react-router-dom'; import { Form, Input, Button, message, Divider } from 'antd'; import { UserOutlined, LockOutlined, SafetyCertificateOutlined } from '@ant-design/icons'; import { useAuthStore } from '../stores/auth'; +import ThemeSwitcher from '../components/ThemeSwitcher'; export default function Login() { const navigate = useNavigate(); @@ -23,100 +24,23 @@ export default function Login() { }; return ( -
+
{contextHolder} {/* 左侧品牌展示区 */} -
- {/* 装饰性背景元素 */} -
-
+
+
+
- {/* 品牌内容 */} -
-
- +
+
+
-

- ERP Platform -

-

- 新一代模块化企业资源管理平台 -

-

- 身份权限 · 工作流引擎 · 消息中心 · 系统配置 -

+

HMR Platform

+

新一代健康管理平台

+

慢病跟踪 · 工作流引擎 · 消息中心 · 系统配置

- {/* 底部特性点 */}
{[ { label: '多租户架构', value: 'SaaS' }, @@ -124,12 +48,8 @@ export default function Login() { { label: '事件驱动', value: '可扩展' }, ].map((item) => (
-
- {item.value} -
-
- {item.label} -
+
{item.value}
+
{item.label}
))}
@@ -137,23 +57,14 @@ export default function Login() {
{/* 右侧登录表单区 */} -
+
+
+ +
+
-

- 欢迎回来 -

-

- 请登录您的账户以继续 -

+

欢迎回来

+

请登录您的账户以继续

@@ -163,9 +74,9 @@ export default function Login() { rules={[{ required: true, message: '请输入用户名' }]} > } + prefix={} placeholder="用户名" - style={{ height: 44, borderRadius: 10 }} + style={{ height: 44, borderRadius: 'var(--erp-radius-md)' }} /> } + prefix={} placeholder="密码" - style={{ height: 44, borderRadius: 10 }} + style={{ height: 44, borderRadius: 'var(--erp-radius-md)' }} /> @@ -184,22 +95,15 @@ export default function Login() { htmlType="submit" loading={loading} block - style={{ - height: 44, - borderRadius: 10, - fontSize: 15, - fontWeight: 600, - }} + style={{ height: 44, borderRadius: 'var(--erp-radius-md)', fontSize: 15, fontWeight: 600 }} > 登录 -
-

- ERP Platform · Powered by Rust + React -

+
+ HMR Platform · ©汕头市智界科技有限公司