diff --git a/react-ui/config/defaultSettings.ts b/react-ui/config/defaultSettings.ts index ba42fc80..cf0e5a16 100644 --- a/react-ui/config/defaultSettings.ts +++ b/react-ui/config/defaultSettings.ts @@ -9,7 +9,7 @@ const Settings: ProLayoutProps & { } = { locale: 'zh-CN', navTheme: 'light', - // 拂晓蓝 + colorPrimary: '#514cf9', // layout: 'mix', contentWidth: 'Fluid', fixedHeader: false, diff --git a/react-ui/package.json b/react-ui/package.json index 5f317010..4b28d7cf 100644 --- a/react-ui/package.json +++ b/react-ui/package.json @@ -60,14 +60,17 @@ "not ie <= 10" ], "dependencies": { + "@ant-design/colors": "~7.2.1", "@ant-design/icons": "^5.0.0", "@ant-design/pro-components": "^2.4.4", "@ant-design/use-emotion-css": "1.0.4", "@antv/g6": "^4.8.24", "@antv/hierarchy": "^0.6.12", + "@ctrl/tinycolor": "~4.1.0", "@types/crypto-js": "^4.2.2", "@umijs/route-utils": "^4.0.1", "antd": "~5.21.4", + "antd-style": "~3.7.1", "caniuse-lite": "~1.0.30001707", "classnames": "^2.3.2", "crypto-js": "^4.2.0", diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx index 1030c81a..0f0777ca 100644 --- a/react-ui/src/app.tsx +++ b/react-ui/src/app.tsx @@ -192,7 +192,7 @@ export const antd: RuntimeAntdConfig = (memo) => { colorPrimary: themes['primaryColor'], colorPrimaryHover: themes['primaryHoverColor'], colorPrimaryActive: themes['primaryActiveColor'], - colorPrimaryBg: 'rgba(81, 76, 249, 0.07)', + // colorPrimaryBg: 'rgba(81, 76, 249, 0.07)', colorSuccess: themes['successColor'], colorError: themes['errorColor'], colorWarning: themes['warningColor'], diff --git a/react-ui/src/components/KFButton/index.less b/react-ui/src/components/KFButton/index.less index e69de29b..36a74d2c 100644 --- a/react-ui/src/components/KFButton/index.less +++ b/react-ui/src/components/KFButton/index.less @@ -0,0 +1,25 @@ +body { + .kf-primary-button.ant-btn-color-primary.ant-btn-variant-link { + background-color: .addAlpha(@primary-color, 0.07) [] !important; + } + + .kf-default-button.ant-btn-color-default.ant-btn-variant-link { + background-color: .addAlpha(@text-color-secondary, 0.07) [] !important; + } + + .kf-danger-button.ant-btn-color-danger.ant-btn-variant-link { + background-color: .addAlpha(@error-color, 0.07) [] !important; + } + + .ant-btn-color-default.ant-btn-variant-link.kf-default-button:not(:disabled):not( + .ant-btn-disabled + ):hover { + color: .addAlpha(@text-color-secondary, 0.5) []; + } + + .ant-btn-color-default.ant-btn-variant-link.kf-default-button:not(:disabled):not( + .ant-btn-disabled + ):active { + color: @text-color-secondary; + } +} diff --git a/react-ui/src/components/KFButton/index.tsx b/react-ui/src/components/KFButton/index.tsx index e69de29b..f28bbcc2 100644 --- a/react-ui/src/components/KFButton/index.tsx +++ b/react-ui/src/components/KFButton/index.tsx @@ -0,0 +1,75 @@ +import themes from '@/styles/theme.less'; +import { addAlpha, derivePrimaryStates } from '@/utils/color'; +import { Button, ButtonProps } from 'antd'; +import { createStyles } from 'antd-style'; +import './index.less'; + +type KFColor = 'primary' | 'default' | 'danger'; + +export interface KFButtonProps extends ButtonProps { + kfColor?: KFColor; +} + +const useStyles = createStyles(({ token, css }) => ({ + primary: css` + color: ${token.colorPrimary} !important; + background-color: ${addAlpha(themes['primaryColor'], 0.07)} !important; + + &:hover { + color: ${token.colorPrimaryHover} !important; + } + + &:active { + color: ${token.colorPrimaryActive} !important; + } + `, + default: css` + color: ${themes['textColorSecondary']} !important; + background-color: ${addAlpha(themes['textColorSecondary'], 0.07)} !important; + + &:hover { + color: ${derivePrimaryStates(themes['textColorSecondary']).colorPrimaryHover} !important; + } + + &:active { + color: ${derivePrimaryStates(themes['textColorSecondary']).colorPrimaryActive} !important; + } + `, + danger: css` + color: ${themes['errorColor']} !important; + background-color: ${addAlpha(themes['errorColor'], 0.07)} !important; + + &:hover { + color: ${derivePrimaryStates(themes['errorColor']).colorPrimaryHover} !important; + } + + &:active { + color: ${derivePrimaryStates(themes['errorColor']).colorPrimaryActive} !important; + } + `, +})); + +function KFButton({ kfColor = 'default', className, ...rest }: KFButtonProps) { + const { styles, cx } = useStyles(); + + let style = ''; + switch (kfColor) { + case 'primary': + style = styles.primary; + break; + case 'default': + style = styles.default; + break; + case 'danger': + style = styles.danger; + break; + default: + break; + } + + return ( + + ); +} + +export default KFButton; diff --git a/react-ui/src/utils/color.ts b/react-ui/src/utils/color.ts new file mode 100644 index 00000000..8e96ccc1 --- /dev/null +++ b/react-ui/src/utils/color.ts @@ -0,0 +1,97 @@ +/* + * @Author: 赵伟 + * @Date: 2025-09-02 09:52:50 + * @Description: 兼容 Antd 颜色设置 + */ + +// 安装依赖:npm i @ant-design/colors +import { generate } from '@ant-design/colors'; +import { TinyColor } from '@ctrl/tinycolor'; + +type Options = { + /** 是否使用暗色算法(需传背景色,默认按 AntD 暗色背景) */ + dark?: boolean; + backgroundColor?: string; // 仅 dark 为 true 时生效 +}; + +export function derivePrimaryStates(seed: string, opts: Options = {}) { + const { dark = false, backgroundColor = '#141414' } = opts; + const palette = generate(seed, dark ? { theme: 'dark', backgroundColor } : undefined); + + return { + // 基础主色 + colorPrimary: palette[5], // 主色(第 6 阶) + + // 状态 + colorPrimaryHover: palette[4], // hover(第 5 阶) + colorPrimaryActive: palette[6], // active(第 7 阶) + colorPrimaryTextHover: palette[4], // 文本 hover + colorPrimaryTextActive: palette[6], // 文本 active + + // 背景 + colorPrimaryBg: palette[0], // 浅背景(第 1 阶) + colorPrimaryBgHover: palette[1], // 背景 hover(第 2 阶) + + // 边框 + colorPrimaryBorder: palette[2], // 普通边框(第 3 阶) + colorPrimaryBorderHover: palette[3], // 边框 hover(第 4 阶) + + // 文本 + colorPrimaryText: palette[5], // 主文本(等同于 colorPrimary) + + // 备用(有时用于选中态阴影/描边) + colorPrimaryShadow: palette[6], + }; +} + +export function derivePrimaryStates2(color: string) { + const base = new TinyColor(color); + return { + colorPrimaryHover: base.lighten(10).toHexString(), + colorPrimaryActive: base.darken(10).toHexString(), + }; +} + +/** + * 给颜色值添加透明度. + * + * @param color - 颜色值, #ff0000、#888 或者 rgb(255, 0, 0) + * @param alpha - 透明的,0~1 + * @return 颜色值,rgba + */ + +export function addAlpha(color: string, alpha: number): string { + const alphaFixed = Math.max(0, Math.min(1, alpha)); // 保证在 [0,1] + + // #rrggbb 或 #rgb + if (color.startsWith('#')) { + let r: number, g: number, b: number; + + if (color.length === 4) { + // #rgb + r = parseInt(color[1] + color[1], 16); + g = parseInt(color[2] + color[2], 16); + b = parseInt(color[3] + color[3], 16); + } else if (color.length === 7) { + // #rrggbb + r = parseInt(color.slice(1, 3), 16); + g = parseInt(color.slice(3, 5), 16); + b = parseInt(color.slice(5, 7), 16); + } else { + throw new Error('Invalid hex color format'); + } + + return `rgba(${r}, ${g}, ${b}, ${alphaFixed})`; + } + + // rgb(r,g,b) + const rgbMatch = color.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/i); + if (rgbMatch) { + const r = parseInt(rgbMatch[1], 10); + const g = parseInt(rgbMatch[2], 10); + const b = parseInt(rgbMatch[3], 10); + return `rgba(${r}, ${g}, ${b}, ${alphaFixed})`; + } + + throw new Error('Unsupported color format'); +}