feat(web): 采用 Notion 设计系统 — 暖色调 + 白色侧边栏 + Inter 字体
引入 Notion 风格的 DESIGN.md 设计系统文件,并全面重构前端 UI: - 主色从 Indigo (#4F46E5) 迁移到 Notion Blue (#0075de) - 页面背景从冷灰 (#F1F5F9) 迁移到暖白 (#f6f5f4) - 侧边栏从深色 (#0F172A) 迁移到白色,活跃项用蓝色指示 - 文字从 Slate 冷色迁移到暖灰系列 (Warm Gray 500/300) - 圆角从 8px 缩小到 4px(按钮/输入),8px(卡片) - 阴影改为多层超轻 Notion 风格(最大 opacity 0.05) - 字体优先使用 Inter,保留中文回退 - 暗色模式适配暖黑色调 (#191918) - 更新 27 个前端文件的硬编码颜色值
This commit is contained in:
@@ -31,52 +31,52 @@ function PrivateRoute({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const themeConfig = {
|
||||
token: {
|
||||
colorPrimary: '#4F46E5',
|
||||
colorSuccess: '#059669',
|
||||
colorWarning: '#D97706',
|
||||
colorError: '#DC2626',
|
||||
colorInfo: '#2563EB',
|
||||
colorBgLayout: '#F1F5F9',
|
||||
colorBgContainer: '#FFFFFF',
|
||||
colorBgElevated: '#FFFFFF',
|
||||
colorBorder: '#E2E8F0',
|
||||
colorBorderSecondary: '#F1F5F9',
|
||||
borderRadius: 8,
|
||||
borderRadiusLG: 12,
|
||||
borderRadiusSM: 6,
|
||||
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Helvetica Neue', Helvetica, Arial, sans-serif",
|
||||
colorPrimary: '#0075de',
|
||||
colorSuccess: '#1aae39',
|
||||
colorWarning: '#dd5b00',
|
||||
colorError: '#e5534b',
|
||||
colorInfo: '#0075de',
|
||||
colorBgLayout: '#f6f5f4',
|
||||
colorBgContainer: '#ffffff',
|
||||
colorBgElevated: '#ffffff',
|
||||
colorBorder: 'rgba(0, 0, 0, 0.1)',
|
||||
colorBorderSecondary: 'rgba(0, 0, 0, 0.06)',
|
||||
borderRadius: 4,
|
||||
borderRadiusLG: 8,
|
||||
borderRadiusSM: 2,
|
||||
fontFamily: "'Inter', -apple-system, system-ui, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', Helvetica, Arial, sans-serif",
|
||||
fontSize: 14,
|
||||
fontSizeHeading4: 20,
|
||||
controlHeight: 36,
|
||||
controlHeightLG: 40,
|
||||
controlHeightSM: 28,
|
||||
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.06)',
|
||||
boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.07)',
|
||||
boxShadow: 'none',
|
||||
boxShadowSecondary: 'rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px',
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
primaryShadow: '0 1px 2px 0 rgba(79, 70, 229, 0.3)',
|
||||
primaryShadow: 'none',
|
||||
fontWeight: 500,
|
||||
},
|
||||
Card: {
|
||||
paddingLG: 20,
|
||||
},
|
||||
Table: {
|
||||
headerBg: '#F8FAFC',
|
||||
headerColor: '#475569',
|
||||
rowHoverBg: '#F5F3FF',
|
||||
headerBg: '#fafaf9',
|
||||
headerColor: '#615d59',
|
||||
rowHoverBg: '#f2f9ff',
|
||||
fontSize: 14,
|
||||
},
|
||||
Menu: {
|
||||
itemBorderRadius: 8,
|
||||
itemBorderRadius: 4,
|
||||
itemMarginInline: 8,
|
||||
itemHeight: 40,
|
||||
itemHeight: 36,
|
||||
},
|
||||
Modal: {
|
||||
borderRadiusLG: 16,
|
||||
borderRadiusLG: 12,
|
||||
},
|
||||
Tag: {
|
||||
borderRadiusSM: 6,
|
||||
borderRadiusSM: 4,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -85,20 +85,20 @@ const darkThemeConfig = {
|
||||
...themeConfig,
|
||||
token: {
|
||||
...themeConfig.token,
|
||||
colorBgLayout: '#0B0F1A',
|
||||
colorBgContainer: '#111827',
|
||||
colorBgElevated: '#1E293B',
|
||||
colorBorder: '#1E293B',
|
||||
colorBorderSecondary: '#1E293B',
|
||||
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.3)',
|
||||
boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.4)',
|
||||
colorBgLayout: '#191918',
|
||||
colorBgContainer: '#232322',
|
||||
colorBgElevated: '#2a2a29',
|
||||
colorBorder: 'rgba(255, 255, 255, 0.08)',
|
||||
colorBorderSecondary: 'rgba(255, 255, 255, 0.05)',
|
||||
boxShadow: 'none',
|
||||
boxShadowSecondary: 'rgba(0,0,0,0.2) 0px 4px 18px, rgba(0,0,0,0.15) 0px 2px 8px',
|
||||
},
|
||||
components: {
|
||||
...themeConfig.components,
|
||||
Table: {
|
||||
headerBg: '#1E293B',
|
||||
headerColor: '#94A3B8',
|
||||
rowHoverBg: '#1E293B',
|
||||
headerBg: '#2a2a29',
|
||||
headerColor: '#a39e98',
|
||||
rowHoverBg: '#2a2a29',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function NotificationPanel() {
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
style={{ fontSize: 12, color: '#4F46E5' }}
|
||||
style={{ fontSize: 12, color: '#0075de' }}
|
||||
onClick={() => navigate('/messages')}
|
||||
>
|
||||
查看全部
|
||||
@@ -76,7 +76,7 @@ export default function NotificationPanel() {
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.15s ease',
|
||||
border: 'none',
|
||||
background: !item.is_read ? (isDark ? '#1E293B' : '#F5F3FF') : 'transparent',
|
||||
background: !item.is_read ? (isDark ? '#1e1e1d' : '#f2f9ff') : 'transparent',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!item.is_read) {
|
||||
@@ -85,7 +85,7 @@ export default function NotificationPanel() {
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (item.is_read) {
|
||||
e.currentTarget.style.background = isDark ? '#1E293B' : '#F8FAFC';
|
||||
e.currentTarget.style.background = isDark ? '#1e1e1d' : '#fafaf9';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -109,7 +109,7 @@ export default function NotificationPanel() {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: '#4F46E5',
|
||||
background: '#0075de',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
@@ -132,12 +132,12 @@ export default function NotificationPanel() {
|
||||
textAlign: 'center',
|
||||
paddingTop: 8,
|
||||
marginTop: 4,
|
||||
borderTop: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
borderTop: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
}}>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => navigate('/messages')}
|
||||
style={{ fontSize: 13, color: '#4F46E5', fontWeight: 500 }}
|
||||
style={{ fontSize: 13, color: '#0075de', fontWeight: 500 }}
|
||||
>
|
||||
查看全部消息
|
||||
</Button>
|
||||
@@ -166,7 +166,7 @@ export default function NotificationPanel() {
|
||||
position: 'relative',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = isDark ? '#1E293B' : '#F1F5F9';
|
||||
e.currentTarget.style.background = isDark ? '#1e1e1d' : '#f6f5f4';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = 'transparent';
|
||||
@@ -175,7 +175,7 @@ export default function NotificationPanel() {
|
||||
<Badge count={unreadCount} size="small" offset={[4, -4]}>
|
||||
<BellOutlined style={{
|
||||
fontSize: 16,
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
}} />
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -2,65 +2,65 @@
|
||||
|
||||
/* ====================================================================
|
||||
* ERP Platform — Design System Tokens & Global Styles
|
||||
* Inspired by Linear, Feishu, SAP Fiori modern design language
|
||||
* Inspired by Notion: warm minimalism, whisper borders, soft elevation
|
||||
* ==================================================================== */
|
||||
|
||||
/* --- Design Tokens (CSS Custom Properties) --- */
|
||||
:root {
|
||||
/* Primary Palette */
|
||||
--erp-primary: #4F46E5;
|
||||
--erp-primary-hover: #4338CA;
|
||||
--erp-primary-active: #3730A3;
|
||||
--erp-primary-light: #EEF2FF;
|
||||
--erp-primary-light-hover: #E0E7FF;
|
||||
--erp-primary-bg-subtle: #F5F3FF;
|
||||
/* Primary Palette — Notion Blue */
|
||||
--erp-primary: #0075de;
|
||||
--erp-primary-hover: #005bab;
|
||||
--erp-primary-active: #004a8c;
|
||||
--erp-primary-light: #f2f9ff;
|
||||
--erp-primary-light-hover: #e4f1ff;
|
||||
--erp-primary-bg-subtle: #f2f9ff;
|
||||
|
||||
/* Semantic Colors */
|
||||
--erp-success: #059669;
|
||||
--erp-success-bg: #ECFDF5;
|
||||
--erp-warning: #D97706;
|
||||
--erp-warning-bg: #FFFBEB;
|
||||
--erp-error: #DC2626;
|
||||
--erp-error-bg: #FEF2F2;
|
||||
--erp-info: #2563EB;
|
||||
--erp-info-bg: #EFF6FF;
|
||||
/* Semantic Colors — Notion warm tones */
|
||||
--erp-success: #1aae39;
|
||||
--erp-success-bg: #ecfdf5;
|
||||
--erp-warning: #dd5b00;
|
||||
--erp-warning-bg: #fff7ed;
|
||||
--erp-error: #e5534b;
|
||||
--erp-error-bg: #fef2f2;
|
||||
--erp-info: #0075de;
|
||||
--erp-info-bg: #f2f9ff;
|
||||
|
||||
/* Neutral Palette */
|
||||
--erp-bg-page: #F1F5F9;
|
||||
--erp-bg-container: #FFFFFF;
|
||||
--erp-bg-elevated: #FFFFFF;
|
||||
--erp-bg-spotlight: #F8FAFC;
|
||||
--erp-bg-sidebar: #0F172A;
|
||||
--erp-bg-sidebar-hover: #1E293B;
|
||||
--erp-bg-sidebar-active: rgba(79, 70, 229, 0.15);
|
||||
/* Neutral Palette — Warm neutrals with yellow-brown undertones */
|
||||
--erp-bg-page: #f6f5f4;
|
||||
--erp-bg-container: #ffffff;
|
||||
--erp-bg-elevated: #ffffff;
|
||||
--erp-bg-spotlight: #fafaf9;
|
||||
--erp-bg-sidebar: #ffffff;
|
||||
--erp-bg-sidebar-hover: #f6f5f4;
|
||||
--erp-bg-sidebar-active: #f2f9ff;
|
||||
|
||||
/* Text Colors */
|
||||
--erp-text-primary: #0F172A;
|
||||
--erp-text-secondary: #475569;
|
||||
--erp-text-tertiary: #94A3B8;
|
||||
--erp-text-inverse: #F8FAFC;
|
||||
--erp-text-sidebar: #CBD5E1;
|
||||
--erp-text-sidebar-active: #FFFFFF;
|
||||
/* Text Colors — Warm near-black */
|
||||
--erp-text-primary: rgba(0, 0, 0, 0.95);
|
||||
--erp-text-secondary: #615d59;
|
||||
--erp-text-tertiary: #a39e98;
|
||||
--erp-text-inverse: #ffffff;
|
||||
--erp-text-sidebar: #615d59;
|
||||
--erp-text-sidebar-active: #0075de;
|
||||
|
||||
/* Border Colors */
|
||||
--erp-border: #E2E8F0;
|
||||
--erp-border-light: #F1F5F9;
|
||||
--erp-border-dark: #334155;
|
||||
/* Border Colors — Whisper borders */
|
||||
--erp-border: rgba(0, 0, 0, 0.1);
|
||||
--erp-border-light: rgba(0, 0, 0, 0.06);
|
||||
--erp-border-dark: rgba(0, 0, 0, 0.15);
|
||||
|
||||
/* Shadows */
|
||||
--erp-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
||||
--erp-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.06), 0 1px 2px -1px rgba(0, 0, 0, 0.06);
|
||||
--erp-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.07);
|
||||
--erp-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.08);
|
||||
--erp-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 8px 10px -6px rgba(0, 0, 0, 0.08);
|
||||
/* Shadows — Multi-layer, ultra-subtle */
|
||||
--erp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.01), 0 0.175px 1.04px rgba(0, 0, 0, 0.01);
|
||||
--erp-shadow-sm: rgba(0, 0, 0, 0.04) 0px 4px 18px, rgba(0, 0, 0, 0.027) 0px 2.025px 7.85px, rgba(0, 0, 0, 0.02) 0px 0.8px 2.93px, rgba(0, 0, 0, 0.01) 0px 0.175px 1.04px;
|
||||
--erp-shadow-md: rgba(0, 0, 0, 0.01) 0px 1px 3px, rgba(0, 0, 0, 0.02) 0px 3px 7px, rgba(0, 0, 0, 0.02) 0px 7px 15px, rgba(0, 0, 0, 0.04) 0px 14px 28px, rgba(0, 0, 0, 0.05) 0px 23px 52px;
|
||||
--erp-shadow-lg: rgba(0, 0, 0, 0.01) 0px 2px 4px, rgba(0, 0, 0, 0.02) 0px 5px 12px, rgba(0, 0, 0, 0.03) 0px 10px 24px, rgba(0, 0, 0, 0.04) 0px 18px 40px, rgba(0, 0, 0, 0.05) 0px 30px 64px;
|
||||
--erp-shadow-xl: rgba(0, 0, 0, 0.01) 0px 3px 6px, rgba(0, 0, 0, 0.02) 0px 8px 16px, rgba(0, 0, 0, 0.03) 0px 14px 30px, rgba(0, 0, 0, 0.04) 0px 22px 48px, rgba(0, 0, 0, 0.06) 0px 36px 80px;
|
||||
|
||||
/* Radius */
|
||||
--erp-radius-sm: 6px;
|
||||
--erp-radius-md: 8px;
|
||||
--erp-radius-lg: 12px;
|
||||
--erp-radius-xl: 16px;
|
||||
/* Radius — Notion subtle roundness */
|
||||
--erp-radius-sm: 2px;
|
||||
--erp-radius-md: 4px;
|
||||
--erp-radius-lg: 8px;
|
||||
--erp-radius-xl: 12px;
|
||||
|
||||
/* Spacing */
|
||||
/* Spacing — 8px base unit */
|
||||
--erp-space-xs: 4px;
|
||||
--erp-space-sm: 8px;
|
||||
--erp-space-md: 16px;
|
||||
@@ -68,9 +68,9 @@
|
||||
--erp-space-xl: 32px;
|
||||
--erp-space-2xl: 48px;
|
||||
|
||||
/* Typography */
|
||||
--erp-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC',
|
||||
'Microsoft YaHei', 'Hiragino Sans GB', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
/* Typography — Inter as primary */
|
||||
--erp-font-family: 'Inter', -apple-system, system-ui, 'Segoe UI', 'PingFang SC',
|
||||
'Microsoft YaHei', 'Hiragino Sans GB', Helvetica, Arial, sans-serif;
|
||||
--erp-font-mono: 'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
--erp-font-size-xs: 12px;
|
||||
@@ -86,10 +86,10 @@
|
||||
--erp-transition-base: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--erp-transition-slow: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Trend Colors */
|
||||
--erp-trend-up: #059669;
|
||||
--erp-trend-down: #DC2626;
|
||||
--erp-trend-neutral: #64748B;
|
||||
/* Trend Colors — Warm */
|
||||
--erp-trend-up: #1aae39;
|
||||
--erp-trend-down: #e5534b;
|
||||
--erp-trend-neutral: #615d59;
|
||||
|
||||
/* Line Height */
|
||||
--erp-line-height-tight: 1.25;
|
||||
@@ -102,36 +102,48 @@
|
||||
--erp-header-height: 56px;
|
||||
}
|
||||
|
||||
/* --- Dark Mode Tokens --- */
|
||||
/* --- Dark Mode Tokens — Warm dark, Notion-inspired --- */
|
||||
[data-theme='dark'] {
|
||||
--erp-bg-page: #0B0F1A;
|
||||
--erp-bg-container: #111827;
|
||||
--erp-bg-elevated: #1E293B;
|
||||
--erp-bg-spotlight: #1E293B;
|
||||
--erp-bg-sidebar: #070B14;
|
||||
--erp-bg-sidebar-hover: #111827;
|
||||
--erp-primary-light: rgba(0, 117, 222, 0.15);
|
||||
--erp-primary-light-hover: rgba(0, 117, 222, 0.22);
|
||||
--erp-primary-bg-subtle: rgba(0, 117, 222, 0.1);
|
||||
|
||||
--erp-text-primary: #F1F5F9;
|
||||
--erp-text-secondary: #94A3B8;
|
||||
--erp-text-tertiary: #64748B;
|
||||
--erp-bg-page: #191918;
|
||||
--erp-bg-container: #232322;
|
||||
--erp-bg-elevated: #2a2a29;
|
||||
--erp-bg-spotlight: #2a2a29;
|
||||
--erp-bg-sidebar: #1e1e1d;
|
||||
--erp-bg-sidebar-hover: #2a2a29;
|
||||
|
||||
--erp-border: #1E293B;
|
||||
--erp-border-light: #1E293B;
|
||||
--erp-text-primary: rgba(255, 255, 255, 0.95);
|
||||
--erp-text-secondary: #a39e98;
|
||||
--erp-text-tertiary: #6d6862;
|
||||
--erp-text-sidebar: #a39e98;
|
||||
--erp-text-sidebar-active: #62aef0;
|
||||
|
||||
--erp-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--erp-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.3);
|
||||
--erp-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
|
||||
--erp-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.3);
|
||||
--erp-border: rgba(255, 255, 255, 0.08);
|
||||
--erp-border-light: rgba(255, 255, 255, 0.05);
|
||||
--erp-border-dark: rgba(255, 255, 255, 0.12);
|
||||
|
||||
--erp-trend-up: #34D399;
|
||||
--erp-trend-down: #F87171;
|
||||
--erp-trend-neutral: #94A3B8;
|
||||
--erp-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--erp-shadow-sm: rgba(0, 0, 0, 0.2) 0px 4px 18px, rgba(0, 0, 0, 0.15) 0px 2px 8px;
|
||||
--erp-shadow-md: rgba(0, 0, 0, 0.15) 0px 1px 3px, rgba(0, 0, 0, 0.2) 0px 5px 12px, rgba(0, 0, 0, 0.2) 0px 10px 24px;
|
||||
--erp-shadow-lg: rgba(0, 0, 0, 0.2) 0px 2px 6px, rgba(0, 0, 0, 0.25) 0px 8px 20px, rgba(0, 0, 0, 0.3) 0px 16px 40px;
|
||||
|
||||
--erp-trend-up: #2a9d99;
|
||||
--erp-trend-down: #e5534b;
|
||||
--erp-trend-neutral: #a39e98;
|
||||
|
||||
--erp-success-bg: rgba(26, 174, 57, 0.15);
|
||||
--erp-warning-bg: rgba(221, 91, 0, 0.15);
|
||||
--erp-error-bg: rgba(229, 83, 75, 0.15);
|
||||
--erp-info-bg: rgba(0, 117, 222, 0.15);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-stat-card-trend-up { color: #34D399; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-down { color: #FCA5A5; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-neutral { color: #94A3B8; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-label { color: #94A3B8; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-up { color: #2a9d99; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-down { color: #e5534b; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-neutral { color: #a39e98; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-label { color: #a39e98; }
|
||||
|
||||
/* --- Global Reset & Base --- */
|
||||
body {
|
||||
@@ -170,20 +182,20 @@ body {
|
||||
|
||||
/* --- Selection --- */
|
||||
::selection {
|
||||
background-color: var(--erp-primary-light);
|
||||
color: var(--erp-primary);
|
||||
background-color: rgba(0, 117, 222, 0.15);
|
||||
color: var(--erp-text-primary);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* Component Overrides — Ant Design Enhancement
|
||||
* ==================================================================== */
|
||||
|
||||
/* --- Card --- */
|
||||
/* --- Card — Whisper border, soft shadow --- */
|
||||
.ant-card {
|
||||
border-radius: var(--erp-radius-lg) !important;
|
||||
border: 1px solid var(--erp-border-light) !important;
|
||||
box-shadow: var(--erp-shadow-xs) !important;
|
||||
transition: box-shadow var(--erp-transition-base), transform var(--erp-transition-base) !important;
|
||||
border: 1px solid var(--erp-border) !important;
|
||||
box-shadow: none !important;
|
||||
transition: box-shadow var(--erp-transition-base) !important;
|
||||
}
|
||||
|
||||
.ant-card:hover {
|
||||
@@ -209,15 +221,14 @@ body {
|
||||
/* --- Statistic Cards --- */
|
||||
.stat-card {
|
||||
border-radius: var(--erp-radius-lg) !important;
|
||||
border: none !important;
|
||||
border: 1px solid var(--erp-border) !important;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: all var(--erp-transition-base) !important;
|
||||
transition: box-shadow var(--erp-transition-base) !important;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px) !important;
|
||||
box-shadow: var(--erp-shadow-md) !important;
|
||||
box-shadow: var(--erp-shadow-sm) !important;
|
||||
}
|
||||
|
||||
.stat-card .ant-statistic-title {
|
||||
@@ -259,30 +270,29 @@ body {
|
||||
border-bottom: 1px solid var(--erp-border-light) !important;
|
||||
}
|
||||
|
||||
/* --- Button --- */
|
||||
/* --- Button — Subtle 4px radius, no heavy shadow --- */
|
||||
.ant-btn-primary {
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
border-radius: 4px !important;
|
||||
font-weight: 500 !important;
|
||||
box-shadow: 0 1px 2px 0 rgba(79, 70, 229, 0.3) !important;
|
||||
box-shadow: none !important;
|
||||
transition: all var(--erp-transition-fast) !important;
|
||||
}
|
||||
|
||||
.ant-btn-primary:hover {
|
||||
box-shadow: 0 2px 4px 0 rgba(79, 70, 229, 0.4) !important;
|
||||
transform: translateY(-1px);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.ant-btn-default {
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
border-radius: 4px !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
/* --- Input --- */
|
||||
/* --- Input — 4px radius, whisper border --- */
|
||||
.ant-input,
|
||||
.ant-input-affix-wrapper,
|
||||
.ant-select-selector,
|
||||
.ant-picker {
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
border-radius: 4px !important;
|
||||
transition: all var(--erp-transition-fast) !important;
|
||||
}
|
||||
|
||||
@@ -297,7 +307,7 @@ body {
|
||||
.ant-select-focused .ant-select-selector,
|
||||
.ant-picker-focused {
|
||||
border-color: var(--erp-primary) !important;
|
||||
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.12) !important;
|
||||
box-shadow: 0 0 0 2px rgba(0, 117, 222, 0.12) !important;
|
||||
}
|
||||
|
||||
/* --- Modal --- */
|
||||
@@ -426,12 +436,12 @@ body {
|
||||
border-radius: var(--erp-radius-lg) var(--erp-radius-lg) 0 0;
|
||||
}
|
||||
|
||||
.erp-gradient-card.indigo::before { background: linear-gradient(90deg, #4F46E5, #818CF8); }
|
||||
.erp-gradient-card.emerald::before { background: linear-gradient(90deg, #059669, #34D399); }
|
||||
.erp-gradient-card.amber::before { background: linear-gradient(90deg, #D97706, #FBBF24); }
|
||||
.erp-gradient-card.rose::before { background: linear-gradient(90deg, #E11D48, #FB7185); }
|
||||
.erp-gradient-card.sky::before { background: linear-gradient(90deg, #0284C7, #38BDF8); }
|
||||
.erp-gradient-card.violet::before { background: linear-gradient(90deg, #7C3AED, #A78BFA); }
|
||||
.erp-gradient-card.indigo::before { background: linear-gradient(90deg, #0075de, #62aef0); }
|
||||
.erp-gradient-card.emerald::before { background: linear-gradient(90deg, #1aae39, #4ade80); }
|
||||
.erp-gradient-card.amber::before { background: linear-gradient(90deg, #dd5b00, #fbbf24); }
|
||||
.erp-gradient-card.rose::before { background: linear-gradient(90deg, #e5534b, #f87171); }
|
||||
.erp-gradient-card.sky::before { background: linear-gradient(90deg, #0075de, #38bdf8); }
|
||||
.erp-gradient-card.violet::before { background: linear-gradient(90deg, #391c57, #a78bfa); }
|
||||
|
||||
/* --- Fade-in Animation --- */
|
||||
@keyframes erp-fade-in {
|
||||
@@ -529,23 +539,23 @@ body {
|
||||
* ==================================================================== */
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item {
|
||||
margin: 2px 8px !important;
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
height: 40px !important;
|
||||
line-height: 40px !important;
|
||||
margin: 1px 8px !important;
|
||||
border-radius: 4px !important;
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item-selected {
|
||||
background: var(--erp-primary) !important;
|
||||
color: #fff !important;
|
||||
background: #f2f9ff !important;
|
||||
color: #0075de !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item-selected .anticon {
|
||||
color: #fff !important;
|
||||
color: #0075de !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item:not(.ant-menu-item-selected):hover {
|
||||
background: var(--erp-bg-sidebar-hover) !important;
|
||||
background: #f6f5f4 !important;
|
||||
}
|
||||
|
||||
/* Sidebar group label */
|
||||
@@ -555,17 +565,17 @@ body {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
color: #94A3B8;
|
||||
color: #a39e98;
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* MainLayout — CSS classes replacing inline styles
|
||||
* ==================================================================== */
|
||||
|
||||
/* Sider */
|
||||
/* Sider — White sidebar, Notion style */
|
||||
.erp-sider-dark {
|
||||
background: #0F172A !important;
|
||||
border-right: none !important;
|
||||
background: #ffffff !important;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.06) !important;
|
||||
position: fixed !important;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@@ -575,57 +585,66 @@ body {
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sider-dark {
|
||||
background: #070B14 !important;
|
||||
background: #1e1e1d !important;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
/* Logo — Warm neutral, Notion style */
|
||||
.erp-sidebar-logo {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-logo {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.ant-layout-sider-collapsed .erp-sidebar-logo {
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.erp-sidebar-logo-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #4F46E5, #818CF8);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
background: #0075de;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.erp-sidebar-logo-text {
|
||||
margin-left: 12px;
|
||||
color: #F8FAFC;
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
color: rgba(0, 0, 0, 0.95);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Sidebar menu item */
|
||||
[data-theme='dark'] .erp-sidebar-logo-text {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
/* Sidebar menu item — White sidebar, warm text */
|
||||
.erp-sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
margin: 2px 8px;
|
||||
padding: 0 16px;
|
||||
border-radius: 8px;
|
||||
height: 36px;
|
||||
margin: 1px 8px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #94A3B8;
|
||||
color: #615d59;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -638,14 +657,28 @@ body {
|
||||
}
|
||||
|
||||
.erp-sidebar-item:hover:not(.erp-sidebar-item-active) {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: #E2E8F0;
|
||||
background: #f6f5f4;
|
||||
color: rgba(0, 0, 0, 0.95);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item {
|
||||
color: #a39e98;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item:hover:not(.erp-sidebar-item-active) {
|
||||
background: #2a2a29;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.erp-sidebar-item-active {
|
||||
background: linear-gradient(135deg, #4F46E5, #6366F1);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
background: #f2f9ff;
|
||||
color: #0075de;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item-active {
|
||||
background: rgba(0, 117, 222, 0.15);
|
||||
color: #62aef0;
|
||||
}
|
||||
|
||||
.erp-sidebar-item-icon {
|
||||
@@ -658,17 +691,17 @@ body {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/* Sidebar sub-menu (plugin group) */
|
||||
/* Sidebar sub-menu (plugin group) — Warm gray group headers */
|
||||
.erp-sidebar-submenu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
margin: 6px 8px 2px 8px;
|
||||
padding: 0 12px;
|
||||
border-radius: 6px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #94A3B8;
|
||||
font-size: 12px;
|
||||
color: #a39e98;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -677,12 +710,25 @@ body {
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-title:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: #E2E8F0;
|
||||
background: #f6f5f4;
|
||||
color: #615d59;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title {
|
||||
color: #6d6862;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title:hover {
|
||||
background: #2a2a29;
|
||||
color: #a39e98;
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-title-active {
|
||||
color: #A5B4FC;
|
||||
color: #0075de;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title-active {
|
||||
color: #62aef0;
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-arrow {
|
||||
@@ -707,10 +753,10 @@ body {
|
||||
transition: margin-left 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.erp-main-layout-light { background: #F1F5F9; }
|
||||
.erp-main-layout-dark { background: #0B0F1A; }
|
||||
.erp-main-layout-light { background: #f6f5f4; }
|
||||
.erp-main-layout-dark { background: #191918; }
|
||||
|
||||
/* Header */
|
||||
/* Header — Clean white, whisper border bottom */
|
||||
.erp-header {
|
||||
height: 56px !important;
|
||||
padding: 0 24px !important;
|
||||
@@ -724,44 +770,44 @@ body {
|
||||
}
|
||||
|
||||
.erp-header-light {
|
||||
background: #FFFFFF !important;
|
||||
border-bottom: 1px solid #F1F5F9;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||
background: #ffffff !important;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.erp-header-dark {
|
||||
background: #111827 !important;
|
||||
border-bottom: 1px solid #1E293B;
|
||||
background: #232322 !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.erp-header-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
color: #94A3B8;
|
||||
color: #615d59;
|
||||
will-change: background;
|
||||
}
|
||||
|
||||
.erp-header-light .erp-header-btn { color: #64748B; }
|
||||
.erp-header-dark .erp-header-btn { color: #94A3B8; }
|
||||
.erp-header-btn:hover { background: #F1F5F9; }
|
||||
.erp-header-dark .erp-header-btn:hover { background: #1E293B; }
|
||||
.erp-header-light .erp-header-btn { color: #615d59; }
|
||||
.erp-header-dark .erp-header-btn { color: #a39e98; }
|
||||
.erp-header-btn:hover { background: #f6f5f4; }
|
||||
.erp-header-dark .erp-header-btn:hover { background: #2a2a29; }
|
||||
|
||||
.erp-header-title { font-size: 15px; font-weight: 600; }
|
||||
.erp-text-light { color: #0F172A; }
|
||||
.erp-text-dark { color: #F1F5F9; }
|
||||
.erp-text-light-secondary { color: #334155; }
|
||||
.erp-text-dark-secondary { color: #E2E8F0; }
|
||||
.erp-text-light { color: rgba(0, 0, 0, 0.95); }
|
||||
.erp-text-dark { color: rgba(255, 255, 255, 0.95); }
|
||||
.erp-text-light-secondary { color: #615d59; }
|
||||
.erp-text-dark-secondary { color: #a39e98; }
|
||||
|
||||
.erp-header-divider { width: 1px; height: 24px; margin: 0 8px; }
|
||||
.erp-header-divider-light { background: #E2E8F0; }
|
||||
.erp-header-divider-dark { background: #1E293B; }
|
||||
.erp-header-divider-light { background: rgba(0, 0, 0, 0.06); }
|
||||
.erp-header-divider-dark { background: rgba(255, 255, 255, 0.05); }
|
||||
|
||||
/* User avatar */
|
||||
.erp-header-user {
|
||||
@@ -774,11 +820,11 @@ body {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.erp-header-user:hover { background: #F1F5F9; }
|
||||
.erp-header-dark .erp-header-user:hover { background: #1E293B; }
|
||||
.erp-header-user:hover { background: #f6f5f4; }
|
||||
.erp-header-dark .erp-header-user:hover { background: #2a2a29; }
|
||||
|
||||
.erp-user-avatar {
|
||||
background: linear-gradient(135deg, #4F46E5, #818CF8) !important;
|
||||
background: #0075de !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
@@ -786,8 +832,8 @@ body {
|
||||
|
||||
/* Footer */
|
||||
.erp-footer { text-align: center; padding: 12px 24px !important; background: transparent !important; font-size: 12px; }
|
||||
.erp-footer-light { color: #475569; }
|
||||
.erp-footer-dark { color: #94A3B8; }
|
||||
.erp-footer-light { color: #a39e98; }
|
||||
.erp-footer-dark { color: #6d6862; }
|
||||
|
||||
/* ====================================================================
|
||||
* Dashboard — Stat Cards & Quick Actions (replacing inline styles)
|
||||
@@ -818,7 +864,7 @@ body {
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: var(--card-gradient, linear-gradient(135deg, #4F46E5, #6366F1));
|
||||
background: var(--card-gradient, linear-gradient(135deg, #0075de, #62aef0));
|
||||
}
|
||||
|
||||
.erp-stat-card-body {
|
||||
@@ -849,7 +895,7 @@ body {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--erp-radius-lg);
|
||||
background: var(--card-icon-bg, rgba(79, 70, 229, 0.12));
|
||||
background: var(--card-icon-bg, rgba(0, 117, 222, 0.08));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -867,7 +913,7 @@ body {
|
||||
|
||||
.erp-section-icon {
|
||||
font-size: 16px;
|
||||
color: #4F46E5;
|
||||
color: #0075de;
|
||||
}
|
||||
|
||||
.erp-section-title {
|
||||
@@ -890,17 +936,17 @@ body {
|
||||
}
|
||||
|
||||
.erp-quick-action:hover {
|
||||
background: #EEF2FF;
|
||||
border-color: var(--action-color, #4F46E5);
|
||||
background: #f2f9ff;
|
||||
border-color: var(--action-color, #0075de);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-quick-action {
|
||||
background: #0B0F1A;
|
||||
background: #191918;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-quick-action:hover {
|
||||
background: #1E293B;
|
||||
border-color: var(--action-color, #4F46E5);
|
||||
background: #2a2a29;
|
||||
border-color: var(--action-color, #0075de);
|
||||
}
|
||||
|
||||
.erp-quick-action-icon {
|
||||
@@ -910,8 +956,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--action-color, #4F46E5) 10%, transparent);
|
||||
color: var(--action-color, #4F46E5);
|
||||
background: color-mix(in srgb, var(--action-color, #0075de) 8%, transparent);
|
||||
color: var(--action-color, #0075de);
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -1000,8 +1046,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--action-color, #4F46E5) 10%, transparent);
|
||||
color: var(--action-color, #4F46E5);
|
||||
background: color-mix(in srgb, var(--action-color, #0075de) 8%, transparent);
|
||||
color: var(--action-color, #0075de);
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.15s ease;
|
||||
@@ -1029,7 +1075,7 @@ body {
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--erp-radius-md);
|
||||
background: var(--erp-bg-spotlight);
|
||||
border-left: 3px solid var(--task-color, #4F46E5);
|
||||
border-left: 3px solid var(--task-color, #0075de);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
@@ -1046,8 +1092,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--task-color, #4F46E5) 12%, transparent);
|
||||
color: var(--task-color, #4F46E5);
|
||||
background: color-mix(in srgb, var(--task-color, #0075de) 8%, transparent);
|
||||
color: var(--task-color, #0075de);
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -1069,7 +1115,7 @@ body {
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
font-size: var(--erp-font-size-xs);
|
||||
color: #64748B;
|
||||
color: #a39e98;
|
||||
}
|
||||
|
||||
.erp-task-priority {
|
||||
@@ -1081,13 +1127,13 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.erp-task-priority-high { background: #FEF2F2; color: #B91C1C; }
|
||||
.erp-task-priority-medium { background: #FFFBEB; color: #92400E; }
|
||||
.erp-task-priority-low { background: #ECFDF5; color: #047857; }
|
||||
.erp-task-priority-high { background: #fef2f2; color: #e5534b; }
|
||||
.erp-task-priority-medium { background: #fff7ed; color: #dd5b00; }
|
||||
.erp-task-priority-low { background: #ecfdf5; color: #1aae39; }
|
||||
|
||||
[data-theme='dark'] .erp-task-priority-high { background: rgba(185, 28, 28, 0.15); color: #FCA5A5; }
|
||||
[data-theme='dark'] .erp-task-priority-medium { background: rgba(146, 64, 14, 0.15); color: #FCD34D; }
|
||||
[data-theme='dark'] .erp-task-priority-low { background: rgba(4, 120, 87, 0.15); color: #6EE7B7; }
|
||||
[data-theme='dark'] .erp-task-priority-high { background: rgba(229, 83, 75, 0.15); color: #e5534b; }
|
||||
[data-theme='dark'] .erp-task-priority-medium { background: rgba(221, 91, 0, 0.15); color: #dd5b00; }
|
||||
[data-theme='dark'] .erp-task-priority-low { background: rgba(26, 174, 57, 0.15); color: #1aae39; }
|
||||
|
||||
/* Activity Timeline */
|
||||
.erp-activity-list {
|
||||
@@ -1143,12 +1189,12 @@ body {
|
||||
|
||||
.erp-activity-time {
|
||||
font-size: 11px;
|
||||
color: #64748B;
|
||||
color: #a39e98;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-activity-time {
|
||||
color: #94A3B8;
|
||||
color: #6d6862;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
|
||||
@@ -167,7 +167,7 @@ export default function Home() {
|
||||
title: '用户总数',
|
||||
value: stats.userCount,
|
||||
icon: <UserOutlined />,
|
||||
gradient: 'linear-gradient(135deg, #4F46E5, #6366F1)',
|
||||
gradient: 'linear-gradient(135deg, #0075de, #62aef0)',
|
||||
iconBg: 'rgba(79, 70, 229, 0.12)',
|
||||
delay: 'erp-fade-in erp-fade-in-delay-1',
|
||||
trend: { value: '+2', direction: 'up', label: '较上周' },
|
||||
@@ -179,7 +179,7 @@ export default function Home() {
|
||||
title: '角色数量',
|
||||
value: stats.roleCount,
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
gradient: 'linear-gradient(135deg, #059669, #10B981)',
|
||||
gradient: 'linear-gradient(135deg, #1aae39, #10B981)',
|
||||
iconBg: 'rgba(5, 150, 105, 0.12)',
|
||||
delay: 'erp-fade-in erp-fade-in-delay-2',
|
||||
trend: { value: '+1', direction: 'up', label: '较上月' },
|
||||
@@ -191,7 +191,7 @@ export default function Home() {
|
||||
title: '流程实例',
|
||||
value: stats.processInstanceCount,
|
||||
icon: <FileTextOutlined />,
|
||||
gradient: 'linear-gradient(135deg, #D97706, #F59E0B)',
|
||||
gradient: 'linear-gradient(135deg, #dd5b00, #F59E0B)',
|
||||
iconBg: 'rgba(217, 119, 6, 0.12)',
|
||||
delay: 'erp-fade-in erp-fade-in-delay-3',
|
||||
trend: { value: '0', direction: 'neutral', label: '较昨日' },
|
||||
@@ -213,18 +213,18 @@ export default function Home() {
|
||||
];
|
||||
|
||||
const quickActions = [
|
||||
{ icon: <UserOutlined />, label: '用户管理', path: '/users', color: '#4F46E5' },
|
||||
{ icon: <SafetyCertificateOutlined />, label: '权限管理', path: '/roles', color: '#059669' },
|
||||
{ icon: <ApartmentOutlined />, label: '组织架构', path: '/organizations', color: '#D97706' },
|
||||
{ icon: <UserOutlined />, label: '用户管理', path: '/users', color: '#0075de' },
|
||||
{ icon: <SafetyCertificateOutlined />, label: '权限管理', path: '/roles', color: '#1aae39' },
|
||||
{ icon: <ApartmentOutlined />, label: '组织架构', path: '/organizations', color: '#dd5b00' },
|
||||
{ icon: <PartitionOutlined />, label: '工作流', path: '/workflow', color: '#7C3AED' },
|
||||
{ icon: <BellOutlined />, label: '消息中心', path: '/messages', color: '#E11D48' },
|
||||
{ icon: <SettingOutlined />, label: '系统设置', path: '/settings', color: '#64748B' },
|
||||
{ icon: <SettingOutlined />, label: '系统设置', path: '/settings', color: '#615d59' },
|
||||
];
|
||||
|
||||
const pendingTasks: TaskItem[] = [
|
||||
{ id: '1', title: '审核新用户注册申请', priority: 'high', assignee: '系统', dueText: '待处理', color: '#DC2626', icon: <UserOutlined />, path: '/users' },
|
||||
{ id: '2', title: '配置工作流审批节点', priority: 'medium', assignee: '管理员', dueText: '进行中', color: '#D97706', icon: <PartitionOutlined />, path: '/workflow' },
|
||||
{ id: '3', title: '更新角色权限策略', priority: 'low', assignee: '管理员', dueText: '计划中', color: '#059669', icon: <SafetyCertificateOutlined />, path: '/roles' },
|
||||
{ id: '1', title: '审核新用户注册申请', priority: 'high', assignee: '系统', dueText: '待处理', color: '#e5534b', icon: <UserOutlined />, path: '/users' },
|
||||
{ id: '2', title: '配置工作流审批节点', priority: 'medium', assignee: '管理员', dueText: '进行中', color: '#dd5b00', icon: <PartitionOutlined />, path: '/workflow' },
|
||||
{ id: '3', title: '更新角色权限策略', priority: 'low', assignee: '管理员', dueText: '计划中', color: '#1aae39', icon: <SafetyCertificateOutlined />, path: '/roles' },
|
||||
];
|
||||
|
||||
const recentActivities: ActivityItem[] = [
|
||||
@@ -243,13 +243,13 @@ export default function Home() {
|
||||
<h2 style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: isDark ? '#F1F5F9' : '#0F172A',
|
||||
color: isDark ? '#f6f5f4' : 'rgba(0,0,0,0.95)',
|
||||
margin: '0 0 4px',
|
||||
letterSpacing: '-0.5px',
|
||||
}}>
|
||||
工作台
|
||||
</h2>
|
||||
<p style={{ fontSize: 14, color: isDark ? '#94A3B8' : '#475569', margin: 0 }}>
|
||||
<p style={{ fontSize: 14, color: isDark ? '#a39e98' : '#615d59', margin: 0 }}>
|
||||
欢迎回来,这是您的系统概览
|
||||
</p>
|
||||
</div>
|
||||
@@ -313,7 +313,7 @@ export default function Home() {
|
||||
<span style={{
|
||||
marginLeft: 'auto',
|
||||
fontSize: 12,
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
}}>
|
||||
{pendingTasks.length} 项待处理
|
||||
</span>
|
||||
@@ -340,7 +340,7 @@ export default function Home() {
|
||||
<span className={`erp-task-priority erp-task-priority-${task.priority}`}>
|
||||
{priorityLabel[task.priority]}
|
||||
</span>
|
||||
<RightOutlined style={{ color: isDark ? '#475569' : '#CBD5E1', fontSize: 12 }} />
|
||||
<RightOutlined style={{ color: isDark ? '#615d59' : '#CBD5E1', fontSize: 12 }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -351,7 +351,7 @@ export default function Home() {
|
||||
<Col xs={24} lg={10}>
|
||||
<div className="erp-content-card erp-fade-in erp-fade-in-delay-3" style={{ height: '100%' }}>
|
||||
<div className="erp-section-header">
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#6366F1' }} />
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#62aef0' }} />
|
||||
<span className="erp-section-title">最近动态</span>
|
||||
</div>
|
||||
<div className="erp-activity-list">
|
||||
@@ -400,7 +400,7 @@ export default function Home() {
|
||||
<Col xs={24} lg={8}>
|
||||
<div className="erp-content-card erp-fade-in erp-fade-in-delay-4" style={{ height: '100%' }}>
|
||||
<div className="erp-section-header">
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#6366F1' }} />
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#62aef0' }} />
|
||||
<span className="erp-section-title">系统信息</span>
|
||||
</div>
|
||||
<div className="erp-system-info-list">
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function Login() {
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
background: 'linear-gradient(135deg, #312E81 0%, #4F46E5 50%, #6366F1 100%)',
|
||||
background: 'linear-gradient(135deg, #312E81 0%, #0075de 50%, #62aef0 100%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
@@ -151,7 +151,7 @@ export default function Login() {
|
||||
<h2 style={{ marginBottom: 4, fontWeight: 700, fontSize: 24 }}>
|
||||
欢迎回来
|
||||
</h2>
|
||||
<p style={{ fontSize: 14, color: '#64748B' }}>
|
||||
<p style={{ fontSize: 14, color: '#615d59' }}>
|
||||
请登录您的账户以继续
|
||||
</p>
|
||||
|
||||
@@ -163,7 +163,7 @@ export default function Login() {
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined style={{ color: '#94A3B8' }} />}
|
||||
prefix={<UserOutlined style={{ color: '#a39e98' }} />}
|
||||
placeholder="用户名"
|
||||
style={{ height: 44, borderRadius: 10 }}
|
||||
/>
|
||||
@@ -173,7 +173,7 @@ export default function Login() {
|
||||
rules={[{ required: true, message: '请输入密码' }]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#94A3B8' }} />}
|
||||
prefix={<LockOutlined style={{ color: '#a39e98' }} />}
|
||||
placeholder="密码"
|
||||
style={{ height: 44, borderRadius: 10 }}
|
||||
/>
|
||||
@@ -197,7 +197,7 @@ export default function Login() {
|
||||
</Form>
|
||||
|
||||
<div style={{ marginTop: 32, textAlign: 'center' }}>
|
||||
<p style={{ fontSize: 12, color: '#64748B', margin: 0 }}>
|
||||
<p style={{ fontSize: 12, color: '#615d59', margin: 0 }}>
|
||||
ERP Platform v0.1.0 · Powered by Rust + React
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function Organizations() {
|
||||
const cardStyle = {
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
};
|
||||
|
||||
// --- Org tree state ---
|
||||
@@ -264,9 +264,9 @@ export default function Organizations() {
|
||||
{item.name}{' '}
|
||||
{item.code && <Tag style={{
|
||||
marginLeft: 4,
|
||||
background: isDark ? '#1E293B' : '#EEF2FF',
|
||||
background: isDark ? '#1e1e1d' : '#f2f9ff',
|
||||
border: 'none',
|
||||
color: '#4F46E5',
|
||||
color: '#0075de',
|
||||
fontSize: 11,
|
||||
}}>{item.code}</Tag>}
|
||||
</span>
|
||||
@@ -282,9 +282,9 @@ export default function Organizations() {
|
||||
{item.name}{' '}
|
||||
{item.code && <Tag style={{
|
||||
marginLeft: 4,
|
||||
background: isDark ? '#1E293B' : '#ECFDF5',
|
||||
background: isDark ? '#1e1e1d' : '#ECFDF5',
|
||||
border: 'none',
|
||||
color: '#059669',
|
||||
color: '#1aae39',
|
||||
fontSize: 11,
|
||||
}}>{item.code}</Tag>}
|
||||
</span>
|
||||
@@ -343,7 +343,7 @@ export default function Organizations() {
|
||||
<div className="erp-page-header">
|
||||
<div>
|
||||
<h4>
|
||||
<ApartmentOutlined style={{ marginRight: 8, color: '#4F46E5' }} />
|
||||
<ApartmentOutlined style={{ marginRight: 8, color: '#0075de' }} />
|
||||
组织架构管理
|
||||
</h4>
|
||||
<div className="erp-page-subtitle">管理组织、部门和岗位的层级结构</div>
|
||||
@@ -356,7 +356,7 @@ export default function Organizations() {
|
||||
<div style={{ width: 300, flexShrink: 0, ...cardStyle, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '14px 20px',
|
||||
borderBottom: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
@@ -418,7 +418,7 @@ export default function Organizations() {
|
||||
<div style={{ width: 300, flexShrink: 0, ...cardStyle, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '14px 20px',
|
||||
borderBottom: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
@@ -471,7 +471,7 @@ export default function Organizations() {
|
||||
<div style={{ flex: 1, ...cardStyle, overflow: 'hidden' }}>
|
||||
<div style={{
|
||||
padding: '14px 20px',
|
||||
borderBottom: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
|
||||
@@ -41,11 +41,11 @@ import {
|
||||
import PluginSettingsForm from '../components/PluginSettingsForm';
|
||||
|
||||
const STATUS_CONFIG: Record<PluginStatus, { color: string; label: string }> = {
|
||||
uploaded: { color: '#64748B', label: '已上传' },
|
||||
uploaded: { color: '#615d59', label: '已上传' },
|
||||
installed: { color: '#2563EB', label: '已安装' },
|
||||
enabled: { color: '#059669', label: '已启用' },
|
||||
running: { color: '#059669', label: '运行中' },
|
||||
disabled: { color: '#DC2626', label: '已禁用' },
|
||||
enabled: { color: '#1aae39', label: '已启用' },
|
||||
running: { color: '#1aae39', label: '运行中' },
|
||||
disabled: { color: '#e5534b', label: '已禁用' },
|
||||
uninstalled: { color: '#9333EA', label: '已卸载' },
|
||||
};
|
||||
|
||||
@@ -215,7 +215,7 @@ export default function PluginAdmin() {
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: PluginStatus) => {
|
||||
const cfg = STATUS_CONFIG[status] || { color: '#64748B', label: status };
|
||||
const cfg = STATUS_CONFIG[status] || { color: '#615d59', label: status };
|
||||
return <Tag color={cfg.color}>{cfg.label}</Tag>;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -299,7 +299,7 @@ export function PluginDashboardPage() {
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: isDark ? '#F1F5F9' : '#0F172A',
|
||||
color: isDark ? '#f6f5f4' : 'rgba(0,0,0,0.95)',
|
||||
margin: '0 0 4px',
|
||||
letterSpacing: '-0.5px',
|
||||
}}
|
||||
@@ -309,7 +309,7 @@ export function PluginDashboardPage() {
|
||||
<p
|
||||
style={{
|
||||
fontSize: 14,
|
||||
color: isDark ? '#94A3B8' : '#475569',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
@@ -352,7 +352,7 @@ export function PluginDashboardPage() {
|
||||
<div className="erp-section-header">
|
||||
<DashboardOutlined
|
||||
className="erp-section-icon"
|
||||
style={{ color: '#4F46E5' }}
|
||||
style={{ color: '#0075de' }}
|
||||
/>
|
||||
<span className="erp-section-title">图表分析</span>
|
||||
</div>
|
||||
@@ -389,7 +389,7 @@ export function PluginDashboardPage() {
|
||||
<div className="erp-section-header">
|
||||
<DashboardOutlined
|
||||
className="erp-section-icon"
|
||||
style={{ color: currentPalette.tagColor === 'purple' ? '#4F46E5' : '#3B82F6' }}
|
||||
style={{ color: currentPalette.tagColor === 'purple' ? '#0075de' : '#3B82F6' }}
|
||||
/>
|
||||
<span className="erp-section-title">
|
||||
{currentEntity?.display_name || selectedEntity} 数据分布
|
||||
|
||||
@@ -313,8 +313,8 @@ export function PluginGraphPage() {
|
||||
const r = degreeToRadius(degree, isCenter);
|
||||
|
||||
// Determine node color from its most common edge type, or default palette
|
||||
let nodeColorBase = '#4F46E5';
|
||||
let nodeColorLight = '#818CF8';
|
||||
let nodeColorBase = '#0075de';
|
||||
let nodeColorLight = '#62aef0';
|
||||
let nodeColorGlow = 'rgba(79,70,229,0.3)';
|
||||
|
||||
if (isCenter) {
|
||||
|
||||
@@ -37,12 +37,12 @@ import {
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
const CATEGORY_COLORS: Record<string, string> = {
|
||||
'财务': '#059669',
|
||||
'财务': '#1aae39',
|
||||
'CRM': '#2563EB',
|
||||
'进销存': '#9333EA',
|
||||
'生产': '#DC2626',
|
||||
'人力资源': '#D97706',
|
||||
'基础': '#64748B',
|
||||
'生产': '#e5534b',
|
||||
'人力资源': '#dd5b00',
|
||||
'基础': '#615d59',
|
||||
};
|
||||
|
||||
export default function PluginMarket() {
|
||||
@@ -190,7 +190,7 @@ export default function PluginMarket() {
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Text strong style={{ fontSize: 16 }}>{plugin.name}</Text>
|
||||
<Tag
|
||||
color={CATEGORY_COLORS[plugin.category ?? ''] ?? '#64748B'}
|
||||
color={CATEGORY_COLORS[plugin.category ?? ''] ?? '#615d59'}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
{plugin.category}
|
||||
@@ -244,7 +244,7 @@ export default function PluginMarket() {
|
||||
<div>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Space>
|
||||
<Tag color={CATEGORY_COLORS[selectedPlugin.category ?? ''] ?? '#64748B'}>
|
||||
<Tag color={CATEGORY_COLORS[selectedPlugin.category ?? ''] ?? '#615d59'}>
|
||||
{selectedPlugin.category}
|
||||
</Tag>
|
||||
<Text type="secondary">v{selectedPlugin.version}</Text>
|
||||
|
||||
@@ -153,12 +153,12 @@ export default function Roles() {
|
||||
height: 32,
|
||||
borderRadius: 8,
|
||||
background: record.is_system
|
||||
? 'linear-gradient(135deg, #4F46E5, #818CF8)'
|
||||
: isDark ? '#1E293B' : '#F1F5F9',
|
||||
? 'linear-gradient(135deg, #0075de, #62aef0)'
|
||||
: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: record.is_system ? '#fff' : isDark ? '#94A3B8' : '#64748B',
|
||||
color: record.is_system ? '#fff' : isDark ? '#a39e98' : '#615d59',
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
@@ -174,9 +174,9 @@ export default function Roles() {
|
||||
key: 'code',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -190,7 +190,7 @@ export default function Roles() {
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
render: (v: string | undefined) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8' }}>{v || '-'}</span>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98' }}>{v || '-'}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -201,8 +201,8 @@ export default function Roles() {
|
||||
render: (v: boolean) => (
|
||||
<Tag
|
||||
style={{
|
||||
color: v ? '#4F46E5' : (isDark ? '#94A3B8' : '#64748B'),
|
||||
background: v ? '#EEF2FF' : (isDark ? '#1E293B' : '#F1F5F9'),
|
||||
color: v ? '#0075de' : (isDark ? '#a39e98' : '#615d59'),
|
||||
background: v ? '#f2f9ff' : (isDark ? '#1e1e1d' : '#f6f5f4'),
|
||||
border: 'none',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
@@ -222,7 +222,7 @@ export default function Roles() {
|
||||
type="text"
|
||||
icon={<SafetyCertificateOutlined />}
|
||||
onClick={() => openPermModal(record)}
|
||||
style={{ color: '#4F46E5' }}
|
||||
style={{ color: '#0075de' }}
|
||||
>
|
||||
权限
|
||||
</Button>
|
||||
@@ -233,7 +233,7 @@ export default function Roles() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEditModal(record)}
|
||||
style={{ color: isDark ? '#94A3B8' : '#64748B' }}
|
||||
style={{ color: isDark ? '#a39e98' : '#615d59' }}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除此角色?"
|
||||
@@ -279,7 +279,7 @@ export default function Roles() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
@@ -336,8 +336,8 @@ export default function Roles() {
|
||||
marginBottom: 16,
|
||||
padding: 16,
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#F8FAFC',
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#fafaf9',
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
|
||||
@@ -35,9 +35,9 @@ import { listRoles, type RoleInfo } from '../api/roles';
|
||||
import type { UserInfo } from '../api/auth';
|
||||
|
||||
const STATUS_COLOR_MAP: Record<string, string> = {
|
||||
active: '#059669',
|
||||
disabled: '#DC2626',
|
||||
locked: '#D97706',
|
||||
active: '#1aae39',
|
||||
disabled: '#e5534b',
|
||||
locked: '#dd5b00',
|
||||
};
|
||||
|
||||
const STATUS_BG_MAP: Record<string, string> = {
|
||||
@@ -219,7 +219,7 @@ export default function Users() {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg, #4F46E5, #818CF8)',
|
||||
background: 'linear-gradient(135deg, #0075de, #62aef0)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
@@ -233,7 +233,7 @@ export default function Users() {
|
||||
<div>
|
||||
<div style={{ fontWeight: 500, fontSize: 14 }}>{v}</div>
|
||||
{record.display_name && (
|
||||
<div style={{ fontSize: 12, color: isDark ? '#64748B' : '#94A3B8' }}>
|
||||
<div style={{ fontSize: 12, color: isDark ? '#615d59' : '#a39e98' }}>
|
||||
{record.display_name}
|
||||
</div>
|
||||
)}
|
||||
@@ -261,8 +261,8 @@ export default function Users() {
|
||||
render: (status: string) => (
|
||||
<Tag
|
||||
style={{
|
||||
color: STATUS_COLOR_MAP[status] || '#64748B',
|
||||
background: STATUS_BG_MAP[status] || '#F1F5F9',
|
||||
color: STATUS_COLOR_MAP[status] || '#615d59',
|
||||
background: STATUS_BG_MAP[status] || '#f6f5f4',
|
||||
border: 'none',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
@@ -279,14 +279,14 @@ export default function Users() {
|
||||
roles.length > 0
|
||||
? roles.map((r) => (
|
||||
<Tag key={r.id} style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
color: isDark ? '#CBD5E1' : '#615d59',
|
||||
}}>
|
||||
{r.name}
|
||||
</Tag>
|
||||
))
|
||||
: <span style={{ color: isDark ? '#475569' : '#CBD5E1' }}>-</span>,
|
||||
: <span style={{ color: isDark ? '#615d59' : '#CBD5E1' }}>-</span>,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@@ -299,14 +299,14 @@ export default function Users() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEditModal(record)}
|
||||
style={{ color: isDark ? '#94A3B8' : '#64748B' }}
|
||||
style={{ color: isDark ? '#a39e98' : '#615d59' }}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<SafetyCertificateOutlined />}
|
||||
onClick={() => openRoleModal(record)}
|
||||
style={{ color: isDark ? '#94A3B8' : '#64748B' }}
|
||||
style={{ color: isDark ? '#a39e98' : '#615d59' }}
|
||||
/>
|
||||
{record.status === 'active' ? (
|
||||
<Popconfirm
|
||||
@@ -326,7 +326,7 @@ export default function Users() {
|
||||
type="text"
|
||||
icon={<CheckCircleOutlined />}
|
||||
onClick={() => handleToggleStatus(record.id, 'active')}
|
||||
style={{ color: '#059669' }}
|
||||
style={{ color: '#1aae39' }}
|
||||
/>
|
||||
)}
|
||||
<Popconfirm
|
||||
@@ -356,7 +356,7 @@ export default function Users() {
|
||||
<Space size={8}>
|
||||
<Input
|
||||
placeholder="搜索用户名..."
|
||||
prefix={<SearchOutlined style={{ color: '#94A3B8' }} />}
|
||||
prefix={<SearchOutlined style={{ color: '#a39e98' }} />}
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
@@ -379,7 +379,7 @@ export default function Users() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
@@ -415,7 +415,7 @@ export default function Users() {
|
||||
label="用户名"
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input prefix={<UserOutlined style={{ color: '#94A3B8' }} />} disabled={!!editUser} />
|
||||
<Input prefix={<UserOutlined style={{ color: '#a39e98' }} />} disabled={!!editUser} />
|
||||
</Form.Item>
|
||||
{!editUser && (
|
||||
<Form.Item
|
||||
@@ -465,13 +465,13 @@ export default function Users() {
|
||||
style={{
|
||||
padding: '10px 14px',
|
||||
borderRadius: 8,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#F8FAFC',
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#fafaf9',
|
||||
}}
|
||||
>
|
||||
<Checkbox value={r.id}>
|
||||
<span style={{ fontWeight: 500 }}>{r.name}</span>
|
||||
<span style={{ color: isDark ? '#475569' : '#94A3B8', marginLeft: 8, fontSize: 12 }}>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', marginLeft: 8, fontSize: 12 }}>
|
||||
{r.code}
|
||||
</span>
|
||||
</Checkbox>
|
||||
|
||||
@@ -46,7 +46,7 @@ function prepareChartData(data: WidgetData['data'], dimensionOrder?: string[]) {
|
||||
const TAG_COLOR_MAP: Record<string, string> = {
|
||||
blue: '#3B82F6', green: '#10B981', orange: '#F59E0B', red: '#EF4444',
|
||||
purple: '#8B5CF6', cyan: '#06B6D4', magenta: '#EC4899', gold: '#EAB308',
|
||||
lime: '#84CC16', geekblue: '#6366F1', volcano: '#F97316',
|
||||
lime: '#84CC16', geekblue: '#62aef0', volcano: '#F97316',
|
||||
};
|
||||
|
||||
function tagStrokeColor(color: string): string {
|
||||
@@ -204,7 +204,7 @@ export function SkeletonBreakdownCard({ index }: { index: number }) {
|
||||
function StatWidgetCard({ widgetData }: { widgetData: WidgetData }) {
|
||||
const { widget, count } = widgetData;
|
||||
const animatedValue = useCountUp(count ?? 0);
|
||||
const color = widget.color || '#4F46E5';
|
||||
const color = widget.color || '#0075de';
|
||||
return (
|
||||
<Card size="small" className="erp-fade-in" style={{ height: '100%' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
@@ -229,7 +229,7 @@ function StatWidgetCard({ widgetData }: { widgetData: WidgetData }) {
|
||||
function BarWidgetCard({ widgetData, isDark }: { widgetData: WidgetData; isDark: boolean }) {
|
||||
const { widget, data } = widgetData;
|
||||
const chartData = prepareChartData(data, widget.dimension_order);
|
||||
const axisLabelStyle = { fill: isDark ? '#94A3B8' : '#475569' };
|
||||
const axisLabelStyle = { fill: isDark ? '#a39e98' : '#615d59' };
|
||||
return (
|
||||
<WidgetCardShell title={widget.title} widgetType={widget.type}>
|
||||
{chartData.length > 0 ? (
|
||||
@@ -275,7 +275,7 @@ function FunnelWidgetCard({ widgetData }: { widgetData: WidgetData }) {
|
||||
function LineWidgetCard({ widgetData, isDark }: { widgetData: WidgetData; isDark: boolean }) {
|
||||
const { widget, data } = widgetData;
|
||||
const chartData = prepareChartData(data, widget.dimension_order);
|
||||
const axisLabelStyle = { fill: isDark ? '#94A3B8' : '#475569' };
|
||||
const axisLabelStyle = { fill: isDark ? '#a39e98' : '#615d59' };
|
||||
return (
|
||||
<WidgetCardShell title={widget.title} widgetType={widget.type}>
|
||||
{chartData.length > 0 ? (
|
||||
@@ -315,7 +315,7 @@ function StatCardsWidget({ widgetData }: { widgetData: WidgetData }) {
|
||||
{statCards.map((sc, i) => (
|
||||
<Col xs={12} sm={6} key={`${sc.card.entity}-${sc.card.label}-${i}`}>
|
||||
<div style={{
|
||||
background: `${sc.card.color || '#4F46E5'}10`,
|
||||
background: `${sc.card.color || '#0075de'}10`,
|
||||
borderRadius: 8,
|
||||
padding: '12px 16px',
|
||||
display: 'flex',
|
||||
@@ -324,9 +324,9 @@ function StatCardsWidget({ widgetData }: { widgetData: WidgetData }) {
|
||||
}}>
|
||||
<div style={{
|
||||
width: 36, height: 36, borderRadius: 8,
|
||||
background: `${sc.card.color || '#4F46E5'}20`,
|
||||
background: `${sc.card.color || '#0075de'}20`,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
color: sc.card.color || '#4F46E5', fontSize: 18,
|
||||
color: sc.card.color || '#0075de', fontSize: 18,
|
||||
}}>
|
||||
<DashboardOutlined />
|
||||
</div>
|
||||
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
// ── 通用调色板 ──
|
||||
|
||||
const UNIVERSAL_COLORS = [
|
||||
{ gradient: 'linear-gradient(135deg, #4F46E5, #6366F1)', iconBg: 'rgba(79, 70, 229, 0.12)', tagColor: 'purple' },
|
||||
{ gradient: 'linear-gradient(135deg, #059669, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', tagColor: 'green' },
|
||||
{ gradient: 'linear-gradient(135deg, #D97706, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', tagColor: 'orange' },
|
||||
{ gradient: 'linear-gradient(135deg, #0075de, #62aef0)', iconBg: 'rgba(79, 70, 229, 0.12)', tagColor: 'purple' },
|
||||
{ gradient: 'linear-gradient(135deg, #1aae39, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', tagColor: 'green' },
|
||||
{ gradient: 'linear-gradient(135deg, #dd5b00, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', tagColor: 'orange' },
|
||||
{ gradient: 'linear-gradient(135deg, #7C3AED, #A78BFA)', iconBg: 'rgba(124, 58, 237, 0.12)', tagColor: 'volcano' },
|
||||
{ gradient: 'linear-gradient(135deg, #E11D48, #F43F5E)', iconBg: 'rgba(225, 29, 72, 0.12)', tagColor: 'red' },
|
||||
{ gradient: 'linear-gradient(135deg, #0891B2, #06B6D4)', iconBg: 'rgba(8, 145, 178, 0.12)', tagColor: 'cyan' },
|
||||
|
||||
@@ -11,11 +11,11 @@ import type { GraphEdge } from './graphTypes';
|
||||
|
||||
/** 关系类型对应的色板 (base / light / glow) — 通用调色板自动分配 */
|
||||
const EDGE_PALETTE: Array<{ base: string; light: string; glow: string }> = [
|
||||
{ base: '#4F46E5', light: '#818CF8', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#059669', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#D97706', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#0075de', light: '#62aef0', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#1aae39', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#dd5b00', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' },
|
||||
{ base: '#DC2626', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#e5534b', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#7C3AED', light: '#A78BFA', glow: 'rgba(124,58,237,0.3)' },
|
||||
{ base: '#EA580C', light: '#FB923C', glow: 'rgba(234,88,12,0.3)' },
|
||||
{ base: '#DB2777', light: '#F472B6', glow: 'rgba(219,39,119,0.3)' },
|
||||
|
||||
@@ -5,9 +5,9 @@ import type { ColumnsType } from 'antd/es/table';
|
||||
import { listTemplates, createTemplate, type MessageTemplateInfo } from '../../api/messageTemplates';
|
||||
|
||||
const channelMap: Record<string, { label: string; color: string }> = {
|
||||
in_app: { label: '站内', color: '#4F46E5' },
|
||||
email: { label: '邮件', color: '#059669' },
|
||||
sms: { label: '短信', color: '#D97706' },
|
||||
in_app: { label: '站内', color: '#0075de' },
|
||||
email: { label: '邮件', color: '#1aae39' },
|
||||
sms: { label: '短信', color: '#dd5b00' },
|
||||
wechat: { label: '微信', color: '#7C3AED' },
|
||||
};
|
||||
|
||||
@@ -64,9 +64,9 @@ export default function MessageTemplates() {
|
||||
key: 'code',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -80,7 +80,7 @@ export default function MessageTemplates() {
|
||||
key: 'channel',
|
||||
width: 90,
|
||||
render: (c: string) => {
|
||||
const info = channelMap[c] || { label: c, color: '#64748B' };
|
||||
const info = channelMap[c] || { label: c, color: '#615d59' };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.color + '15',
|
||||
@@ -111,7 +111,7 @@ export default function MessageTemplates() {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>{v}</span>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>{v}</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -124,7 +124,7 @@ export default function MessageTemplates() {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#64748B' : '#94A3B8' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#615d59' : '#a39e98' }}>
|
||||
共 {total} 个模板
|
||||
</span>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => setModalOpen(true)}>
|
||||
@@ -135,7 +135,7 @@ export default function MessageTemplates() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -11,9 +11,9 @@ interface Props {
|
||||
}
|
||||
|
||||
const priorityStyles: Record<string, { bg: string; color: string; text: string }> = {
|
||||
urgent: { bg: '#FEF2F2', color: '#DC2626', text: '紧急' },
|
||||
important: { bg: '#FFFBEB', color: '#D97706', text: '重要' },
|
||||
normal: { bg: '#EEF2FF', color: '#4F46E5', text: '普通' },
|
||||
urgent: { bg: '#FEF2F2', color: '#e5534b', text: '紧急' },
|
||||
important: { bg: '#FFFBEB', color: '#dd5b00', text: '重要' },
|
||||
normal: { bg: '#f2f9ff', color: '#0075de', text: '普通' },
|
||||
};
|
||||
|
||||
export default function NotificationList({ queryFilter }: Props) {
|
||||
@@ -83,7 +83,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
content: (
|
||||
<div>
|
||||
<Paragraph>{record.body}</Paragraph>
|
||||
<div style={{ marginTop: 8, color: isDark ? '#475569' : '#94A3B8', fontSize: 12 }}>
|
||||
<div style={{ marginTop: 8, color: isDark ? '#615d59' : '#a39e98', fontSize: 12 }}>
|
||||
{record.created_at}
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,7 +104,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
style={{
|
||||
fontWeight: record.is_read ? 400 : 600,
|
||||
cursor: 'pointer',
|
||||
color: record.is_read ? (isDark ? '#94A3B8' : '#64748B') : 'inherit',
|
||||
color: record.is_read ? (isDark ? '#a39e98' : '#615d59') : 'inherit',
|
||||
}}
|
||||
onClick={() => showDetail(record)}
|
||||
>
|
||||
@@ -114,7 +114,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: '#4F46E5',
|
||||
background: '#0075de',
|
||||
marginRight: 8,
|
||||
}} />
|
||||
)}
|
||||
@@ -128,7 +128,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
key: 'priority',
|
||||
width: 90,
|
||||
render: (p: string) => {
|
||||
const info = priorityStyles[p] || { bg: '#F1F5F9', color: '#64748B', text: p };
|
||||
const info = priorityStyles[p] || { bg: '#f6f5f4', color: '#615d59', text: p };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.bg,
|
||||
@@ -146,7 +146,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
dataIndex: 'sender_type',
|
||||
key: 'sender_type',
|
||||
width: 80,
|
||||
render: (s: string) => <span style={{ color: isDark ? '#64748B' : '#94A3B8' }}>{s === 'system' ? '系统' : '用户'}</span>,
|
||||
render: (s: string) => <span style={{ color: isDark ? '#615d59' : '#a39e98' }}>{s === 'system' ? '系统' : '用户'}</span>,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -155,9 +155,9 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
width: 80,
|
||||
render: (r: boolean) => (
|
||||
<Tag style={{
|
||||
background: r ? (isDark ? '#1E293B' : '#F1F5F9') : '#EEF2FF',
|
||||
background: r ? (isDark ? '#1e1e1d' : '#f6f5f4') : '#f2f9ff',
|
||||
border: 'none',
|
||||
color: r ? (isDark ? '#64748B' : '#94A3B8') : '#4F46E5',
|
||||
color: r ? (isDark ? '#615d59' : '#a39e98') : '#0075de',
|
||||
fontWeight: 500,
|
||||
}}>
|
||||
{r ? '已读' : '未读'}
|
||||
@@ -170,7 +170,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>{v}</span>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>{v}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -185,7 +185,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={() => handleMarkRead(record.id)}
|
||||
style={{ color: '#4F46E5' }}
|
||||
style={{ color: '#0075de' }}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
@@ -193,7 +193,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
size="small"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => showDetail(record)}
|
||||
style={{ color: isDark ? '#64748B' : '#94A3B8' }}
|
||||
style={{ color: isDark ? '#615d59' : '#a39e98' }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
@@ -215,7 +215,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#64748B' : '#94A3B8' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#615d59' : '#a39e98' }}>
|
||||
共 {total} 条消息
|
||||
</span>
|
||||
<Button icon={<CheckOutlined />} onClick={handleMarkAllRead}>
|
||||
@@ -226,7 +226,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -48,12 +48,12 @@ export default function NotificationPreferences() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
padding: 24,
|
||||
maxWidth: 600,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 20 }}>
|
||||
<BellOutlined style={{ fontSize: 16, color: '#4F46E5' }} />
|
||||
<BellOutlined style={{ fontSize: 16, color: '#0075de' }} />
|
||||
<span style={{ fontSize: 15, fontWeight: 600 }}>通知偏好设置</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
// 通用边调色板
|
||||
const EDGE_PALETTE: Array<{ base: string; light: string; glow: string }> = [
|
||||
{ base: '#4F46E5', light: '#818CF8', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#059669', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#D97706', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#0075de', light: '#62aef0', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#1aae39', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#dd5b00', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' },
|
||||
{ base: '#DC2626', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#e5534b', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#7C3AED', light: '#A78BFA', glow: 'rgba(124,58,237,0.3)' },
|
||||
{ base: '#EA580C', light: '#FB923C', glow: 'rgba(234,88,12,0.3)' },
|
||||
{ base: '#DB2777', light: '#F472B6', glow: 'rgba(219,39,119,0.3)' },
|
||||
|
||||
@@ -295,8 +295,8 @@ export function drawFullGraph(
|
||||
const degree = degreeMap.get(node.id) || 0;
|
||||
const r = degreeToRadius(degree, isCenter);
|
||||
|
||||
let nodeColorBase = '#4F46E5';
|
||||
let nodeColorLight = '#818CF8';
|
||||
let nodeColorBase = '#0075de';
|
||||
let nodeColorLight = '#62aef0';
|
||||
let nodeColorGlow = 'rgba(79,70,229,0.3)';
|
||||
|
||||
if (isCenter) {
|
||||
|
||||
@@ -17,9 +17,9 @@ const RESOURCE_TYPE_OPTIONS = [
|
||||
];
|
||||
|
||||
const ACTION_STYLES: Record<string, { bg: string; color: string; text: string }> = {
|
||||
create: { bg: '#ECFDF5', color: '#059669', text: '创建' },
|
||||
update: { bg: '#EEF2FF', color: '#4F46E5', text: '更新' },
|
||||
delete: { bg: '#FEF2F2', color: '#DC2626', text: '删除' },
|
||||
create: { bg: '#ECFDF5', color: '#1aae39', text: '创建' },
|
||||
update: { bg: '#f2f9ff', color: '#0075de', text: '更新' },
|
||||
delete: { bg: '#FEF2F2', color: '#e5534b', text: '删除' },
|
||||
};
|
||||
|
||||
function formatDateTime(value: string): string {
|
||||
@@ -80,7 +80,7 @@ export default function AuditLogViewer() {
|
||||
key: 'action',
|
||||
width: 100,
|
||||
render: (action: string) => {
|
||||
const info = ACTION_STYLES[action] || { bg: '#F1F5F9', color: '#64748B', text: action };
|
||||
const info = ACTION_STYLES[action] || { bg: '#f6f5f4', color: '#615d59', text: action };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.bg,
|
||||
@@ -100,9 +100,9 @@ export default function AuditLogViewer() {
|
||||
width: 120,
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
color: isDark ? '#CBD5E1' : '#615d59',
|
||||
}}>
|
||||
{v}
|
||||
</Tag>
|
||||
@@ -115,7 +115,7 @@ export default function AuditLogViewer() {
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
render: (v: string) => (
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#94A3B8' : '#64748B' }}>
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#a39e98' : '#615d59' }}>
|
||||
{v}
|
||||
</span>
|
||||
),
|
||||
@@ -127,7 +127,7 @@ export default function AuditLogViewer() {
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
render: (v: string) => (
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#94A3B8' : '#64748B' }}>
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#a39e98' : '#615d59' }}>
|
||||
{v}
|
||||
</span>
|
||||
),
|
||||
@@ -138,7 +138,7 @@ export default function AuditLogViewer() {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (value: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>
|
||||
{formatDateTime(value)}
|
||||
</span>
|
||||
),
|
||||
@@ -156,7 +156,7 @@ export default function AuditLogViewer() {
|
||||
padding: 12,
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
}}>
|
||||
<Select
|
||||
allowClear
|
||||
@@ -173,7 +173,7 @@ export default function AuditLogViewer() {
|
||||
value={query.user_id ?? ''}
|
||||
onChange={(e) => handleFilterChange('user_id', e.target.value)}
|
||||
/>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#64748B' : '#94A3B8', marginLeft: 'auto' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#615d59' : '#a39e98', marginLeft: 'auto' }}>
|
||||
共 {total} 条日志
|
||||
</span>
|
||||
</div>
|
||||
@@ -182,7 +182,7 @@ export default function AuditLogViewer() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -132,9 +132,9 @@ export default function SystemSettings() {
|
||||
width: 250,
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
color: isDark ? '#CBD5E1' : '#615d59',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -162,7 +162,7 @@ export default function SystemSettings() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEdit(record)}
|
||||
style={{ color: isDark ? '#94A3B8' : '#64748B' }}
|
||||
style={{ color: isDark ? '#a39e98' : '#615d59' }}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除此设置?"
|
||||
@@ -191,7 +191,7 @@ export default function SystemSettings() {
|
||||
<Space>
|
||||
<Input
|
||||
placeholder="输入设置键名查询"
|
||||
prefix={<SearchOutlined style={{ color: '#94A3B8' }} />}
|
||||
prefix={<SearchOutlined style={{ color: '#a39e98' }} />}
|
||||
value={searchKey}
|
||||
onChange={(e) => setSearchKey(e.target.value)}
|
||||
onPressEnter={handleSearch}
|
||||
@@ -207,7 +207,7 @@ export default function SystemSettings() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -4,9 +4,9 @@ import type { ColumnsType } from 'antd/es/table';
|
||||
import { listCompletedTasks, type TaskInfo } from '../../api/workflowTasks';
|
||||
|
||||
const outcomeStyles: Record<string, { bg: string; color: string; text: string }> = {
|
||||
approved: { bg: '#ECFDF5', color: '#059669', text: '同意' },
|
||||
rejected: { bg: '#FEF2F2', color: '#DC2626', text: '拒绝' },
|
||||
delegated: { bg: '#EEF2FF', color: '#4F46E5', text: '已委派' },
|
||||
approved: { bg: '#ECFDF5', color: '#1aae39', text: '同意' },
|
||||
rejected: { bg: '#FEF2F2', color: '#e5534b', text: '拒绝' },
|
||||
delegated: { bg: '#f2f9ff', color: '#0075de', text: '已委派' },
|
||||
};
|
||||
|
||||
export default function CompletedTasks() {
|
||||
@@ -50,7 +50,7 @@ export default function CompletedTasks() {
|
||||
key: 'outcome',
|
||||
width: 100,
|
||||
render: (o: string) => {
|
||||
const info = outcomeStyles[o] || { bg: '#F1F5F9', color: '#64748B', text: o };
|
||||
const info = outcomeStyles[o] || { bg: '#f6f5f4', color: '#615d59', text: o };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.bg,
|
||||
@@ -69,7 +69,7 @@ export default function CompletedTasks() {
|
||||
key: 'completed_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>
|
||||
{v ? new Date(v).toLocaleString() : '-'}
|
||||
</span>
|
||||
),
|
||||
@@ -80,7 +80,7 @@ export default function CompletedTasks() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -13,10 +13,10 @@ import { getProcessDefinition, type NodeDef, type EdgeDef } from '../../api/work
|
||||
import ProcessViewer from './ProcessViewer';
|
||||
|
||||
const statusStyles: Record<string, { bg: string; color: string; text: string }> = {
|
||||
running: { bg: '#EEF2FF', color: '#4F46E5', text: '运行中' },
|
||||
suspended: { bg: '#FFFBEB', color: '#D97706', text: '已挂起' },
|
||||
completed: { bg: '#ECFDF5', color: '#059669', text: '已完成' },
|
||||
terminated: { bg: '#FEF2F2', color: '#DC2626', text: '已终止' },
|
||||
running: { bg: '#f2f9ff', color: '#0075de', text: '运行中' },
|
||||
suspended: { bg: '#FFFBEB', color: '#dd5b00', text: '已挂起' },
|
||||
completed: { bg: '#ECFDF5', color: '#1aae39', text: '已完成' },
|
||||
terminated: { bg: '#FEF2F2', color: '#e5534b', text: '已终止' },
|
||||
};
|
||||
|
||||
export default function InstanceMonitor() {
|
||||
@@ -129,7 +129,7 @@ export default function InstanceMonitor() {
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (s: string) => {
|
||||
const info = statusStyles[s] || { bg: '#F1F5F9', color: '#64748B', text: s };
|
||||
const info = statusStyles[s] || { bg: '#f6f5f4', color: '#615d59', text: s };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.bg,
|
||||
@@ -154,7 +154,7 @@ export default function InstanceMonitor() {
|
||||
key: 'started_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>
|
||||
{new Date(v).toLocaleString()}
|
||||
</span>
|
||||
),
|
||||
@@ -214,7 +214,7 @@ export default function InstanceMonitor() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -76,9 +76,9 @@ export default function PendingTasks() {
|
||||
key: 'business_key',
|
||||
render: (v: string | undefined) => v ? (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1e1e1d' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -93,9 +93,9 @@ export default function PendingTasks() {
|
||||
width: 100,
|
||||
render: (s: string) => (
|
||||
<Tag style={{
|
||||
background: '#EEF2FF',
|
||||
background: '#f2f9ff',
|
||||
border: 'none',
|
||||
color: '#4F46E5',
|
||||
color: '#0075de',
|
||||
fontWeight: 500,
|
||||
}}>
|
||||
{s}
|
||||
@@ -108,7 +108,7 @@ export default function PendingTasks() {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#64748B' : '#94A3B8', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#615d59' : '#a39e98', fontSize: 13 }}>
|
||||
{new Date(v).toLocaleString()}
|
||||
</span>
|
||||
),
|
||||
@@ -145,7 +145,7 @@ export default function PendingTasks() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1e1e1d' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
import ProcessDesigner from './ProcessDesigner';
|
||||
|
||||
const statusColors: Record<string, { bg: string; color: string; text: string }> = {
|
||||
draft: { bg: '#F1F5F9', color: '#64748B', text: '草稿' },
|
||||
published: { bg: '#ECFDF5', color: '#059669', text: '已发布' },
|
||||
deprecated: { bg: '#FEF2F2', color: '#DC2626', text: '已弃用' },
|
||||
draft: { bg: '#f6f5f4', color: '#615d59', text: '草稿' },
|
||||
published: { bg: '#ecfdf5', color: '#1aae39', text: '已发布' },
|
||||
deprecated: { bg: '#fef2f2', color: '#e5534b', text: '已弃用' },
|
||||
};
|
||||
|
||||
export default function ProcessDefinitions() {
|
||||
@@ -92,9 +92,9 @@ export default function ProcessDefinitions() {
|
||||
key: 'key',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#F1F5F9',
|
||||
background: isDark ? '#1E293B' : '#f6f5f4',
|
||||
border: 'none',
|
||||
color: isDark ? '#94A3B8' : '#64748B',
|
||||
color: isDark ? '#a39e98' : '#615d59',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -110,7 +110,7 @@ export default function ProcessDefinitions() {
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (s: string) => {
|
||||
const info = statusColors[s] || { bg: '#F1F5F9', color: '#64748B', text: s };
|
||||
const info = statusColors[s] || { bg: '#f6f5f4', color: '#615d59', text: s };
|
||||
return (
|
||||
<Tag style={{
|
||||
background: info.bg,
|
||||
@@ -152,7 +152,7 @@ export default function ProcessDefinitions() {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#64748B' : '#94A3B8' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#615d59' : '#a39e98' }}>
|
||||
共 {total} 个流程定义
|
||||
</span>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={handleCreate}>
|
||||
@@ -163,7 +163,7 @@ export default function ProcessDefinitions() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#f6f5f4'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
Reference in New Issue
Block a user