feat(web): 采用 UI UX Pro Max Soft UI Evolution 设计系统
从 Pinterest 风格切换到 Soft UI Evolution 设计系统,使用 UI UX Pro Max 推理引擎生成适合跨行业 ERP 业务用户的专业设计方案。 设计变更: - 主色从 Pinterest Red (#e60023) 切换到 Trust Blue (#2563EB) - 字体从系统默认切换到 Noto Sans SC(中文优先) - 圆角从 16-20px 调整到 10-12px(专业但不夸张) - 中性色从暖橄榄调切换到 Slate 石板蓝调 - 成功色 #103c25 → #059669,警告色 #b56e1a → #d97706 - 暗色模式从暖黑 (#1a1a18) 切换到深海军蓝 (#0f172a) 涉及文件:DESIGN.md + index.css + App.tsx + 24 个组件文件
This commit is contained in:
@@ -31,27 +31,27 @@ function PrivateRoute({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const themeConfig = {
|
||||
token: {
|
||||
colorPrimary: '#e60023',
|
||||
colorSuccess: '#103c25',
|
||||
colorWarning: '#b56e1a',
|
||||
colorError: '#9e0a0a',
|
||||
colorInfo: '#435ee5',
|
||||
colorBgLayout: '#f6f6f3',
|
||||
colorPrimary: '#2563eb',
|
||||
colorSuccess: '#059669',
|
||||
colorWarning: '#d97706',
|
||||
colorError: '#dc2626',
|
||||
colorInfo: '#0284c7',
|
||||
colorBgLayout: '#f8fafc',
|
||||
colorBgContainer: '#ffffff',
|
||||
colorBgElevated: '#ffffff',
|
||||
colorBorder: '#e5e5e0',
|
||||
colorBorderSecondary: '#e0e0d9',
|
||||
borderRadius: 16,
|
||||
borderRadiusLG: 20,
|
||||
borderRadiusSM: 8,
|
||||
fontFamily: "-apple-system, system-ui, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', Helvetica, Arial, sans-serif",
|
||||
colorBorder: '#e2e8f0',
|
||||
colorBorderSecondary: '#f1f5f9',
|
||||
borderRadius: 10,
|
||||
borderRadiusLG: 12,
|
||||
borderRadiusSM: 6,
|
||||
fontFamily: "'Noto Sans SC', -apple-system, system-ui, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', Helvetica, Arial, sans-serif",
|
||||
fontSize: 14,
|
||||
fontSizeHeading4: 20,
|
||||
controlHeight: 40,
|
||||
controlHeightLG: 44,
|
||||
controlHeightSM: 32,
|
||||
boxShadow: 'none',
|
||||
boxShadowSecondary: '0 2px 8px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04)',
|
||||
boxShadowSecondary: '0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)',
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
@@ -62,21 +62,21 @@ const themeConfig = {
|
||||
paddingLG: 20,
|
||||
},
|
||||
Table: {
|
||||
headerBg: '#fafaf8',
|
||||
headerColor: '#62625b',
|
||||
rowHoverBg: '#fef0f0',
|
||||
headerBg: '#f1f5f9',
|
||||
headerColor: '#475569',
|
||||
rowHoverBg: '#f1f5f9',
|
||||
fontSize: 14,
|
||||
},
|
||||
Menu: {
|
||||
itemBorderRadius: 12,
|
||||
itemBorderRadius: 10,
|
||||
itemMarginInline: 8,
|
||||
itemHeight: 40,
|
||||
},
|
||||
Modal: {
|
||||
borderRadiusLG: 28,
|
||||
borderRadiusLG: 16,
|
||||
},
|
||||
Tag: {
|
||||
borderRadiusSM: 8,
|
||||
borderRadiusSM: 6,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -85,20 +85,20 @@ const darkThemeConfig = {
|
||||
...themeConfig,
|
||||
token: {
|
||||
...themeConfig.token,
|
||||
colorBgLayout: '#1a1a18',
|
||||
colorBgContainer: '#2a2a28',
|
||||
colorBgElevated: '#33332e',
|
||||
colorBorder: 'rgba(255, 255, 255, 0.08)',
|
||||
colorBgLayout: '#0f172a',
|
||||
colorBgContainer: '#1e293b',
|
||||
colorBgElevated: '#334155',
|
||||
colorBorder: '#334155',
|
||||
colorBorderSecondary: 'rgba(255, 255, 255, 0.06)',
|
||||
boxShadow: 'none',
|
||||
boxShadowSecondary: '0 4px 16px rgba(0,0,0,0.2), 0 2px 6px rgba(0,0,0,0.15)',
|
||||
boxShadowSecondary: '0 2px 8px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2)',
|
||||
},
|
||||
components: {
|
||||
...themeConfig.components,
|
||||
Table: {
|
||||
headerBg: '#33332e',
|
||||
headerColor: '#91918c',
|
||||
rowHoverBg: '#33332e',
|
||||
headerBg: '#1e293b',
|
||||
headerColor: '#94a3b8',
|
||||
rowHoverBg: '#1e293b',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function NotificationPanel() {
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
style={{ fontSize: 12, color: '#e60023' }}
|
||||
style={{ fontSize: 12, color: '#2563eb' }}
|
||||
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 ? '#211922' : '#fef0f0') : 'transparent',
|
||||
background: !item.is_read ? (isDark ? '#0f172a' : '#eff6ff') : '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 ? '#211922' : '#fafaf8';
|
||||
e.currentTarget.style.background = isDark ? '#0f172a' : '#f1f5f9';
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
@@ -109,7 +109,7 @@ export default function NotificationPanel() {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: '#e60023',
|
||||
background: '#2563eb',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
@@ -132,12 +132,12 @@ export default function NotificationPanel() {
|
||||
textAlign: 'center',
|
||||
paddingTop: 8,
|
||||
marginTop: 4,
|
||||
borderTop: `1px solid ${isDark ? '#211922' : '#f6f6f3'}`,
|
||||
borderTop: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
}}>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => navigate('/messages')}
|
||||
style={{ fontSize: 13, color: '#e60023', fontWeight: 500 }}
|
||||
style={{ fontSize: 13, color: '#2563eb', fontWeight: 500 }}
|
||||
>
|
||||
查看全部消息
|
||||
</Button>
|
||||
@@ -166,7 +166,7 @@ export default function NotificationPanel() {
|
||||
position: 'relative',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = isDark ? '#211922' : '#f6f6f3';
|
||||
e.currentTarget.style.background = isDark ? '#0f172a' : '#f8fafc';
|
||||
}}
|
||||
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 ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
}} />
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -2,65 +2,66 @@
|
||||
|
||||
/* ====================================================================
|
||||
* ERP Platform — Design System Tokens & Global Styles
|
||||
* Inspired by Pinterest: warm discovery, red accent, generous radius
|
||||
* Soft UI Evolution: Professional, warm, accessible for all industries
|
||||
* Generated by UI UX Pro Max
|
||||
* ==================================================================== */
|
||||
|
||||
/* --- Design Tokens (CSS Custom Properties) --- */
|
||||
:root {
|
||||
/* Primary Palette — Pinterest Red */
|
||||
--erp-primary: #e60023;
|
||||
--erp-primary-hover: #ad081b;
|
||||
--erp-primary-active: #9e0a0a;
|
||||
--erp-primary-light: #fef0f0;
|
||||
--erp-primary-light-hover: #fddbdb;
|
||||
--erp-primary-bg-subtle: #fef0f0;
|
||||
/* Primary Palette — Trust Blue */
|
||||
--erp-primary: #2563eb;
|
||||
--erp-primary-hover: #1d4ed8;
|
||||
--erp-primary-active: #1e40af;
|
||||
--erp-primary-light: #eff6ff;
|
||||
--erp-primary-light-hover: #dbeafe;
|
||||
--erp-primary-bg-subtle: #eff6ff;
|
||||
|
||||
/* Semantic Colors — Pinterest warm tones */
|
||||
--erp-success: #103c25;
|
||||
/* Semantic Colors — Professional slate tones */
|
||||
--erp-success: #059669;
|
||||
--erp-success-bg: #ecfdf5;
|
||||
--erp-warning: #b56e1a;
|
||||
--erp-warning-bg: #fff7ed;
|
||||
--erp-error: #9e0a0a;
|
||||
--erp-warning: #d97706;
|
||||
--erp-warning-bg: #fffbeb;
|
||||
--erp-error: #dc2626;
|
||||
--erp-error-bg: #fef2f2;
|
||||
--erp-info: #435ee5;
|
||||
--erp-info-bg: #eef1fd;
|
||||
--erp-info: #0284c7;
|
||||
--erp-info-bg: #f0f9ff;
|
||||
|
||||
/* Neutral Palette — Warm neutrals with olive/sand undertones */
|
||||
--erp-bg-page: #f6f6f3;
|
||||
/* Neutral Palette — Slate neutrals with blue undertones */
|
||||
--erp-bg-page: #f8fafc;
|
||||
--erp-bg-container: #ffffff;
|
||||
--erp-bg-elevated: #ffffff;
|
||||
--erp-bg-spotlight: #fafaf8;
|
||||
--erp-bg-spotlight: #f1f5f9;
|
||||
--erp-bg-sidebar: #ffffff;
|
||||
--erp-bg-sidebar-hover: #f6f6f3;
|
||||
--erp-bg-sidebar-active: #fef0f0;
|
||||
--erp-bg-sidebar-hover: #f1f5f9;
|
||||
--erp-bg-sidebar-active: #eff6ff;
|
||||
|
||||
/* Text Colors — Warm near-black */
|
||||
--erp-text-primary: #211922;
|
||||
--erp-text-secondary: #62625b;
|
||||
--erp-text-tertiary: #91918c;
|
||||
/* Text Colors — Deep navy */
|
||||
--erp-text-primary: #0f172a;
|
||||
--erp-text-secondary: #475569;
|
||||
--erp-text-tertiary: #94a3b8;
|
||||
--erp-text-inverse: #ffffff;
|
||||
--erp-text-sidebar: #62625b;
|
||||
--erp-text-sidebar-active: #e60023;
|
||||
--erp-text-sidebar: #475569;
|
||||
--erp-text-sidebar-active: #2563eb;
|
||||
|
||||
/* Border Colors — Whisper borders */
|
||||
--erp-border: #e5e5e0;
|
||||
--erp-border-light: #e0e0d9;
|
||||
--erp-border-dark: #c8c8c1;
|
||||
/* Border Colors — Slate borders */
|
||||
--erp-border: #e2e8f0;
|
||||
--erp-border-light: #f1f5f9;
|
||||
--erp-border-dark: #cbd5e1;
|
||||
|
||||
/* Shadows — Multi-layer, ultra-subtle */
|
||||
--erp-shadow-xs: 0 1px 2px rgba(0,0,0,0.03);
|
||||
--erp-shadow-sm: 0 2px 8px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
|
||||
--erp-shadow-md: 0 4px 16px rgba(0,0,0,0.07), 0 2px 6px rgba(0,0,0,0.04);
|
||||
--erp-shadow-lg: 0 8px 24px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.05);
|
||||
--erp-shadow-xl: 0 12px 36px rgba(0,0,0,0.1), 0 6px 18px rgba(0,0,0,0.06);
|
||||
/* Shadows — Soft UI Evolution: subtle, layered depth */
|
||||
--erp-shadow-xs: 0 1px 2px rgba(0,0,0,0.05);
|
||||
--erp-shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
|
||||
--erp-shadow-md: 0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.05);
|
||||
--erp-shadow-lg: 0 8px 30px rgba(0,0,0,0.12);
|
||||
--erp-shadow-xl: 0 12px 40px rgba(0,0,0,0.15);
|
||||
|
||||
/* Radius — Notion subtle roundness */
|
||||
--erp-radius-sm: 8px;
|
||||
--erp-radius-md: 16px;
|
||||
--erp-radius-lg: 20px;
|
||||
--erp-radius-xl: 28px;
|
||||
/* Radius — Soft UI: friendly but professional */
|
||||
--erp-radius-sm: 6px;
|
||||
--erp-radius-md: 10px;
|
||||
--erp-radius-lg: 12px;
|
||||
--erp-radius-xl: 16px;
|
||||
|
||||
/* Spacing — 8px base unit */
|
||||
/* Spacing — 4px base unit */
|
||||
--erp-space-xs: 4px;
|
||||
--erp-space-sm: 8px;
|
||||
--erp-space-md: 16px;
|
||||
@@ -68,11 +69,10 @@
|
||||
--erp-space-xl: 32px;
|
||||
--erp-space-2xl: 48px;
|
||||
|
||||
/* 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;
|
||||
/* Typography — Noto Sans SC for Chinese-first ERP */
|
||||
--erp-font-family: 'Noto Sans SC', -apple-system, system-ui, 'Segoe UI', Roboto,
|
||||
'PingFang SC', 'Microsoft YaHei', Helvetica, Arial, sans-serif;
|
||||
--erp-font-mono: 'JetBrains Mono', 'Fira Code', Consolas, Monaco, monospace;
|
||||
--erp-font-size-xs: 12px;
|
||||
--erp-font-size-sm: 13px;
|
||||
--erp-font-size-base: 14px;
|
||||
@@ -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 — Warm */
|
||||
--erp-trend-up: #103c25;
|
||||
--erp-trend-down: #9e0a0a;
|
||||
--erp-trend-neutral: #62625b;
|
||||
/* Trend Colors */
|
||||
--erp-trend-up: #059669;
|
||||
--erp-trend-down: #dc2626;
|
||||
--erp-trend-neutral: #475569;
|
||||
|
||||
/* Line Height */
|
||||
--erp-line-height-tight: 1.25;
|
||||
@@ -102,48 +102,48 @@
|
||||
--erp-header-height: 56px;
|
||||
}
|
||||
|
||||
/* --- Dark Mode Tokens — Warm dark, Notion-inspired --- */
|
||||
/* --- Dark Mode Tokens --- */
|
||||
[data-theme='dark'] {
|
||||
--erp-primary-light: rgba(230, 0, 35, 0.15);
|
||||
--erp-primary-light-hover: rgba(230, 0, 35, 0.22);
|
||||
--erp-primary-bg-subtle: rgba(230, 0, 35, 0.1);
|
||||
--erp-primary-light: rgba(37, 99, 235, 0.15);
|
||||
--erp-primary-light-hover: rgba(37, 99, 235, 0.22);
|
||||
--erp-primary-bg-subtle: rgba(37, 99, 235, 0.1);
|
||||
|
||||
--erp-bg-page: #1a1a18;
|
||||
--erp-bg-container: #2a2a28;
|
||||
--erp-bg-elevated: #33332e;
|
||||
--erp-bg-spotlight: #33332e;
|
||||
--erp-bg-sidebar: #211922;
|
||||
--erp-bg-sidebar-hover: #33332e;
|
||||
--erp-bg-page: #0f172a;
|
||||
--erp-bg-container: #1e293b;
|
||||
--erp-bg-elevated: #334155;
|
||||
--erp-bg-spotlight: #1e293b;
|
||||
--erp-bg-sidebar: #0f172a;
|
||||
--erp-bg-sidebar-hover: #1e293b;
|
||||
|
||||
--erp-text-primary: rgba(255, 255, 255, 0.95);
|
||||
--erp-text-secondary: #91918c;
|
||||
--erp-text-tertiary: #62625b;
|
||||
--erp-text-sidebar: #91918c;
|
||||
--erp-text-sidebar-active: #f05a5a;
|
||||
--erp-text-secondary: #94a3b8;
|
||||
--erp-text-tertiary: #64748b;
|
||||
--erp-text-sidebar: #94a3b8;
|
||||
--erp-text-sidebar-active: #60a5fa;
|
||||
|
||||
--erp-border: rgba(255, 255, 255, 0.08);
|
||||
--erp-border-light: rgba(255, 255, 255, 0.05);
|
||||
--erp-border: #334155;
|
||||
--erp-border-light: rgba(255, 255, 255, 0.06);
|
||||
--erp-border-dark: rgba(255, 255, 255, 0.12);
|
||||
|
||||
--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-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
--erp-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3), 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
--erp-shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.4);
|
||||
|
||||
--erp-trend-up: #3db377;
|
||||
--erp-trend-down: #9e0a0a;
|
||||
--erp-trend-neutral: #91918c;
|
||||
--erp-trend-up: #34d399;
|
||||
--erp-trend-down: #f87171;
|
||||
--erp-trend-neutral: #94a3b8;
|
||||
|
||||
--erp-success-bg: rgba(16, 60, 37, 0.15);
|
||||
--erp-warning-bg: rgba(181, 110, 26, 0.15);
|
||||
--erp-error-bg: rgba(158, 10, 10, 0.15);
|
||||
--erp-info-bg: rgba(67, 94, 229, 0.15);
|
||||
--erp-success-bg: rgba(5, 150, 105, 0.15);
|
||||
--erp-warning-bg: rgba(217, 119, 6, 0.15);
|
||||
--erp-error-bg: rgba(220, 38, 38, 0.15);
|
||||
--erp-info-bg: rgba(2, 132, 199, 0.15);
|
||||
}
|
||||
|
||||
[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: #91918c; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-label { color: #91918c; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-up { color: #34d399; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-down { color: #f87171; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-neutral { color: #94a3b8; }
|
||||
[data-theme='dark'] .erp-stat-card-trend-label { color: #94a3b8; }
|
||||
|
||||
/* --- Global Reset & Base --- */
|
||||
body {
|
||||
@@ -182,7 +182,7 @@ body {
|
||||
|
||||
/* --- Selection --- */
|
||||
::selection {
|
||||
background-color: rgba(230, 0, 35, 0.15);
|
||||
background-color: rgba(37, 99, 235, 0.15);
|
||||
color: var(--erp-text-primary);
|
||||
}
|
||||
|
||||
@@ -190,11 +190,11 @@ body {
|
||||
* Component Overrides — Ant Design Enhancement
|
||||
* ==================================================================== */
|
||||
|
||||
/* --- Card — Whisper border, soft shadow --- */
|
||||
/* --- Card — Soft shadow, clean border --- */
|
||||
.ant-card {
|
||||
border-radius: var(--erp-radius-lg) !important;
|
||||
border: 1px solid var(--erp-border) !important;
|
||||
box-shadow: none !important;
|
||||
box-shadow: var(--erp-shadow-xs) !important;
|
||||
transition: box-shadow var(--erp-transition-base) !important;
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ body {
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr:hover > td {
|
||||
background: var(--erp-primary-bg-subtle) !important;
|
||||
background: var(--erp-bg-spotlight) !important;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr > td {
|
||||
@@ -270,9 +270,9 @@ body {
|
||||
border-bottom: 1px solid var(--erp-border-light) !important;
|
||||
}
|
||||
|
||||
/* --- Button — Subtle 4px radius, no heavy shadow --- */
|
||||
/* --- Button --- */
|
||||
.ant-btn-primary {
|
||||
border-radius: 16px !important;
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
font-weight: 500 !important;
|
||||
box-shadow: none !important;
|
||||
transition: all var(--erp-transition-fast) !important;
|
||||
@@ -283,16 +283,16 @@ body {
|
||||
}
|
||||
|
||||
.ant-btn-default {
|
||||
border-radius: 16px !important;
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
/* --- Input — 4px radius, whisper border --- */
|
||||
/* --- Input --- */
|
||||
.ant-input,
|
||||
.ant-input-affix-wrapper,
|
||||
.ant-select-selector,
|
||||
.ant-picker {
|
||||
border-radius: 16px !important;
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
transition: all var(--erp-transition-fast) !important;
|
||||
}
|
||||
|
||||
@@ -307,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(230, 0, 35, 0.12) !important;
|
||||
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.12) !important;
|
||||
}
|
||||
|
||||
/* --- Modal --- */
|
||||
@@ -436,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, #e60023, #f05a5a); }
|
||||
.erp-gradient-card.emerald::before { background: linear-gradient(90deg, #103c25, #3db377); }
|
||||
.erp-gradient-card.amber::before { background: linear-gradient(90deg, #b56e1a, #fbbf24); }
|
||||
.erp-gradient-card.rose::before { background: linear-gradient(90deg, #9e0a0a, #f05a5a); }
|
||||
.erp-gradient-card.sky::before { background: linear-gradient(90deg, #435ee5, #8fa4f0); }
|
||||
.erp-gradient-card.violet::before { background: linear-gradient(90deg, #6845ab, #a78bfa); }
|
||||
.erp-gradient-card.indigo::before { background: linear-gradient(90deg, #2563eb, #60a5fa); }
|
||||
.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, #dc2626, #f87171); }
|
||||
.erp-gradient-card.sky::before { background: linear-gradient(90deg, #0284c7, #38bdf8); }
|
||||
.erp-gradient-card.violet::before { background: linear-gradient(90deg, #7c3aed, #a78bfa); }
|
||||
|
||||
/* --- Fade-in Animation --- */
|
||||
@keyframes erp-fade-in {
|
||||
@@ -475,7 +475,7 @@ body {
|
||||
*:focus-visible {
|
||||
outline: 2px solid var(--erp-primary);
|
||||
outline-offset: 2px;
|
||||
border-radius: 16px;
|
||||
border-radius: var(--erp-radius-sm);
|
||||
}
|
||||
|
||||
.erp-sidebar-item:focus-visible {
|
||||
@@ -491,7 +491,7 @@ body {
|
||||
background: var(--erp-primary);
|
||||
color: #fff;
|
||||
padding: 8px 24px;
|
||||
border-radius: 0 0 16px 16px;
|
||||
border-radius: 0 0 var(--erp-radius-md) var(--erp-radius-md);
|
||||
z-index: 10000;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
@@ -540,22 +540,22 @@ body {
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item {
|
||||
margin: 1px 8px !important;
|
||||
border-radius: 16px !important;
|
||||
border-radius: var(--erp-radius-md) !important;
|
||||
height: 36px !important;
|
||||
line-height: 36px !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item-selected {
|
||||
background: #fef0f0 !important;
|
||||
color: #e60023 !important;
|
||||
background: #eff6ff !important;
|
||||
color: #2563eb !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item-selected .anticon {
|
||||
color: #e60023 !important;
|
||||
color: #2563eb !important;
|
||||
}
|
||||
|
||||
.erp-sidebar-menu .ant-menu-item:not(.ant-menu-item-selected):hover {
|
||||
background: #f6f6f3 !important;
|
||||
background: #f1f5f9 !important;
|
||||
}
|
||||
|
||||
/* Sidebar group label */
|
||||
@@ -565,17 +565,17 @@ body {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
color: #91918c;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* MainLayout — CSS classes replacing inline styles
|
||||
* ==================================================================== */
|
||||
|
||||
/* Sider — White sidebar, Notion style */
|
||||
/* Sider — White sidebar, Soft UI style */
|
||||
.erp-sider-dark {
|
||||
background: #ffffff !important;
|
||||
border-right: 1px solid #e0e0d9 !important;
|
||||
border-right: 1px solid #e2e8f0 !important;
|
||||
position: fixed !important;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@@ -585,22 +585,22 @@ body {
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sider-dark {
|
||||
background: #211922 !important;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.06) !important;
|
||||
background: #0f172a !important;
|
||||
border-right: 1px solid #334155 !important;
|
||||
}
|
||||
|
||||
/* Logo — Warm neutral, Notion style */
|
||||
/* Logo */
|
||||
.erp-sidebar-logo {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
border-bottom: 1px solid #e0e0d9;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-logo {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
|
||||
.ant-layout-sider-collapsed .erp-sidebar-logo {
|
||||
@@ -611,8 +611,8 @@ body {
|
||||
.erp-sidebar-logo-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 16px;
|
||||
background: #e60023;
|
||||
border-radius: var(--erp-radius-sm);
|
||||
background: #2563eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -624,7 +624,7 @@ body {
|
||||
|
||||
.erp-sidebar-logo-text {
|
||||
margin-left: 10px;
|
||||
color: #211922;
|
||||
color: #0f172a;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.3px;
|
||||
@@ -635,16 +635,16 @@ body {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
/* Sidebar menu item — White sidebar, warm text */
|
||||
/* Sidebar menu item */
|
||||
.erp-sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 36px;
|
||||
margin: 1px 8px;
|
||||
padding: 0 12px;
|
||||
border-radius: 16px;
|
||||
border-radius: var(--erp-radius-md);
|
||||
cursor: pointer;
|
||||
color: #62625b;
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -657,28 +657,28 @@ body {
|
||||
}
|
||||
|
||||
.erp-sidebar-item:hover:not(.erp-sidebar-item-active) {
|
||||
background: #f6f6f3;
|
||||
color: #211922;
|
||||
background: #f1f5f9;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item {
|
||||
color: #91918c;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item:hover:not(.erp-sidebar-item-active) {
|
||||
background: #33332e;
|
||||
background: #1e293b;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.erp-sidebar-item-active {
|
||||
background: #fef0f0;
|
||||
color: #e60023;
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-item-active {
|
||||
background: rgba(230, 0, 35, 0.15);
|
||||
color: #f05a5a;
|
||||
background: rgba(37, 99, 235, 0.15);
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.erp-sidebar-item-icon {
|
||||
@@ -691,16 +691,16 @@ body {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/* Sidebar sub-menu (plugin group) — Warm gray group headers */
|
||||
/* Sidebar sub-menu (plugin group) */
|
||||
.erp-sidebar-submenu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
margin: 6px 8px 2px 8px;
|
||||
padding: 0 12px;
|
||||
border-radius: 16px;
|
||||
border-radius: var(--erp-radius-md);
|
||||
cursor: pointer;
|
||||
color: #91918c;
|
||||
color: #94a3b8;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
@@ -710,25 +710,25 @@ body {
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-title:hover {
|
||||
background: #f6f6f3;
|
||||
color: #62625b;
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title {
|
||||
color: #62625b;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title:hover {
|
||||
background: #33332e;
|
||||
color: #91918c;
|
||||
background: #1e293b;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-title-active {
|
||||
color: #e60023;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-sidebar-submenu-title-active {
|
||||
color: #f05a5a;
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.erp-sidebar-submenu-arrow {
|
||||
@@ -753,10 +753,10 @@ body {
|
||||
transition: margin-left 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.erp-main-layout-light { background: #f6f6f3; }
|
||||
.erp-main-layout-dark { background: #1a1a18; }
|
||||
.erp-main-layout-light { background: #f8fafc; }
|
||||
.erp-main-layout-dark { background: #0f172a; }
|
||||
|
||||
/* Header — Clean white, whisper border bottom */
|
||||
/* Header */
|
||||
.erp-header {
|
||||
height: 56px !important;
|
||||
padding: 0 24px !important;
|
||||
@@ -771,43 +771,43 @@ body {
|
||||
|
||||
.erp-header-light {
|
||||
background: #ffffff !important;
|
||||
border-bottom: 1px solid #e0e0d9;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.erp-header-dark {
|
||||
background: #2a2a28 !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
background: #1e293b !important;
|
||||
border-bottom: 1px solid #334155;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.erp-header-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
border-radius: var(--erp-radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
color: #62625b;
|
||||
color: #475569;
|
||||
will-change: background;
|
||||
}
|
||||
|
||||
.erp-header-light .erp-header-btn { color: #62625b; }
|
||||
.erp-header-dark .erp-header-btn { color: #91918c; }
|
||||
.erp-header-btn:hover { background: #f6f6f3; }
|
||||
.erp-header-dark .erp-header-btn:hover { background: #33332e; }
|
||||
.erp-header-light .erp-header-btn { color: #475569; }
|
||||
.erp-header-dark .erp-header-btn { color: #94a3b8; }
|
||||
.erp-header-btn:hover { background: #f1f5f9; }
|
||||
.erp-header-dark .erp-header-btn:hover { background: #334155; }
|
||||
|
||||
.erp-header-title { font-size: 15px; font-weight: 600; }
|
||||
.erp-text-light { color: #211922; }
|
||||
.erp-text-light { color: #0f172a; }
|
||||
.erp-text-dark { color: rgba(255, 255, 255, 0.95); }
|
||||
.erp-text-light-secondary { color: #62625b; }
|
||||
.erp-text-dark-secondary { color: #91918c; }
|
||||
.erp-text-light-secondary { color: #475569; }
|
||||
.erp-text-dark-secondary { color: #94a3b8; }
|
||||
|
||||
.erp-header-divider { width: 1px; height: 24px; margin: 0 8px; }
|
||||
.erp-header-divider-light { background: rgba(0, 0, 0, 0.06); }
|
||||
.erp-header-divider-dark { background: rgba(255, 255, 255, 0.05); }
|
||||
.erp-header-divider-dark { background: rgba(255, 255, 255, 0.06); }
|
||||
|
||||
/* User avatar */
|
||||
.erp-header-user {
|
||||
@@ -816,15 +816,15 @@ body {
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--erp-radius-sm);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.erp-header-user:hover { background: #f6f6f3; }
|
||||
.erp-header-dark .erp-header-user:hover { background: #33332e; }
|
||||
.erp-header-user:hover { background: #f1f5f9; }
|
||||
.erp-header-dark .erp-header-user:hover { background: #334155; }
|
||||
|
||||
.erp-user-avatar {
|
||||
background: #0075de !important;
|
||||
background: #2563eb !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
@@ -832,11 +832,11 @@ body {
|
||||
|
||||
/* Footer */
|
||||
.erp-footer { text-align: center; padding: 12px 24px !important; background: transparent !important; font-size: 12px; }
|
||||
.erp-footer-light { color: #91918c; }
|
||||
.erp-footer-dark { color: #62625b; }
|
||||
.erp-footer-light { color: #94a3b8; }
|
||||
.erp-footer-dark { color: #64748b; }
|
||||
|
||||
/* ====================================================================
|
||||
* Dashboard — Stat Cards & Quick Actions (replacing inline styles)
|
||||
* Dashboard — Stat Cards & Quick Actions
|
||||
* ==================================================================== */
|
||||
|
||||
/* Stat Card */
|
||||
@@ -864,7 +864,7 @@ body {
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: var(--card-gradient, linear-gradient(135deg, #e60023, #f05a5a));
|
||||
background: var(--card-gradient, linear-gradient(135deg, #2563eb, #60a5fa));
|
||||
}
|
||||
|
||||
.erp-stat-card-body {
|
||||
@@ -895,7 +895,7 @@ body {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--erp-radius-lg);
|
||||
background: var(--card-icon-bg, rgba(230, 0, 35, 0.08));
|
||||
background: var(--card-icon-bg, rgba(37, 99, 235, 0.08));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -903,7 +903,7 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Section Header (shared by dashboard sections) */
|
||||
/* Section Header */
|
||||
.erp-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -913,7 +913,7 @@ body {
|
||||
|
||||
.erp-section-icon {
|
||||
font-size: 16px;
|
||||
color: #e60023;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.erp-section-title {
|
||||
@@ -928,7 +928,7 @@ body {
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 12px;
|
||||
border-radius: var(--erp-radius-lg);
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
background: var(--erp-bg-spotlight);
|
||||
@@ -936,17 +936,17 @@ body {
|
||||
}
|
||||
|
||||
.erp-quick-action:hover {
|
||||
background: #fef0f0;
|
||||
border-color: var(--action-color, #e60023);
|
||||
background: #eff6ff;
|
||||
border-color: var(--action-color, #2563eb);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-quick-action {
|
||||
background: #1a1a18;
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-quick-action:hover {
|
||||
background: #33332e;
|
||||
border-color: var(--action-color, #e60023);
|
||||
background: #1e293b;
|
||||
border-color: var(--action-color, #2563eb);
|
||||
}
|
||||
|
||||
.erp-quick-action-icon {
|
||||
@@ -956,8 +956,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--action-color, #e60023) 8%, transparent);
|
||||
color: var(--action-color, #e60023);
|
||||
background: color-mix(in srgb, var(--action-color, #2563eb) 8%, transparent);
|
||||
color: var(--action-color, #2563eb);
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -1008,12 +1008,12 @@ body {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.erp-stat-card-trend-up { color: #0b5030; }
|
||||
.erp-stat-card-trend-down { color: #9e0a0a; }
|
||||
.erp-stat-card-trend-neutral { color: #62625b; }
|
||||
.erp-stat-card-trend-up { color: #059669; }
|
||||
.erp-stat-card-trend-down { color: #dc2626; }
|
||||
.erp-stat-card-trend-neutral { color: #475569; }
|
||||
|
||||
.erp-stat-card-trend-label {
|
||||
color: #62625b;
|
||||
color: #475569;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@@ -1046,8 +1046,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--action-color, #e60023) 8%, transparent);
|
||||
color: var(--action-color, #e60023);
|
||||
background: color-mix(in srgb, var(--action-color, #2563eb) 8%, transparent);
|
||||
color: var(--action-color, #2563eb);
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.15s ease;
|
||||
@@ -1075,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, #e60023);
|
||||
border-left: 3px solid var(--task-color, #2563eb);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
@@ -1088,12 +1088,12 @@ body {
|
||||
.erp-task-item-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--erp-radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--task-color, #e60023) 8%, transparent);
|
||||
color: var(--task-color, #e60023);
|
||||
background: color-mix(in srgb, var(--task-color, #2563eb) 8%, transparent);
|
||||
color: var(--task-color, #2563eb);
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -1115,25 +1115,25 @@ body {
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
font-size: var(--erp-font-size-xs);
|
||||
color: #91918c;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.erp-task-priority {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 1px 8px;
|
||||
border-radius: 12px;
|
||||
border-radius: var(--erp-radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.erp-task-priority-high { background: #fef2f2; color: #9e0a0a; }
|
||||
.erp-task-priority-medium { background: #fff7ed; color: #b56e1a; }
|
||||
.erp-task-priority-low { background: #ecfdf5; color: #103c25; }
|
||||
.erp-task-priority-high { background: #fef2f2; color: #dc2626; }
|
||||
.erp-task-priority-medium { background: #fffbeb; color: #d97706; }
|
||||
.erp-task-priority-low { background: #ecfdf5; color: #059669; }
|
||||
|
||||
[data-theme='dark'] .erp-task-priority-high { background: rgba(158, 10, 10, 0.15); color: #f05a5a; }
|
||||
[data-theme='dark'] .erp-task-priority-medium { background: rgba(181, 110, 26, 0.15); color: #d4852a; }
|
||||
[data-theme='dark'] .erp-task-priority-low { background: rgba(16, 60, 37, 0.15); color: #3db377; }
|
||||
[data-theme='dark'] .erp-task-priority-high { background: rgba(220, 38, 38, 0.15); color: #f87171; }
|
||||
[data-theme='dark'] .erp-task-priority-medium { background: rgba(217, 119, 6, 0.15); color: #fbbf24; }
|
||||
[data-theme='dark'] .erp-task-priority-low { background: rgba(5, 150, 105, 0.15); color: #34d399; }
|
||||
|
||||
/* Activity Timeline */
|
||||
.erp-activity-list {
|
||||
@@ -1189,12 +1189,12 @@ body {
|
||||
|
||||
.erp-activity-time {
|
||||
font-size: 11px;
|
||||
color: #91918c;
|
||||
color: #94a3b8;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .erp-activity-time {
|
||||
color: #62625b;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
|
||||
@@ -167,7 +167,7 @@ export default function Home() {
|
||||
title: '用户总数',
|
||||
value: stats.userCount,
|
||||
icon: <UserOutlined />,
|
||||
gradient: 'linear-gradient(135deg, #e60023, #f05a5a)',
|
||||
gradient: 'linear-gradient(135deg, #2563eb, #60a5fa)',
|
||||
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, #103c25, #10B981)',
|
||||
gradient: 'linear-gradient(135deg, #059669, #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, #b56e1a, #F59E0B)',
|
||||
gradient: 'linear-gradient(135deg, #d97706, #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: '#e60023' },
|
||||
{ icon: <SafetyCertificateOutlined />, label: '权限管理', path: '/roles', color: '#103c25' },
|
||||
{ icon: <ApartmentOutlined />, label: '组织架构', path: '/organizations', color: '#b56e1a' },
|
||||
{ icon: <UserOutlined />, label: '用户管理', path: '/users', color: '#2563eb' },
|
||||
{ icon: <SafetyCertificateOutlined />, label: '权限管理', path: '/roles', color: '#059669' },
|
||||
{ icon: <ApartmentOutlined />, label: '组织架构', path: '/organizations', color: '#d97706' },
|
||||
{ icon: <PartitionOutlined />, label: '工作流', path: '/workflow', color: '#7C3AED' },
|
||||
{ icon: <BellOutlined />, label: '消息中心', path: '/messages', color: '#E11D48' },
|
||||
{ icon: <SettingOutlined />, label: '系统设置', path: '/settings', color: '#62625b' },
|
||||
{ icon: <SettingOutlined />, label: '系统设置', path: '/settings', color: '#475569' },
|
||||
];
|
||||
|
||||
const pendingTasks: TaskItem[] = [
|
||||
{ id: '1', title: '审核新用户注册申请', priority: 'high', assignee: '系统', dueText: '待处理', color: '#9e0a0a', icon: <UserOutlined />, path: '/users' },
|
||||
{ id: '2', title: '配置工作流审批节点', priority: 'medium', assignee: '管理员', dueText: '进行中', color: '#b56e1a', icon: <PartitionOutlined />, path: '/workflow' },
|
||||
{ id: '3', title: '更新角色权限策略', priority: 'low', assignee: '管理员', dueText: '计划中', color: '#103c25', icon: <SafetyCertificateOutlined />, path: '/roles' },
|
||||
{ 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' },
|
||||
];
|
||||
|
||||
const recentActivities: ActivityItem[] = [
|
||||
@@ -243,13 +243,13 @@ export default function Home() {
|
||||
<h2 style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: isDark ? '#f6f6f3' : 'rgba(0,0,0,0.95)',
|
||||
color: isDark ? '#f8fafc' : 'rgba(0,0,0,0.95)',
|
||||
margin: '0 0 4px',
|
||||
letterSpacing: '-0.5px',
|
||||
}}>
|
||||
工作台
|
||||
</h2>
|
||||
<p style={{ fontSize: 14, color: isDark ? '#91918c' : '#62625b', margin: 0 }}>
|
||||
<p style={{ fontSize: 14, color: isDark ? '#94a3b8' : '#475569', margin: 0 }}>
|
||||
欢迎回来,这是您的系统概览
|
||||
</p>
|
||||
</div>
|
||||
@@ -308,12 +308,12 @@ export default function Home() {
|
||||
<Col xs={24} lg={14}>
|
||||
<div className="erp-content-card erp-fade-in erp-fade-in-delay-2">
|
||||
<div className="erp-section-header">
|
||||
<CheckCircleOutlined className="erp-section-icon" style={{ color: '#E11D48' }} />
|
||||
<CheckCircleOutlined className="erp-section-icon" style={{ color: '#2563eb' }} />
|
||||
<span className="erp-section-title">待办任务</span>
|
||||
<span style={{
|
||||
marginLeft: 'auto',
|
||||
fontSize: 12,
|
||||
color: isDark ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
}}>
|
||||
{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 ? '#62625b' : '#CBD5E1', fontSize: 12 }} />
|
||||
<RightOutlined style={{ color: isDark ? '#475569' : '#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: '#f05a5a' }} />
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#60a5fa' }} />
|
||||
<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: '#f05a5a' }} />
|
||||
<ClockCircleOutlined className="erp-section-icon" style={{ color: '#60a5fa' }} />
|
||||
<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%, #e60023 50%, #f05a5a 100%)',
|
||||
background: 'linear-gradient(135deg, #312E81 0%, #2563eb 50%, #60a5fa 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: '#62625b' }}>
|
||||
<p style={{ fontSize: 14, color: '#475569' }}>
|
||||
请登录您的账户以继续
|
||||
</p>
|
||||
|
||||
@@ -163,7 +163,7 @@ export default function Login() {
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined style={{ color: '#91918c' }} />}
|
||||
prefix={<UserOutlined style={{ color: '#94a3b8' }} />}
|
||||
placeholder="用户名"
|
||||
style={{ height: 44, borderRadius: 10 }}
|
||||
/>
|
||||
@@ -173,7 +173,7 @@ export default function Login() {
|
||||
rules={[{ required: true, message: '请输入密码' }]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#91918c' }} />}
|
||||
prefix={<LockOutlined style={{ color: '#94a3b8' }} />}
|
||||
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: '#62625b', margin: 0 }}>
|
||||
<p style={{ fontSize: 12, color: '#475569', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
};
|
||||
|
||||
// --- Org tree state ---
|
||||
@@ -264,9 +264,9 @@ export default function Organizations() {
|
||||
{item.name}{' '}
|
||||
{item.code && <Tag style={{
|
||||
marginLeft: 4,
|
||||
background: isDark ? '#211922' : '#fef0f0',
|
||||
background: isDark ? '#0f172a' : '#eff6ff',
|
||||
border: 'none',
|
||||
color: '#e60023',
|
||||
color: '#2563eb',
|
||||
fontSize: 11,
|
||||
}}>{item.code}</Tag>}
|
||||
</span>
|
||||
@@ -282,9 +282,9 @@ export default function Organizations() {
|
||||
{item.name}{' '}
|
||||
{item.code && <Tag style={{
|
||||
marginLeft: 4,
|
||||
background: isDark ? '#211922' : '#ECFDF5',
|
||||
background: isDark ? '#0f172a' : '#ECFDF5',
|
||||
border: 'none',
|
||||
color: '#103c25',
|
||||
color: '#059669',
|
||||
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: '#e60023' }} />
|
||||
<ApartmentOutlined style={{ marginRight: 8, color: '#2563eb' }} />
|
||||
组织架构管理
|
||||
</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 ? '#211922' : '#f6f6f3'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
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 ? '#211922' : '#f6f6f3'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
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 ? '#211922' : '#f6f6f3'}`,
|
||||
borderBottom: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
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: '#62625b', label: '已上传' },
|
||||
uploaded: { color: '#475569', label: '已上传' },
|
||||
installed: { color: '#2563EB', label: '已安装' },
|
||||
enabled: { color: '#103c25', label: '已启用' },
|
||||
running: { color: '#103c25', label: '运行中' },
|
||||
disabled: { color: '#9e0a0a', label: '已禁用' },
|
||||
enabled: { color: '#059669', label: '已启用' },
|
||||
running: { color: '#059669', label: '运行中' },
|
||||
disabled: { color: '#dc2626', 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: '#62625b', label: status };
|
||||
const cfg = STATUS_CONFIG[status] || { color: '#475569', label: status };
|
||||
return <Tag color={cfg.color}>{cfg.label}</Tag>;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -299,7 +299,7 @@ export function PluginDashboardPage() {
|
||||
style={{
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
color: isDark ? '#f6f6f3' : 'rgba(0,0,0,0.95)',
|
||||
color: isDark ? '#f8fafc' : '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 ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
@@ -352,7 +352,7 @@ export function PluginDashboardPage() {
|
||||
<div className="erp-section-header">
|
||||
<DashboardOutlined
|
||||
className="erp-section-icon"
|
||||
style={{ color: '#e60023' }}
|
||||
style={{ color: '#2563eb' }}
|
||||
/>
|
||||
<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' ? '#e60023' : '#3B82F6' }}
|
||||
style={{ color: currentPalette.tagColor === 'purple' ? '#2563eb' : '#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 = '#e60023';
|
||||
let nodeColorLight = '#f05a5a';
|
||||
let nodeColorBase = '#2563eb';
|
||||
let nodeColorLight = '#60a5fa';
|
||||
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> = {
|
||||
'财务': '#103c25',
|
||||
'财务': '#059669',
|
||||
'CRM': '#2563EB',
|
||||
'进销存': '#9333EA',
|
||||
'生产': '#9e0a0a',
|
||||
'人力资源': '#b56e1a',
|
||||
'基础': '#62625b',
|
||||
'生产': '#dc2626',
|
||||
'人力资源': '#d97706',
|
||||
'基础': '#475569',
|
||||
};
|
||||
|
||||
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 ?? ''] ?? '#62625b'}
|
||||
color={CATEGORY_COLORS[plugin.category ?? ''] ?? '#475569'}
|
||||
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 ?? ''] ?? '#62625b'}>
|
||||
<Tag color={CATEGORY_COLORS[selectedPlugin.category ?? ''] ?? '#475569'}>
|
||||
{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, #e60023, #f05a5a)'
|
||||
: isDark ? '#211922' : '#f6f6f3',
|
||||
? 'linear-gradient(135deg, #2563eb, #60a5fa)'
|
||||
: isDark ? '#0f172a' : '#f8fafc',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: record.is_system ? '#fff' : isDark ? '#91918c' : '#62625b',
|
||||
color: record.is_system ? '#fff' : isDark ? '#94a3b8' : '#475569',
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
@@ -174,9 +174,9 @@ export default function Roles() {
|
||||
key: 'code',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -190,7 +190,7 @@ export default function Roles() {
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
render: (v: string | undefined) => (
|
||||
<span style={{ color: isDark ? '#62625b' : '#91918c' }}>{v || '-'}</span>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8' }}>{v || '-'}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -201,8 +201,8 @@ export default function Roles() {
|
||||
render: (v: boolean) => (
|
||||
<Tag
|
||||
style={{
|
||||
color: v ? '#e60023' : (isDark ? '#91918c' : '#62625b'),
|
||||
background: v ? '#fef0f0' : (isDark ? '#211922' : '#f6f6f3'),
|
||||
color: v ? '#2563eb' : (isDark ? '#94a3b8' : '#475569'),
|
||||
background: v ? '#eff6ff' : (isDark ? '#0f172a' : '#f8fafc'),
|
||||
border: 'none',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
@@ -222,7 +222,7 @@ export default function Roles() {
|
||||
type="text"
|
||||
icon={<SafetyCertificateOutlined />}
|
||||
onClick={() => openPermModal(record)}
|
||||
style={{ color: '#e60023' }}
|
||||
style={{ color: '#2563eb' }}
|
||||
>
|
||||
权限
|
||||
</Button>
|
||||
@@ -233,7 +233,7 @@ export default function Roles() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEditModal(record)}
|
||||
style={{ color: isDark ? '#91918c' : '#62625b' }}
|
||||
style={{ color: isDark ? '#94a3b8' : '#475569' }}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除此角色?"
|
||||
@@ -279,7 +279,7 @@ export default function Roles() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
@@ -336,8 +336,8 @@ export default function Roles() {
|
||||
marginBottom: 16,
|
||||
padding: 16,
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${isDark ? '#211922' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#fafaf8',
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#f1f5f9',
|
||||
}}
|
||||
>
|
||||
<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: '#103c25',
|
||||
disabled: '#9e0a0a',
|
||||
locked: '#b56e1a',
|
||||
active: '#059669',
|
||||
disabled: '#dc2626',
|
||||
locked: '#d97706',
|
||||
};
|
||||
|
||||
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, #e60023, #f05a5a)',
|
||||
background: 'linear-gradient(135deg, #2563eb, #60a5fa)',
|
||||
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 ? '#62625b' : '#91918c' }}>
|
||||
<div style={{ fontSize: 12, color: isDark ? '#475569' : '#94a3b8' }}>
|
||||
{record.display_name}
|
||||
</div>
|
||||
)}
|
||||
@@ -262,7 +262,7 @@ export default function Users() {
|
||||
<Tag
|
||||
style={{
|
||||
color: STATUS_COLOR_MAP[status] || '#62625b',
|
||||
background: STATUS_BG_MAP[status] || '#f6f6f3',
|
||||
background: STATUS_BG_MAP[status] || '#f8fafc',
|
||||
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 ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#62625b',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
}}>
|
||||
{r.name}
|
||||
</Tag>
|
||||
))
|
||||
: <span style={{ color: isDark ? '#62625b' : '#CBD5E1' }}>-</span>,
|
||||
: <span style={{ color: isDark ? '#475569' : '#CBD5E1' }}>-</span>,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
@@ -299,14 +299,14 @@ export default function Users() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEditModal(record)}
|
||||
style={{ color: isDark ? '#91918c' : '#62625b' }}
|
||||
style={{ color: isDark ? '#94a3b8' : '#475569' }}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<SafetyCertificateOutlined />}
|
||||
onClick={() => openRoleModal(record)}
|
||||
style={{ color: isDark ? '#91918c' : '#62625b' }}
|
||||
style={{ color: isDark ? '#94a3b8' : '#475569' }}
|
||||
/>
|
||||
{record.status === 'active' ? (
|
||||
<Popconfirm
|
||||
@@ -326,7 +326,7 @@ export default function Users() {
|
||||
type="text"
|
||||
icon={<CheckCircleOutlined />}
|
||||
onClick={() => handleToggleStatus(record.id, 'active')}
|
||||
style={{ color: '#103c25' }}
|
||||
style={{ color: '#059669' }}
|
||||
/>
|
||||
)}
|
||||
<Popconfirm
|
||||
@@ -356,7 +356,7 @@ export default function Users() {
|
||||
<Space size={8}>
|
||||
<Input
|
||||
placeholder="搜索用户名..."
|
||||
prefix={<SearchOutlined style={{ color: '#91918c' }} />}
|
||||
prefix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||
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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
@@ -415,7 +415,7 @@ export default function Users() {
|
||||
label="用户名"
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input prefix={<UserOutlined style={{ color: '#91918c' }} />} disabled={!!editUser} />
|
||||
<Input prefix={<UserOutlined style={{ color: '#94a3b8' }} />} 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 ? '#211922' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#fafaf8',
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#E2E8F0'}`,
|
||||
background: isDark ? '#0B0F1A' : '#f1f5f9',
|
||||
}}
|
||||
>
|
||||
<Checkbox value={r.id}>
|
||||
<span style={{ fontWeight: 500 }}>{r.name}</span>
|
||||
<span style={{ color: isDark ? '#62625b' : '#91918c', marginLeft: 8, fontSize: 12 }}>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', 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: '#f05a5a', volcano: '#F97316',
|
||||
lime: '#84CC16', geekblue: '#60a5fa', 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 || '#e60023';
|
||||
const color = widget.color || '#2563eb';
|
||||
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 ? '#91918c' : '#62625b' };
|
||||
const axisLabelStyle = { fill: isDark ? '#94a3b8' : '#475569' };
|
||||
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 ? '#91918c' : '#62625b' };
|
||||
const axisLabelStyle = { fill: isDark ? '#94a3b8' : '#475569' };
|
||||
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 || '#e60023'}10`,
|
||||
background: `${sc.card.color || '#2563eb'}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 || '#e60023'}20`,
|
||||
background: `${sc.card.color || '#2563eb'}20`,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
color: sc.card.color || '#e60023', fontSize: 18,
|
||||
color: sc.card.color || '#2563eb', fontSize: 18,
|
||||
}}>
|
||||
<DashboardOutlined />
|
||||
</div>
|
||||
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
// ── 通用调色板 ──
|
||||
|
||||
const UNIVERSAL_COLORS = [
|
||||
{ gradient: 'linear-gradient(135deg, #e60023, #f05a5a)', iconBg: 'rgba(79, 70, 229, 0.12)', tagColor: 'purple' },
|
||||
{ gradient: 'linear-gradient(135deg, #103c25, #10B981)', iconBg: 'rgba(5, 150, 105, 0.12)', tagColor: 'green' },
|
||||
{ gradient: 'linear-gradient(135deg, #b56e1a, #F59E0B)', iconBg: 'rgba(217, 119, 6, 0.12)', tagColor: 'orange' },
|
||||
{ gradient: 'linear-gradient(135deg, #2563eb, #60a5fa)', 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, #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: '#e60023', light: '#f05a5a', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#103c25', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#b56e1a', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#2563eb', light: '#60a5fa', 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: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' },
|
||||
{ base: '#9e0a0a', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#dc2626', 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: '#e60023' },
|
||||
email: { label: '邮件', color: '#103c25' },
|
||||
sms: { label: '短信', color: '#b56e1a' },
|
||||
in_app: { label: '站内', color: '#2563eb' },
|
||||
email: { label: '邮件', color: '#059669' },
|
||||
sms: { label: '短信', color: '#d97706' },
|
||||
wechat: { label: '微信', color: '#7C3AED' },
|
||||
};
|
||||
|
||||
@@ -64,9 +64,9 @@ export default function MessageTemplates() {
|
||||
key: 'code',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
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: '#62625b' };
|
||||
const info = channelMap[c] || { label: c, color: '#475569' };
|
||||
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 ? '#62625b' : '#91918c', fontSize: 13 }}>{v}</span>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', fontSize: 13 }}>{v}</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -124,7 +124,7 @@ export default function MessageTemplates() {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#62625b' : '#91918c' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#475569' : '#94a3b8' }}>
|
||||
共 {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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -11,9 +11,9 @@ interface Props {
|
||||
}
|
||||
|
||||
const priorityStyles: Record<string, { bg: string; color: string; text: string }> = {
|
||||
urgent: { bg: '#FEF2F2', color: '#9e0a0a', text: '紧急' },
|
||||
important: { bg: '#FFFBEB', color: '#b56e1a', text: '重要' },
|
||||
normal: { bg: '#fef0f0', color: '#e60023', text: '普通' },
|
||||
urgent: { bg: '#FEF2F2', color: '#dc2626', text: '紧急' },
|
||||
important: { bg: '#FFFBEB', color: '#d97706', text: '重要' },
|
||||
normal: { bg: '#eff6ff', color: '#2563eb', 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 ? '#62625b' : '#91918c', fontSize: 12 }}>
|
||||
<div style={{ marginTop: 8, color: isDark ? '#475569' : '#94a3b8', 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 ? '#91918c' : '#62625b') : 'inherit',
|
||||
color: record.is_read ? (isDark ? '#94a3b8' : '#475569') : 'inherit',
|
||||
}}
|
||||
onClick={() => showDetail(record)}
|
||||
>
|
||||
@@ -114,7 +114,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: '50%',
|
||||
background: '#e60023',
|
||||
background: '#2563eb',
|
||||
marginRight: 8,
|
||||
}} />
|
||||
)}
|
||||
@@ -128,7 +128,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
key: 'priority',
|
||||
width: 90,
|
||||
render: (p: string) => {
|
||||
const info = priorityStyles[p] || { bg: '#f6f6f3', color: '#62625b', text: p };
|
||||
const info = priorityStyles[p] || { bg: '#f8fafc', color: '#475569', 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 ? '#62625b' : '#91918c' }}>{s === 'system' ? '系统' : '用户'}</span>,
|
||||
render: (s: string) => <span style={{ color: isDark ? '#475569' : '#94a3b8' }}>{s === 'system' ? '系统' : '用户'}</span>,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -155,9 +155,9 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
width: 80,
|
||||
render: (r: boolean) => (
|
||||
<Tag style={{
|
||||
background: r ? (isDark ? '#211922' : '#f6f6f3') : '#fef0f0',
|
||||
background: r ? (isDark ? '#0f172a' : '#f8fafc') : '#eff6ff',
|
||||
border: 'none',
|
||||
color: r ? (isDark ? '#62625b' : '#91918c') : '#e60023',
|
||||
color: r ? (isDark ? '#475569' : '#94a3b8') : '#2563eb',
|
||||
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 ? '#62625b' : '#91918c', fontSize: 13 }}>{v}</span>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', fontSize: 13 }}>{v}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -185,7 +185,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
size="small"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={() => handleMarkRead(record.id)}
|
||||
style={{ color: '#e60023' }}
|
||||
style={{ color: '#2563eb' }}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
@@ -193,7 +193,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
size="small"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => showDetail(record)}
|
||||
style={{ color: isDark ? '#62625b' : '#91918c' }}
|
||||
style={{ color: isDark ? '#475569' : '#94a3b8' }}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
@@ -215,7 +215,7 @@ export default function NotificationList({ queryFilter }: Props) {
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#62625b' : '#91918c' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#475569' : '#94a3b8' }}>
|
||||
共 {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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -48,12 +48,12 @@ export default function NotificationPreferences() {
|
||||
<div style={{
|
||||
background: isDark ? '#111827' : '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${isDark ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
padding: 24,
|
||||
maxWidth: 600,
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 20 }}>
|
||||
<BellOutlined style={{ fontSize: 16, color: '#e60023' }} />
|
||||
<BellOutlined style={{ fontSize: 16, color: '#2563eb' }} />
|
||||
<span style={{ fontSize: 15, fontWeight: 600 }}>通知偏好设置</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
// 通用边调色板
|
||||
const EDGE_PALETTE: Array<{ base: string; light: string; glow: string }> = [
|
||||
{ base: '#e60023', light: '#f05a5a', glow: 'rgba(79,70,229,0.3)' },
|
||||
{ base: '#103c25', light: '#34D399', glow: 'rgba(5,150,105,0.3)' },
|
||||
{ base: '#b56e1a', light: '#FBBF24', glow: 'rgba(217,119,6,0.3)' },
|
||||
{ base: '#2563eb', light: '#60a5fa', 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: '#0891B2', light: '#22D3EE', glow: 'rgba(8,145,178,0.3)' },
|
||||
{ base: '#9e0a0a', light: '#F87171', glow: 'rgba(220,38,38,0.3)' },
|
||||
{ base: '#dc2626', 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 = '#e60023';
|
||||
let nodeColorLight = '#f05a5a';
|
||||
let nodeColorBase = '#2563eb';
|
||||
let nodeColorLight = '#60a5fa';
|
||||
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: '#103c25', text: '创建' },
|
||||
update: { bg: '#fef0f0', color: '#e60023', text: '更新' },
|
||||
delete: { bg: '#FEF2F2', color: '#9e0a0a', text: '删除' },
|
||||
create: { bg: '#ECFDF5', color: '#059669', text: '创建' },
|
||||
update: { bg: '#eff6ff', color: '#2563eb', text: '更新' },
|
||||
delete: { bg: '#FEF2F2', color: '#dc2626', 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: '#f6f6f3', color: '#62625b', text: action };
|
||||
const info = ACTION_STYLES[action] || { bg: '#f8fafc', color: '#475569', 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 ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#62625b',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
}}>
|
||||
{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 ? '#91918c' : '#62625b' }}>
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#94a3b8' : '#475569' }}>
|
||||
{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 ? '#91918c' : '#62625b' }}>
|
||||
<span style={{ fontFamily: 'monospace', fontSize: 12, color: isDark ? '#94a3b8' : '#475569' }}>
|
||||
{v}
|
||||
</span>
|
||||
),
|
||||
@@ -138,7 +138,7 @@ export default function AuditLogViewer() {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (value: string) => (
|
||||
<span style={{ color: isDark ? '#62625b' : '#91918c', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
}}>
|
||||
<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 ? '#62625b' : '#91918c', marginLeft: 'auto' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#475569' : '#94a3b8', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -132,9 +132,9 @@ export default function SystemSettings() {
|
||||
width: 250,
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#CBD5E1' : '#62625b',
|
||||
color: isDark ? '#CBD5E1' : '#475569',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -162,7 +162,7 @@ export default function SystemSettings() {
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEdit(record)}
|
||||
style={{ color: isDark ? '#91918c' : '#62625b' }}
|
||||
style={{ color: isDark ? '#94a3b8' : '#475569' }}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除此设置?"
|
||||
@@ -191,7 +191,7 @@ export default function SystemSettings() {
|
||||
<Space>
|
||||
<Input
|
||||
placeholder="输入设置键名查询"
|
||||
prefix={<SearchOutlined style={{ color: '#91918c' }} />}
|
||||
prefix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||
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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
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: '#103c25', text: '同意' },
|
||||
rejected: { bg: '#FEF2F2', color: '#9e0a0a', text: '拒绝' },
|
||||
delegated: { bg: '#fef0f0', color: '#e60023', text: '已委派' },
|
||||
approved: { bg: '#ECFDF5', color: '#059669', text: '同意' },
|
||||
rejected: { bg: '#FEF2F2', color: '#dc2626', text: '拒绝' },
|
||||
delegated: { bg: '#eff6ff', color: '#2563eb', 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: '#f6f6f3', color: '#62625b', text: o };
|
||||
const info = outcomeStyles[o] || { bg: '#f8fafc', color: '#475569', 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 ? '#62625b' : '#91918c', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
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: '#fef0f0', color: '#e60023', text: '运行中' },
|
||||
suspended: { bg: '#FFFBEB', color: '#b56e1a', text: '已挂起' },
|
||||
completed: { bg: '#ECFDF5', color: '#103c25', text: '已完成' },
|
||||
terminated: { bg: '#FEF2F2', color: '#9e0a0a', text: '已终止' },
|
||||
running: { bg: '#eff6ff', color: '#2563eb', text: '运行中' },
|
||||
suspended: { bg: '#FFFBEB', color: '#d97706', text: '已挂起' },
|
||||
completed: { bg: '#ECFDF5', color: '#059669', text: '已完成' },
|
||||
terminated: { bg: '#FEF2F2', color: '#dc2626', 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: '#f6f6f3', color: '#62625b', text: s };
|
||||
const info = statusStyles[s] || { bg: '#f8fafc', color: '#475569', 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 ? '#62625b' : '#91918c', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -76,9 +76,9 @@ export default function PendingTasks() {
|
||||
key: 'business_key',
|
||||
render: (v: string | undefined) => v ? (
|
||||
<Tag style={{
|
||||
background: isDark ? '#211922' : '#f6f6f3',
|
||||
background: isDark ? '#0f172a' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -93,9 +93,9 @@ export default function PendingTasks() {
|
||||
width: 100,
|
||||
render: (s: string) => (
|
||||
<Tag style={{
|
||||
background: '#fef0f0',
|
||||
background: '#eff6ff',
|
||||
border: 'none',
|
||||
color: '#e60023',
|
||||
color: '#2563eb',
|
||||
fontWeight: 500,
|
||||
}}>
|
||||
{s}
|
||||
@@ -108,7 +108,7 @@ export default function PendingTasks() {
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (v: string) => (
|
||||
<span style={{ color: isDark ? '#62625b' : '#91918c', fontSize: 13 }}>
|
||||
<span style={{ color: isDark ? '#475569' : '#94a3b8', 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 ? '#211922' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#0f172a' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
import ProcessDesigner from './ProcessDesigner';
|
||||
|
||||
const statusColors: Record<string, { bg: string; color: string; text: string }> = {
|
||||
draft: { bg: '#f6f6f3', color: '#62625b', text: '草稿' },
|
||||
published: { bg: '#ecfdf5', color: '#103c25', text: '已发布' },
|
||||
deprecated: { bg: '#fef2f2', color: '#9e0a0a', text: '已弃用' },
|
||||
draft: { bg: '#f8fafc', color: '#475569', text: '草稿' },
|
||||
published: { bg: '#ecfdf5', color: '#059669', text: '已发布' },
|
||||
deprecated: { bg: '#fef2f2', color: '#dc2626', text: '已弃用' },
|
||||
};
|
||||
|
||||
export default function ProcessDefinitions() {
|
||||
@@ -92,9 +92,9 @@ export default function ProcessDefinitions() {
|
||||
key: 'key',
|
||||
render: (v: string) => (
|
||||
<Tag style={{
|
||||
background: isDark ? '#1E293B' : '#f6f6f3',
|
||||
background: isDark ? '#1E293B' : '#f8fafc',
|
||||
border: 'none',
|
||||
color: isDark ? '#91918c' : '#62625b',
|
||||
color: isDark ? '#94a3b8' : '#475569',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
@@ -110,7 +110,7 @@ export default function ProcessDefinitions() {
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (s: string) => {
|
||||
const info = statusColors[s] || { bg: '#f6f6f3', color: '#62625b', text: s };
|
||||
const info = statusColors[s] || { bg: '#f8fafc', color: '#475569', 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 ? '#62625b' : '#91918c' }}>
|
||||
<span style={{ fontSize: 13, color: isDark ? '#475569' : '#94a3b8' }}>
|
||||
共 {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' : '#f6f6f3'}`,
|
||||
border: `1px solid ${isDark ? '#1E293B' : '#f8fafc'}`,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Table
|
||||
|
||||
Reference in New Issue
Block a user