feat(Company): 优化主题系统,添加颜色工具函数和 ESLint 规则
- 新增 colorUtils.ts:提供 alpha()、hex()、fui.* 语义化颜色 API - 新增 chartTheme:统一图表主题配置(坐标轴、tooltip、渐变等) - 扩展 FUI_COLORS.line:完整的透明度级别(0.03-0.8) - 新增 ESLint 规则:检测硬编码金色值(rgba/hex),warning 级别 - 迁移 chartOptions.ts:~15 处硬编码值改用工具函数 - 迁移 shared/styles.ts:~8 处硬编码值改用工具函数 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
10
.eslintrc.js
10
.eslintrc.js
@@ -79,6 +79,16 @@ module.exports = {
|
|||||||
'no-unused-vars': 'off', // 使用 TS 版本的规则
|
'no-unused-vars': 'off', // 使用 TS 版本的规则
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Company 视图主题硬编码检测
|
||||||
|
{
|
||||||
|
files: ['src/views/Company/**/*.{ts,tsx,js,jsx}'],
|
||||||
|
excludedFiles: ['**/theme/**', '**/*.test.*', '**/*.spec.*'],
|
||||||
|
plugins: ['local-rules'],
|
||||||
|
rules: {
|
||||||
|
// warning 级别:提醒开发者但不阻塞构建
|
||||||
|
'local-rules/no-hardcoded-fui-colors': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
/* 忽略文件(与 .eslintignore 等效)*/
|
/* 忽略文件(与 .eslintignore 等效)*/
|
||||||
|
|||||||
10
eslint-rules/index.js
Normal file
10
eslint-rules/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 本地 ESLint 规则插件
|
||||||
|
*
|
||||||
|
* 在 .eslintrc.js 中通过 eslint-plugin-local-rules 使用
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
rules: {
|
||||||
|
'no-hardcoded-fui-colors': require('./no-hardcoded-fui-colors'),
|
||||||
|
},
|
||||||
|
};
|
||||||
86
eslint-rules/no-hardcoded-fui-colors.js
Normal file
86
eslint-rules/no-hardcoded-fui-colors.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* ESLint 规则:禁止在 Company 目录使用硬编码颜色
|
||||||
|
*
|
||||||
|
* 检测模式:
|
||||||
|
* - #D4AF37 (金色十六进制)
|
||||||
|
* - rgba(212, 175, 55, x) (金色 RGBA)
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* 在 .eslintrc.js 中添加规则配置
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
type: 'suggestion',
|
||||||
|
docs: {
|
||||||
|
description: '禁止在 FUI 主题组件中使用硬编码颜色值',
|
||||||
|
category: 'Best Practices',
|
||||||
|
recommended: true,
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
noHardcodedGoldRgba:
|
||||||
|
'避免硬编码金色 RGBA 值 "{{value}}"。请使用 alpha("gold", {{opacity}}) 或 fui.border() 等语义化 API。',
|
||||||
|
noHardcodedGoldHex:
|
||||||
|
'避免硬编码金色十六进制值 "{{value}}"。请使用 fui.gold 或 FUI_COLORS.gold[400]。',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
create(context) {
|
||||||
|
const filename = context.getFilename();
|
||||||
|
|
||||||
|
// 仅在 src/views/Company 目录下生效
|
||||||
|
if (!filename.includes('src/views/Company')) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除主题定义文件
|
||||||
|
if (
|
||||||
|
filename.includes('/theme/') ||
|
||||||
|
filename.includes('/utils/colorUtils')
|
||||||
|
) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金色 RGBA 正则 - 匹配 rgba(212, 175, 55, x)
|
||||||
|
const goldRgbaPattern =
|
||||||
|
/rgba\(\s*212\s*,\s*175\s*,\s*55\s*,\s*([\d.]+)\s*\)/i;
|
||||||
|
|
||||||
|
// 金色十六进制 - 匹配 #D4AF37(不区分大小写)
|
||||||
|
const goldHexPattern = /#D4AF37/i;
|
||||||
|
|
||||||
|
function checkValue(node, value) {
|
||||||
|
if (typeof value !== 'string') return;
|
||||||
|
|
||||||
|
// 检测金色十六进制
|
||||||
|
if (goldHexPattern.test(value)) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
messageId: 'noHardcodedGoldHex',
|
||||||
|
data: { value },
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测金色 RGBA
|
||||||
|
const rgbaMatch = value.match(goldRgbaPattern);
|
||||||
|
if (rgbaMatch) {
|
||||||
|
const opacity = rgbaMatch[1];
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
messageId: 'noHardcodedGoldRgba',
|
||||||
|
data: { value, opacity },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
Literal(node) {
|
||||||
|
checkValue(node, node.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
TemplateElement(node) {
|
||||||
|
checkValue(node, node.value.raw);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -98,6 +98,7 @@
|
|||||||
"cos-nodejs-sdk-v5": "^2.15.4",
|
"cos-nodejs-sdk-v5": "^2.15.4",
|
||||||
"env-cmd": "^11.0.0",
|
"env-cmd": "^11.0.0",
|
||||||
"eslint-config-prettier": "8.3.0",
|
"eslint-config-prettier": "8.3.0",
|
||||||
|
"eslint-plugin-local-rules": "^3.0.2",
|
||||||
"eslint-plugin-prettier": "3.4.0",
|
"eslint-plugin-prettier": "3.4.0",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-append-prepend": "1.0.9",
|
"gulp-append-prepend": "1.0.9",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { formatUtils } from '@services/financialService';
|
import { formatUtils } from '@services/financialService';
|
||||||
|
import { alpha, fui, chartTheme } from '@views/Company/theme';
|
||||||
|
|
||||||
interface ChartDataItem {
|
interface ChartDataItem {
|
||||||
period: string;
|
period: string;
|
||||||
@@ -106,22 +107,22 @@ export const getComparisonChartOption = (
|
|||||||
text: '营收与利润趋势',
|
text: '营收与利润趋势',
|
||||||
left: 'center',
|
left: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#D4AF37',
|
color: fui.gold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
backgroundColor: 'rgba(26, 32, 44, 0.95)',
|
backgroundColor: chartTheme.tooltip.bg,
|
||||||
borderColor: 'rgba(212, 175, 55, 0.3)',
|
borderColor: chartTheme.tooltip.border,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#E2E8F0',
|
color: '#E2E8F0',
|
||||||
},
|
},
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
type: 'cross',
|
type: 'cross',
|
||||||
crossStyle: {
|
crossStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.5)',
|
color: alpha('gold', 0.5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -144,7 +145,7 @@ export const getComparisonChartOption = (
|
|||||||
data: revenueData.map((d) => d.period),
|
data: revenueData.map((d) => d.period),
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.3)',
|
color: chartTheme.axisLine,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
@@ -161,7 +162,7 @@ export const getComparisonChartOption = (
|
|||||||
},
|
},
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.3)',
|
color: chartTheme.axisLine,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
@@ -169,7 +170,7 @@ export const getComparisonChartOption = (
|
|||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.1)',
|
color: chartTheme.splitLine,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -182,7 +183,7 @@ export const getComparisonChartOption = (
|
|||||||
},
|
},
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.3)',
|
color: chartTheme.axisLine,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
@@ -201,7 +202,7 @@ export const getComparisonChartOption = (
|
|||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: (params: { dataIndex: number; value: number }) => {
|
color: (params: { dataIndex: number; value: number }) => {
|
||||||
const idx = params.dataIndex;
|
const idx = params.dataIndex;
|
||||||
if (idx === 0) return '#D4AF37'; // 金色作为基准
|
if (idx === 0) return fui.gold; // 金色作为基准
|
||||||
const prevValue = revenueData[idx - 1].value;
|
const prevValue = revenueData[idx - 1].value;
|
||||||
const currValue = params.value;
|
const currValue = params.value;
|
||||||
// 红涨绿跌
|
// 红涨绿跌
|
||||||
@@ -215,37 +216,18 @@ export const getComparisonChartOption = (
|
|||||||
yAxisIndex: 1,
|
yAxisIndex: 1,
|
||||||
data: profitData.map((d) => d.value?.toFixed(2)),
|
data: profitData.map((d) => d.value?.toFixed(2)),
|
||||||
smooth: true,
|
smooth: true,
|
||||||
itemStyle: { color: '#D4AF37' },
|
itemStyle: { color: fui.gold },
|
||||||
lineStyle: { width: 2, color: '#D4AF37' },
|
lineStyle: { width: 2, color: fui.gold },
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
color: {
|
color: chartTheme.goldGradient(0.3, 0.05),
|
||||||
type: 'linear',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
x2: 0,
|
|
||||||
y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(212, 175, 55, 0.3)' },
|
|
||||||
{ offset: 1, color: 'rgba(212, 175, 55, 0.05)' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 黑金主题饼图配色
|
// 黑金主题饼图配色(使用 chartTheme 统一定义)
|
||||||
const BLACK_GOLD_PIE_COLORS = [
|
const BLACK_GOLD_PIE_COLORS = chartTheme.goldSeries;
|
||||||
'#D4AF37', // 金色
|
|
||||||
'#B8860B', // 深金色
|
|
||||||
'#FFD700', // 亮金色
|
|
||||||
'#DAA520', // 金菊色
|
|
||||||
'#CD853F', // 秘鲁色
|
|
||||||
'#F4A460', // 沙褐色
|
|
||||||
'#DEB887', // 实木色
|
|
||||||
'#D2691E', // 巧克力色
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成主营业务饼图配置 - 黑金主题
|
* 生成主营业务饼图配置 - 黑金主题
|
||||||
@@ -265,7 +247,7 @@ export const getMainBusinessPieOption = (
|
|||||||
subtext: subtitle,
|
subtext: subtitle,
|
||||||
left: 'center',
|
left: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#D4AF37',
|
color: fui.gold,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
subtextStyle: {
|
subtextStyle: {
|
||||||
@@ -275,8 +257,8 @@ export const getMainBusinessPieOption = (
|
|||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
backgroundColor: 'rgba(26, 32, 44, 0.95)',
|
backgroundColor: chartTheme.tooltip.bg,
|
||||||
borderColor: 'rgba(212, 175, 55, 0.3)',
|
borderColor: chartTheme.tooltip.border,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#E2E8F0',
|
color: '#E2E8F0',
|
||||||
},
|
},
|
||||||
@@ -310,14 +292,14 @@ export const getMainBusinessPieOption = (
|
|||||||
},
|
},
|
||||||
labelLine: {
|
labelLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: 'rgba(212, 175, 55, 0.5)',
|
color: alpha('gold', 0.5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
shadowBlur: 10,
|
shadowBlur: 10,
|
||||||
shadowOffsetX: 0,
|
shadowOffsetX: 0,
|
||||||
shadowColor: 'rgba(212, 175, 55, 0.5)',
|
shadowColor: alpha('gold', 0.5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// 共享样式常量
|
// 共享样式常量
|
||||||
|
|
||||||
import { darkGoldTheme } from '../../constants';
|
import { darkGoldTheme } from '../../constants';
|
||||||
|
import { alpha, fui } from '@views/Company/theme';
|
||||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +22,7 @@ export const darkGoldCardStyle: SystemStyleObject = {
|
|||||||
*/
|
*/
|
||||||
export const darkGoldCardHoverStyle: SystemStyleObject = {
|
export const darkGoldCardHoverStyle: SystemStyleObject = {
|
||||||
borderColor: darkGoldTheme.borderHover,
|
borderColor: darkGoldTheme.borderHover,
|
||||||
boxShadow: '0 8px 30px rgba(212, 175, 55, 0.15)',
|
boxShadow: `0 8px 30px ${alpha('gold', 0.15)}`,
|
||||||
transform: 'translateY(-2px)',
|
transform: 'translateY(-2px)',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ export const dataRowStyle: SystemStyleObject = {
|
|||||||
* 表格行悬停样式
|
* 表格行悬停样式
|
||||||
*/
|
*/
|
||||||
export const tableRowHoverStyle: SystemStyleObject = {
|
export const tableRowHoverStyle: SystemStyleObject = {
|
||||||
bg: 'rgba(212, 175, 55, 0.08)',
|
bg: alpha('gold', 0.08),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +56,7 @@ export const tableRowHoverStyle: SystemStyleObject = {
|
|||||||
*/
|
*/
|
||||||
export const tableBorderStyle: SystemStyleObject = {
|
export const tableBorderStyle: SystemStyleObject = {
|
||||||
borderBottom: '1px solid',
|
borderBottom: '1px solid',
|
||||||
borderColor: 'rgba(212, 175, 55, 0.1)',
|
borderColor: fui.border('subtle'),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,14 +64,14 @@ export const tableBorderStyle: SystemStyleObject = {
|
|||||||
*/
|
*/
|
||||||
export const financingRowStyle: SystemStyleObject = {
|
export const financingRowStyle: SystemStyleObject = {
|
||||||
p: 3,
|
p: 3,
|
||||||
bg: 'rgba(212, 175, 55, 0.08)',
|
bg: alpha('gold', 0.08),
|
||||||
borderRadius: 'md',
|
borderRadius: 'md',
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: 'rgba(212, 175, 55, 0.15)',
|
borderColor: alpha('gold', 0.15),
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
_hover: {
|
_hover: {
|
||||||
bg: 'rgba(212, 175, 55, 0.12)',
|
bg: alpha('gold', 0.12),
|
||||||
borderColor: 'rgba(212, 175, 55, 0.3)',
|
borderColor: alpha('gold', 0.3),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,8 +128,8 @@ export const sellRowStyle: SystemStyleObject = {
|
|||||||
*/
|
*/
|
||||||
export const dayCardStyle: SystemStyleObject = {
|
export const dayCardStyle: SystemStyleObject = {
|
||||||
p: 4,
|
p: 4,
|
||||||
bg: 'rgba(212, 175, 55, 0.05)',
|
bg: alpha('gold', 0.05),
|
||||||
borderRadius: 'lg',
|
borderRadius: 'lg',
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: 'rgba(212, 175, 55, 0.15)',
|
borderColor: alpha('gold', 0.15),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,6 +50,20 @@ export const FUI_COLORS = {
|
|||||||
default: 'rgba(212, 175, 55, 0.2)',
|
default: 'rgba(212, 175, 55, 0.2)',
|
||||||
emphasis: 'rgba(212, 175, 55, 0.4)',
|
emphasis: 'rgba(212, 175, 55, 0.4)',
|
||||||
glow: 'rgba(212, 175, 55, 0.6)',
|
glow: 'rgba(212, 175, 55, 0.6)',
|
||||||
|
// 完整透明度等级(用于替换硬编码)
|
||||||
|
opacity03: 'rgba(212, 175, 55, 0.03)',
|
||||||
|
opacity05: 'rgba(212, 175, 55, 0.05)',
|
||||||
|
opacity08: 'rgba(212, 175, 55, 0.08)',
|
||||||
|
opacity10: 'rgba(212, 175, 55, 0.1)',
|
||||||
|
opacity12: 'rgba(212, 175, 55, 0.12)',
|
||||||
|
opacity15: 'rgba(212, 175, 55, 0.15)',
|
||||||
|
opacity20: 'rgba(212, 175, 55, 0.2)',
|
||||||
|
opacity30: 'rgba(212, 175, 55, 0.3)',
|
||||||
|
opacity40: 'rgba(212, 175, 55, 0.4)',
|
||||||
|
opacity50: 'rgba(212, 175, 55, 0.5)',
|
||||||
|
opacity60: 'rgba(212, 175, 55, 0.6)',
|
||||||
|
opacity70: 'rgba(212, 175, 55, 0.7)',
|
||||||
|
opacity80: 'rgba(212, 175, 55, 0.8)',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 文字
|
// 文字
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* 使用方式:
|
* 使用方式:
|
||||||
* import { COLORS, GLOW, GLASS } from '@views/Company/theme';
|
* import { COLORS, GLOW, GLASS } from '@views/Company/theme';
|
||||||
* import { FUI_COLORS, FUI_THEME } from '@views/Company/theme';
|
* import { FUI_COLORS, FUI_THEME } from '@views/Company/theme';
|
||||||
|
* import { alpha, fui, chartTheme } from '@views/Company/theme';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 完整主题对象
|
// 完整主题对象
|
||||||
@@ -20,6 +21,22 @@ export {
|
|||||||
// 主题组件
|
// 主题组件
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 工具函数导出(推荐使用)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export {
|
||||||
|
// 核心工具
|
||||||
|
alpha,
|
||||||
|
hex,
|
||||||
|
fui,
|
||||||
|
BASE_COLORS,
|
||||||
|
OPACITY,
|
||||||
|
chartTheme,
|
||||||
|
// 类型
|
||||||
|
type ColorName,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 便捷常量导出(推荐使用)
|
// 便捷常量导出(推荐使用)
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|||||||
251
src/views/Company/theme/utils/colorUtils.ts
Normal file
251
src/views/Company/theme/utils/colorUtils.ts
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
* FUI 颜色工具函数
|
||||||
|
*
|
||||||
|
* 提供便捷的颜色+透明度生成能力,用于替换硬编码的 rgba 值
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 替换 'rgba(212, 175, 55, 0.2)' 为:
|
||||||
|
* alpha('gold', 0.2)
|
||||||
|
*
|
||||||
|
* // 或使用语义化 API:
|
||||||
|
* fui.border() // => 'rgba(212, 175, 55, 0.2)'
|
||||||
|
* fui.border('hover') // => 'rgba(212, 175, 55, 0.4)'
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 基础色值(单一数据源)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export const BASE_COLORS = {
|
||||||
|
gold: { r: 212, g: 175, b: 55 }, // #D4AF37
|
||||||
|
white: { r: 255, g: 255, b: 255 },
|
||||||
|
black: { r: 0, g: 0, b: 0 },
|
||||||
|
bgPrimary: { r: 15, g: 15, b: 26 }, // #0F0F1A
|
||||||
|
bgElevated: { r: 26, g: 26, b: 46 }, // #1A1A2E
|
||||||
|
positive: { r: 239, g: 68, b: 68 }, // #EF4444 涨
|
||||||
|
negative: { r: 34, g: 197, b: 94 }, // #22C55E 跌
|
||||||
|
warning: { r: 245, g: 158, b: 11 }, // #F59E0B
|
||||||
|
info: { r: 59, g: 130, b: 246 }, // #3B82F6
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type ColorName = keyof typeof BASE_COLORS;
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 核心工具函数
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成带透明度的颜色值
|
||||||
|
* @param color - 颜色名称
|
||||||
|
* @param opacity - 透明度 (0-1)
|
||||||
|
* @returns rgba 字符串
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* alpha('gold', 0.2) => 'rgba(212, 175, 55, 0.2)'
|
||||||
|
* alpha('white', 0.5) => 'rgba(255, 255, 255, 0.5)'
|
||||||
|
*/
|
||||||
|
export function alpha(color: ColorName, opacity: number): string {
|
||||||
|
const { r, g, b } = BASE_COLORS[color];
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取十六进制颜色值
|
||||||
|
* @param color - 颜色名称
|
||||||
|
* @returns hex 字符串
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* hex('gold') => '#D4AF37'
|
||||||
|
*/
|
||||||
|
export function hex(color: ColorName): string {
|
||||||
|
const { r, g, b } = BASE_COLORS[color];
|
||||||
|
const toHex = (n: number) => n.toString(16).padStart(2, '0').toUpperCase();
|
||||||
|
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 预设透明度级别
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export const OPACITY = {
|
||||||
|
/** 0.05 - 极淡背景 */
|
||||||
|
ghost: 0.05,
|
||||||
|
/** 0.08 - 微弱背景 */
|
||||||
|
faint: 0.08,
|
||||||
|
/** 0.1 - 淡背景/边框 */
|
||||||
|
subtle: 0.1,
|
||||||
|
/** 0.15 - 轻背景 */
|
||||||
|
light: 0.15,
|
||||||
|
/** 0.2 - 默认边框 */
|
||||||
|
muted: 0.2,
|
||||||
|
/** 0.3 - 标签边框 */
|
||||||
|
soft: 0.3,
|
||||||
|
/** 0.4 - 悬停边框 */
|
||||||
|
medium: 0.4,
|
||||||
|
/** 0.5 - 半透明 */
|
||||||
|
half: 0.5,
|
||||||
|
/** 0.6 - 发光边框 */
|
||||||
|
emphasis: 0.6,
|
||||||
|
/** 0.7 - 次要文字 */
|
||||||
|
secondary: 0.7,
|
||||||
|
/** 0.8 - 高可见 */
|
||||||
|
high: 0.8,
|
||||||
|
/** 0.95 - 主文字 */
|
||||||
|
primary: 0.95,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 语义化颜色生成器
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
type BorderVariant = 'subtle' | 'default' | 'hover' | 'emphasis';
|
||||||
|
type BgVariant = 'ghost' | 'subtle' | 'light' | 'medium';
|
||||||
|
type TextVariant = 'primary' | 'secondary' | 'muted' | 'dim';
|
||||||
|
type GlowSize = 'sm' | 'md' | 'lg' | 'pulse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FUI 语义化颜色 API
|
||||||
|
* 提供直观的颜色获取方式
|
||||||
|
*/
|
||||||
|
export const fui = {
|
||||||
|
/**
|
||||||
|
* 边框颜色
|
||||||
|
* @example
|
||||||
|
* fui.border() => 'rgba(212, 175, 55, 0.2)'
|
||||||
|
* fui.border('hover') => 'rgba(212, 175, 55, 0.4)'
|
||||||
|
*/
|
||||||
|
border: (variant: BorderVariant = 'default'): string => {
|
||||||
|
const opacityMap: Record<BorderVariant, number> = {
|
||||||
|
subtle: 0.1,
|
||||||
|
default: 0.2,
|
||||||
|
hover: 0.4,
|
||||||
|
emphasis: 0.6,
|
||||||
|
};
|
||||||
|
return alpha('gold', opacityMap[variant]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 背景颜色(金色系)
|
||||||
|
* @example
|
||||||
|
* fui.bg() => 'rgba(212, 175, 55, 0.1)'
|
||||||
|
* fui.bg('ghost') => 'rgba(212, 175, 55, 0.05)'
|
||||||
|
*/
|
||||||
|
bg: (variant: BgVariant = 'subtle'): string => {
|
||||||
|
const opacityMap: Record<BgVariant, number> = {
|
||||||
|
ghost: 0.05,
|
||||||
|
subtle: 0.1,
|
||||||
|
light: 0.15,
|
||||||
|
medium: 0.2,
|
||||||
|
};
|
||||||
|
return alpha('gold', opacityMap[variant]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文字颜色(白色系)
|
||||||
|
* @example
|
||||||
|
* fui.text() => 'rgba(255, 255, 255, 0.95)'
|
||||||
|
* fui.text('secondary') => 'rgba(255, 255, 255, 0.7)'
|
||||||
|
*/
|
||||||
|
text: (variant: TextVariant = 'primary'): string => {
|
||||||
|
const opacityMap: Record<TextVariant, number> = {
|
||||||
|
primary: 0.95,
|
||||||
|
secondary: 0.7,
|
||||||
|
muted: 0.5,
|
||||||
|
dim: 0.3,
|
||||||
|
};
|
||||||
|
return alpha('white', opacityMap[variant]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发光效果(box-shadow 值)
|
||||||
|
* @example
|
||||||
|
* fui.glow() => '0 0 16px rgba(212, 175, 55, 0.4)'
|
||||||
|
* fui.glow('lg') => '0 0 32px rgba(212, 175, 55, 0.5)'
|
||||||
|
*/
|
||||||
|
glow: (size: GlowSize = 'md'): string => {
|
||||||
|
const sizeMap: Record<GlowSize, string> = {
|
||||||
|
sm: `0 0 8px ${alpha('gold', 0.3)}`,
|
||||||
|
md: `0 0 16px ${alpha('gold', 0.4)}`,
|
||||||
|
lg: `0 0 32px ${alpha('gold', 0.5)}`,
|
||||||
|
pulse: `0 0 20px ${alpha('gold', 0.6)}, 0 0 40px ${alpha('gold', 0.3)}`,
|
||||||
|
};
|
||||||
|
return sizeMap[size];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玻璃态边框(完整 border 属性)
|
||||||
|
* @example
|
||||||
|
* fui.glassBorder() => '1px solid rgba(212, 175, 55, 0.2)'
|
||||||
|
*/
|
||||||
|
glassBorder: (variant: BorderVariant = 'default'): string => {
|
||||||
|
return `1px solid ${fui.border(variant)}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态颜色
|
||||||
|
*/
|
||||||
|
status: {
|
||||||
|
positive: hex('positive'), // #EF4444
|
||||||
|
negative: hex('negative'), // #22C55E
|
||||||
|
warning: hex('warning'), // #F59E0B
|
||||||
|
info: hex('info'), // #3B82F6
|
||||||
|
positiveBg: alpha('positive', 0.15),
|
||||||
|
negativeBg: alpha('negative', 0.15),
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金色(主色调)
|
||||||
|
*/
|
||||||
|
gold: hex('gold'), // #D4AF37
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 图表主题配置
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ECharts 图表主题配置
|
||||||
|
*/
|
||||||
|
export const chartTheme = {
|
||||||
|
// 轴线和网格线
|
||||||
|
axisLine: alpha('gold', 0.3),
|
||||||
|
splitLine: alpha('gold', 0.1),
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
tooltip: {
|
||||||
|
bg: alpha('bgElevated', 0.95),
|
||||||
|
border: alpha('gold', 0.3),
|
||||||
|
text: alpha('white', 0.9),
|
||||||
|
},
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
title: {
|
||||||
|
color: hex('gold'),
|
||||||
|
subtextColor: alpha('white', 0.6),
|
||||||
|
},
|
||||||
|
|
||||||
|
// 金色系列配色
|
||||||
|
goldSeries: [
|
||||||
|
'#D4AF37',
|
||||||
|
'#B8860B',
|
||||||
|
'#FFD700',
|
||||||
|
'#DAA520',
|
||||||
|
'#CD853F',
|
||||||
|
'#F4A460',
|
||||||
|
'#DEB887',
|
||||||
|
'#D2691E',
|
||||||
|
],
|
||||||
|
|
||||||
|
// 渐变生成器
|
||||||
|
goldGradient: (startOpacity = 0.3, endOpacity = 0.05) => ({
|
||||||
|
type: 'linear' as const,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: alpha('gold', startOpacity) },
|
||||||
|
{ offset: 1, color: alpha('gold', endOpacity) },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
} as const;
|
||||||
15
src/views/Company/theme/utils/index.ts
Normal file
15
src/views/Company/theme/utils/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* FUI 主题工具函数导出
|
||||||
|
*/
|
||||||
|
|
||||||
|
export {
|
||||||
|
// 核心工具
|
||||||
|
alpha,
|
||||||
|
hex,
|
||||||
|
fui,
|
||||||
|
BASE_COLORS,
|
||||||
|
OPACITY,
|
||||||
|
chartTheme,
|
||||||
|
// 类型
|
||||||
|
type ColorName,
|
||||||
|
} from './colorUtils';
|
||||||
Reference in New Issue
Block a user