fix(web,plugin): 前端审计修复 — 401 消除 + 统计卡片 crash + 销售漏斗 500 + antd 6 废弃 API
- API client: proactive token refresh(请求前 30s 检查过期,提前刷新避免 401) - Plugin store: fetchPlugins promise 去重,防止 StrictMode 并发重复请求 - Home stats: 简化 useEffect 加载逻辑,修复 tagColor undefined crash - PluginGraphPage: valueStyle → styles.content, Spin tip → description(antd 6) - DashboardWidgets: trailColor → railColor(antd 6) - data_service: build_scope_sql 参数索引修复(硬编码 $100 → 动态 values.len()+1) - erp-core error: Internal 错误添加 tracing::error 日志输出
This commit is contained in:
@@ -169,7 +169,7 @@ export function BreakdownCard({
|
||||
percent={barPercent}
|
||||
showInfo={false}
|
||||
strokeColor={tagStrokeColor(color)}
|
||||
trailColor="var(--erp-border-light)"
|
||||
railColor="var(--erp-border-light)"
|
||||
size="small"
|
||||
style={{ marginBottom: 0 }}
|
||||
/>
|
||||
|
||||
@@ -9,37 +9,25 @@ import {
|
||||
PieChartOutlined,
|
||||
LineChartOutlined,
|
||||
FunnelPlotOutlined,
|
||||
AppstoreOutlined,
|
||||
UserOutlined,
|
||||
ShoppingOutlined,
|
||||
FileTextOutlined,
|
||||
DatabaseOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
// ── 色板配置 ──
|
||||
// ── 通用调色板 ──
|
||||
|
||||
export const ENTITY_PALETTE: Record<string, { gradient: string; iconBg: string; tagColor: string }> = {
|
||||
customer: {
|
||||
gradient: 'linear-gradient(135deg, #4F46E5, #6366F1)',
|
||||
iconBg: 'rgba(79, 70, 229, 0.12)',
|
||||
tagColor: 'purple',
|
||||
},
|
||||
contact: {
|
||||
gradient: 'linear-gradient(135deg, #059669, #10B981)',
|
||||
iconBg: 'rgba(5, 150, 105, 0.12)',
|
||||
tagColor: 'green',
|
||||
},
|
||||
communication: {
|
||||
gradient: 'linear-gradient(135deg, #D97706, #F59E0B)',
|
||||
iconBg: 'rgba(217, 119, 6, 0.12)',
|
||||
tagColor: 'orange',
|
||||
},
|
||||
customer_tag: {
|
||||
gradient: 'linear-gradient(135deg, #7C3AED, #A78BFA)',
|
||||
iconBg: 'rgba(124, 58, 237, 0.12)',
|
||||
tagColor: 'volcano',
|
||||
},
|
||||
customer_relationship: {
|
||||
gradient: 'linear-gradient(135deg, #E11D48, #F43F5E)',
|
||||
iconBg: 'rgba(225, 29, 72, 0.12)',
|
||||
tagColor: 'red',
|
||||
},
|
||||
};
|
||||
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, #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' },
|
||||
{ gradient: 'linear-gradient(135deg, #EA580C, #F97316)', iconBg: 'rgba(234, 88, 12, 0.12)', tagColor: 'orange' },
|
||||
{ gradient: 'linear-gradient(135deg, #DB2777, #EC4899)', iconBg: 'rgba(219, 39, 119, 0.12)', tagColor: 'magenta' },
|
||||
];
|
||||
|
||||
export const DEFAULT_PALETTE = {
|
||||
gradient: 'linear-gradient(135deg, #2563EB, #3B82F6)',
|
||||
@@ -52,16 +40,42 @@ export const TAG_COLORS = [
|
||||
'magenta', 'gold', 'lime', 'geekblue', 'volcano',
|
||||
];
|
||||
|
||||
// ── 图标映射 ──
|
||||
// ── 按实体顺序自动分配颜色 ──
|
||||
|
||||
export const ENTITY_ICONS: Record<string, React.ReactNode> = {
|
||||
customer: <TeamOutlined />,
|
||||
contact: <TeamOutlined />,
|
||||
communication: <PhoneOutlined />,
|
||||
customer_tag: <TagsOutlined />,
|
||||
customer_relationship: <RiseOutlined />,
|
||||
const paletteCache = new Map<string, { gradient: string; iconBg: string; tagColor: string }>();
|
||||
|
||||
export function getEntityPalette(entityName: string, index: number) {
|
||||
const cached = paletteCache.get(entityName);
|
||||
if (cached) return cached;
|
||||
const safeIndex = index >= 0 ? index : 0;
|
||||
const palette = UNIVERSAL_COLORS[safeIndex % UNIVERSAL_COLORS.length];
|
||||
paletteCache.set(entityName, palette);
|
||||
return palette;
|
||||
}
|
||||
|
||||
// ── 通用图标映射 ──
|
||||
|
||||
const ICON_MAP: Record<string, React.ReactNode> = {
|
||||
team: <TeamOutlined />,
|
||||
user: <UserOutlined />,
|
||||
users: <TeamOutlined />,
|
||||
message: <PhoneOutlined />,
|
||||
phone: <PhoneOutlined />,
|
||||
tags: <TagsOutlined />,
|
||||
tag: <TagsOutlined />,
|
||||
apartment: <RiseOutlined />,
|
||||
dashboard: <DashboardOutlined />,
|
||||
shopping: <ShoppingOutlined />,
|
||||
file: <FileTextOutlined />,
|
||||
database: <DatabaseOutlined />,
|
||||
appstore: <AppstoreOutlined />,
|
||||
};
|
||||
|
||||
export function getEntityIcon(iconName?: string): React.ReactNode {
|
||||
if (!iconName) return <AppstoreOutlined />;
|
||||
return ICON_MAP[iconName.toLowerCase().replace('outlined', '')] ?? <AppstoreOutlined />;
|
||||
}
|
||||
|
||||
export const WIDGET_ICON_MAP: Record<string, React.ReactNode> = {
|
||||
stat_card: <DashboardOutlined />,
|
||||
bar_chart: <BarChartOutlined />,
|
||||
|
||||
Reference in New Issue
Block a user