diff --git a/frontend/src/components/base/BaseButton.vue b/frontend/src/components/base/BaseButton.vue index 835432b73..56e393a87 100644 --- a/frontend/src/components/base/BaseButton.vue +++ b/frontend/src/components/base/BaseButton.vue @@ -35,7 +35,7 @@ const variantClasses: Record = { 'bg-cp-fill-tertiary text-cp-text shadow-cp-tertiary hover:bg-cp-bg-text-active active:bg-cp-bg-text-active', ghost: 'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary', - destructive: 'bg-cp-error-bg text-cp-error-text shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active', + destructive: 'bg-cp-error-bg text-cp-error-text-on-bg shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active', } const sizeClasses: Record = { diff --git a/frontend/src/components/base/BaseIconButton.vue b/frontend/src/components/base/BaseIconButton.vue index 331fefb1c..bb4e2ee30 100644 --- a/frontend/src/components/base/BaseIconButton.vue +++ b/frontend/src/components/base/BaseIconButton.vue @@ -36,10 +36,10 @@ const variantClasses: Record = { secondary: 'bg-cp-bg-container text-cp-text-secondary shadow-cp-tertiary hover:bg-cp-bg-text-hover hover:text-cp-text active:bg-cp-bg-text-active', success: - 'bg-cp-success-bg text-cp-success-text shadow-none hover:bg-cp-success-bg-hover active:bg-cp-success-bg-active', + 'bg-cp-success-bg text-cp-success-text-on-bg shadow-none hover:bg-cp-success-bg-hover active:bg-cp-success-bg-active', ghost: 'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary', - destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active', + destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg active:bg-cp-error-bg-active active:text-cp-error-text-on-bg', } const sizeClasses: Record = { diff --git a/frontend/src/components/base/BaseMenuItem.vue b/frontend/src/components/base/BaseMenuItem.vue index 50488c03e..6bdeb00e1 100644 --- a/frontend/src/components/base/BaseMenuItem.vue +++ b/frontend/src/components/base/BaseMenuItem.vue @@ -26,7 +26,7 @@ const classes = computed(() => [ 'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-cp-control-outline', 'disabled:cursor-not-allowed disabled:text-cp-text-disabled', props.tone === 'destructive' - ? 'text-cp-error-text hover:bg-cp-error-bg-hover' + ? 'text-cp-error-text hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg' : 'text-cp-text hover:bg-cp-bg-text-hover', ]) diff --git a/frontend/src/components/base/BaseSelect.vue b/frontend/src/components/base/BaseSelect.vue index 7445d920b..a3bbe93e2 100644 --- a/frontend/src/components/base/BaseSelect.vue +++ b/frontend/src/components/base/BaseSelect.vue @@ -98,7 +98,7 @@ const triggerClasses = computed(() => [ props.disabled ? 'cursor-not-allowed bg-cp-bg-container-disabled text-cp-text-disabled shadow-none' : invalid.value - ? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-text shadow-cp-input-error-active' + ? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-text-on-bg shadow-cp-input-error-active' : open.value ? 'cursor-pointer bg-(--cp-input-active-bg) text-cp-text shadow-cp-input-active' : [ diff --git a/frontend/src/layout/components/AppSidebar.vue b/frontend/src/layout/components/AppSidebar.vue index ca2932b5c..04fad0492 100644 --- a/frontend/src/layout/components/AppSidebar.vue +++ b/frontend/src/layout/components/AppSidebar.vue @@ -342,7 +342,7 @@ onBeforeUnmount(() => { class="inline-flex h-4.5 min-w-0 cursor-pointer items-center gap-1 rounded-cp-sm border-0 px-1.5 font-mono text-[10px] leading-none font-bold transition-colors outline-none focus-visible:ring-2 focus-visible:ring-cp-control-outline focus-visible:ring-offset-2 focus-visible:ring-offset-cp-bg-container" :class="[ hasUpdate - ? 'bg-cp-success-bg text-cp-success-text hover:bg-cp-success-bg-hover' + ? 'bg-cp-success-bg text-cp-success-text-on-bg hover:bg-cp-success-bg-hover' : 'bg-cp-fill-quaternary text-cp-text-quaternary hover:bg-cp-fill-tertiary hover:text-cp-text-secondary', ]" :title="updateButtonLabel" @@ -399,7 +399,7 @@ onBeforeUnmount(() => { > 在线 diff --git a/frontend/src/layout/components/SystemUpdateModal/index.vue b/frontend/src/layout/components/SystemUpdateModal/index.vue index e3ee78759..cecc6a278 100644 --- a/frontend/src/layout/components/SystemUpdateModal/index.vue +++ b/frontend/src/layout/components/SystemUpdateModal/index.vue @@ -257,7 +257,7 @@ watch(

{{ updateError || updateInfo?.warning }}

diff --git a/frontend/src/layout/components/SystemUpdateModal/presenter.ts b/frontend/src/layout/components/SystemUpdateModal/presenter.ts index 5d0468fa2..20f41cbd9 100644 --- a/frontend/src/layout/components/SystemUpdateModal/presenter.ts +++ b/frontend/src/layout/components/SystemUpdateModal/presenter.ts @@ -47,7 +47,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) { return { label: input.restarting ? '重启中' : '更新中', icon: RefreshCw, - badge: 'bg-cp-info-bg text-cp-info-text', + badge: 'bg-cp-info-bg text-cp-info-text-on-bg', iconClass: 'text-cp-info', } } @@ -55,7 +55,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) { return { label: '异常', icon: XCircle, - badge: 'bg-cp-error-bg text-cp-error-text', + badge: 'bg-cp-error-bg text-cp-error-text-on-bg', iconClass: 'text-cp-error', } } @@ -63,7 +63,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) { return { label: input.updateSuccess ? '已更新' : input.hasUpdate ? '有新版本' : '已是最新', icon: input.hasUpdate ? ArrowUpCircle : CheckCircle2, - badge: 'bg-cp-success-bg text-cp-success-text', + badge: 'bg-cp-success-bg text-cp-success-text-on-bg', iconClass: 'text-cp-success', } } diff --git a/frontend/src/styles/index.css b/frontend/src/styles/index.css index 60c8488ff..3324fde42 100644 --- a/frontend/src/styles/index.css +++ b/frontend/src/styles/index.css @@ -72,6 +72,7 @@ --color-cp-info-active: var(--cp-color-info-active); --color-cp-info-text-hover: var(--cp-color-info-text-hover); --color-cp-info-text: var(--cp-color-info-text); + --color-cp-info-text-on-bg: var(--cp-color-info-text-on-bg); --color-cp-info-text-active: var(--cp-color-info-text-active); --color-cp-success-bg: var(--cp-color-success-bg); --color-cp-success-bg-hover: var(--cp-color-success-bg-hover); @@ -83,6 +84,7 @@ --color-cp-success-active: var(--cp-color-success-active); --color-cp-success-text-hover: var(--cp-color-success-text-hover); --color-cp-success-text: var(--cp-color-success-text); + --color-cp-success-text-on-bg: var(--cp-color-success-text-on-bg); --color-cp-success-text-active: var(--cp-color-success-text-active); --color-cp-warning-bg: var(--cp-color-warning-bg); --color-cp-warning-bg-hover: var(--cp-color-warning-bg-hover); @@ -94,6 +96,7 @@ --color-cp-warning-active: var(--cp-color-warning-active); --color-cp-warning-text-hover: var(--cp-color-warning-text-hover); --color-cp-warning-text: var(--cp-color-warning-text); + --color-cp-warning-text-on-bg: var(--cp-color-warning-text-on-bg); --color-cp-warning-text-active: var(--cp-color-warning-text-active); --color-cp-error-bg: var(--cp-color-error-bg); --color-cp-error-bg-hover: var(--cp-color-error-bg-hover); @@ -105,6 +108,7 @@ --color-cp-error-active: var(--cp-color-error-active); --color-cp-error-text-hover: var(--cp-color-error-text-hover); --color-cp-error-text: var(--cp-color-error-text); + --color-cp-error-text-on-bg: var(--cp-color-error-text-on-bg); --color-cp-error-text-active: var(--cp-color-error-text-active); /* 颜色 · 预设(色系按字母排序,角色顺序固定) */ diff --git a/frontend/src/theme/core/resolve.ts b/frontend/src/theme/core/resolve.ts index 459b8fccd..ce563c34e 100644 --- a/frontend/src/theme/core/resolve.ts +++ b/frontend/src/theme/core/resolve.ts @@ -100,7 +100,7 @@ function deriveThemeMap( const aliases = deriveThemeAliasMap(surfaces) const primary = deriveThemePrimaryMap(seed, name, surfaces.colorBgContainer) const link = deriveThemeLinkMap(seedTokens.colorLink, name, surfaces.colorBgContainer) - const semantics = deriveThemeSemanticMap(name, surfaces.colorBgContainer, seedTokens) + const semantics = deriveThemeSemanticMap(name, surfaces, seedTokens) return { surfaces, diff --git a/frontend/src/theme/core/tokens.ts b/frontend/src/theme/core/tokens.ts index 9234869b5..c768c5bb3 100644 --- a/frontend/src/theme/core/tokens.ts +++ b/frontend/src/theme/core/tokens.ts @@ -38,6 +38,7 @@ const FUNCTIONAL_TOKEN_ROLES = [ ['active', '-active'], ['textHover', '-text-hover'], ['text', '-text'], + ['textOnBackground', '-text-on-bg'], ['textActive', '-text-active'], ] as const satisfies readonly (readonly [keyof FunctionalColorMap, FunctionalTokenSuffix])[] diff --git a/frontend/src/theme/derive/colors.ts b/frontend/src/theme/derive/colors.ts index fed0c6522..1d9b6f836 100644 --- a/frontend/src/theme/derive/colors.ts +++ b/frontend/src/theme/derive/colors.ts @@ -133,16 +133,51 @@ export function deriveThemeLinkMap( export function deriveThemeSemanticMap( theme: ThemeName, - containerBg: string, + surfaces: ThemeSurfaceMap, seedTokens: ResolvedThemeSeedTokens, ): ThemeSemanticMap { const recipe = THEME_COLOR_ROLE_RECIPES[theme].semantic + const containerBg = surfaces.colorBgContainer + const textBackgrounds = [ + surfaces.colorBgLayout, + surfaces.colorBgContainer, + surfaces.colorBgElevated, + surfaces.colorBgTextHover, + surfaces.colorBgTextActive, + surfaces.colorFillSecondary, + surfaces.colorFillTertiary, + surfaces.colorFillQuaternary, + ] return { - info: deriveColorRoleMap(seedTokens.colorInfo, theme, containerBg, recipe), - success: deriveColorRoleMap(seedTokens.colorSuccess, theme, containerBg, recipe), - warning: deriveColorRoleMap(seedTokens.colorWarning, theme, containerBg, recipe), - error: deriveColorRoleMap(seedTokens.colorError, theme, containerBg, recipe), + info: deriveColorRoleMap( + seedTokens.colorInfo, + theme, + containerBg, + recipe, + textBackgrounds, + ), + success: deriveColorRoleMap( + seedTokens.colorSuccess, + theme, + containerBg, + recipe, + textBackgrounds, + ), + warning: deriveColorRoleMap( + seedTokens.colorWarning, + theme, + containerBg, + recipe, + textBackgrounds, + ), + error: deriveColorRoleMap( + seedTokens.colorError, + theme, + containerBg, + recipe, + textBackgrounds, + ), } } @@ -409,6 +444,7 @@ function deriveColorRoleMap( theme: ThemeName, containerBg: string, recipe: ThemeColorRoleRecipe, + textBackgrounds: string | readonly string[] = containerBg, ): FunctionalColorMap { const normalizedSeed = normalizeHexColor(seed) ?? DEFAULT_CUSTOM_THEME_COLOR const palette = generateColorPalette(normalizedSeed, theme, containerBg) @@ -421,13 +457,16 @@ function deriveColorRoleMap( const background = mix(containerBg, color, recipe.backgroundMix) const backgroundHover = mix(containerBg, hover, recipe.backgroundHoverMix) const backgroundActive = mix(containerBg, active, recipe.backgroundActiveMix) - const textBackgrounds = [containerBg, background, backgroundHover, backgroundActive] - const resolveText = (source: ThemePaletteSource): string => { + const semanticBackgrounds = [background, backgroundHover, backgroundActive] + const resolveText = ( + source: ThemePaletteSource, + backgrounds: string | readonly string[] = textBackgrounds, + ): string => { const value = resolve(source) const toned = recipe.minimumTextLightness === undefined ? value : ensureLightness(value, recipe.minimumTextLightness) - return ensureContrast(toned, textBackgrounds, 4.5) + return ensureContrast(toned, backgrounds, 4.5) } return { @@ -445,6 +484,10 @@ function deriveColorRoleMap( : ensureContrast(active, containerBg, recipe.activeContrast), textHover: resolveText(recipe.textHover), text: resolveText(recipe.text), + textOnBackground: resolveText( + recipe.textOnBackground ?? recipe.text, + semanticBackgrounds, + ), textActive: resolveText(recipe.textActive), } } diff --git a/frontend/src/theme/derive/components.ts b/frontend/src/theme/derive/components.ts index d0cd9f647..f73f578f8 100644 --- a/frontend/src/theme/derive/components.ts +++ b/frontend/src/theme/derive/components.ts @@ -184,8 +184,8 @@ function deriveDarkComponentMap( `0 0 0 3px ${inputOutlineColor}, 0 14px 28px -22px ${withAlpha(BLACK, 0.72)}`, shadowStrength, ), - inputActiveShadow: '0 0 0 3px var(--cp-control-outline)', - inputErrorActiveShadow: `0 0 0 3px ${error.border}`, + inputActiveShadow: `0 0 0 3px ${inputOutlineColor}`, + inputErrorActiveShadow: `0 0 0 3px ${withAlpha(error.color, 0.28)}`, layoutSiderShadow: scaleShadowAlpha(`2px 0 18px -14px ${withAlpha(BLACK, 0.72)}`, shadowStrength), scrollbarThumbBg: mix(surfaces.colorBorderSecondary, surfaces.colorTextSecondary, 0.16), scrollbarThumbHoverBg: mix(surfaces.colorBorderSecondary, surfaces.colorTextSecondary, 0.3), @@ -237,8 +237,8 @@ function deriveLightComponentMap( `0 0 0 3px ${inputOutlineColor}, 0 12px 24px -16px ${withAlpha(LIGHT_SHADOW_BASE, 0.12)}`, shadowStrength, ), - inputActiveShadow: '0 0 0 3px var(--cp-control-outline)', - inputErrorActiveShadow: `0 0 0 3px ${error.border}`, + inputActiveShadow: `0 0 0 3px ${inputOutlineColor}`, + inputErrorActiveShadow: `0 0 0 3px ${withAlpha(error.color, 0.18)}`, layoutSiderShadow: scaleShadowAlpha(`2px 0 12px -12px ${withAlpha(LIGHT_SHADOW_BASE, 0.027)}`, shadowStrength), scrollbarThumbBg: mix(surfaces.colorBgLayout, surfaces.colorTextSecondary, 0.2), scrollbarThumbHoverBg: mix(surfaces.colorBgLayout, surfaces.colorTextSecondary, 0.34), diff --git a/frontend/src/theme/derive/roles.ts b/frontend/src/theme/derive/roles.ts index cd885474d..b0aac78c3 100644 --- a/frontend/src/theme/derive/roles.ts +++ b/frontend/src/theme/derive/roles.ts @@ -24,7 +24,8 @@ export const THEME_COLOR_ROLE_RECIPES = { hover: 5, active: 7, textHover: 5, - text: 6, + text: 7, + textOnBackground: 6, textActive: 7, backgroundMix: 0.08, backgroundHoverMix: 0.12, @@ -62,7 +63,8 @@ export const THEME_COLOR_ROLE_RECIPES = { hover: 8, active: 6, textHover: 8, - text: 9, + text: 7, + textOnBackground: 9, textActive: 10, backgroundMix: 0.18, backgroundHoverMix: 0.24, diff --git a/frontend/src/theme/types.ts b/frontend/src/theme/types.ts index a46edb0cc..4a6229d99 100644 --- a/frontend/src/theme/types.ts +++ b/frontend/src/theme/types.ts @@ -90,6 +90,7 @@ export interface ThemeColorRoleRecipe { active: ThemePaletteSource textHover: ThemePaletteSource text: ThemePaletteSource + textOnBackground?: ThemePaletteSource textActive: ThemePaletteSource backgroundMix: number backgroundHoverMix: number @@ -211,7 +212,10 @@ export interface FunctionalColorMap { border: string borderHover: string textHover: string + /** 语义色在中性表面上的强调文字。 */ text: string + /** 语义色在自身背景上的高对比文字。 */ + textOnBackground: string textActive: string } @@ -308,6 +312,7 @@ export type FunctionalTokenSuffix | '-border-hover' | '-text-hover' | '-text' + | '-text-on-bg' | '-text-active' type SemanticColorName = Extract diff --git a/frontend/src/views/account-groups/index.vue b/frontend/src/views/account-groups/index.vue index 74280ea91..19e51090e 100644 --- a/frontend/src/views/account-groups/index.vue +++ b/frontend/src/views/account-groups/index.vue @@ -121,7 +121,7 @@ const { allSelected, indeterminate, selectedRowKeys, toggleSelection, toggleAll {{ row.enabled ? '已启用' : '已禁用' }} diff --git a/frontend/src/views/accounts/components/AccountOverviewCards.vue b/frontend/src/views/accounts/components/AccountOverviewCards.vue index 1e58e82b7..faa51b67c 100644 --- a/frontend/src/views/accounts/components/AccountOverviewCards.vue +++ b/frontend/src/views/accounts/components/AccountOverviewCards.vue @@ -50,7 +50,7 @@ function overviewIconClass(tone: string) { return 'bg-cp-orange-bg text-cp-orange-text-on-bg' } if (tone === 'danger') { - return 'bg-cp-error-bg text-cp-error-text' + return 'bg-cp-error-bg text-cp-error-text-on-bg' } return 'bg-cp-blue-bg text-cp-blue-text-on-bg' } diff --git a/frontend/src/views/accounts/components/AccountQuotaPanel/ResetCredits.vue b/frontend/src/views/accounts/components/AccountQuotaPanel/ResetCredits.vue index 854eae4c8..d32bc75c7 100644 --- a/frontend/src/views/accounts/components/AccountQuotaPanel/ResetCredits.vue +++ b/frontend/src/views/accounts/components/AccountQuotaPanel/ResetCredits.vue @@ -140,9 +140,9 @@ async function handleConfirmConsume() {
- +
-

+

上次操作结果待确认

@@ -193,7 +193,7 @@ async function handleConfirmConsume() {

{{ loadError }},请刷新重试。 diff --git a/frontend/src/views/accounts/components/AccountStatusBadge/presenter.ts b/frontend/src/views/accounts/components/AccountStatusBadge/presenter.ts index b6be604f4..6dd7546de 100644 --- a/frontend/src/views/accounts/components/AccountStatusBadge/presenter.ts +++ b/frontend/src/views/accounts/components/AccountStatusBadge/presenter.ts @@ -53,26 +53,26 @@ const statusStyles: Record = { success: { text: 'text-cp-success-text', dot: 'bg-cp-success', - badge: 'bg-cp-success-bg text-cp-success-text', - icon: 'bg-cp-success-bg text-cp-success-text', + badge: 'bg-cp-success-bg text-cp-success-text-on-bg', + icon: 'bg-cp-success-bg text-cp-success-text-on-bg', }, danger: { text: 'text-cp-error-text', dot: 'bg-cp-error', - badge: 'bg-cp-error-bg text-cp-error-text', - icon: 'bg-cp-error-bg text-cp-error-text', + badge: 'bg-cp-error-bg text-cp-error-text-on-bg', + icon: 'bg-cp-error-bg text-cp-error-text-on-bg', }, warning: { text: 'text-cp-warning-text', dot: 'bg-cp-warning', - badge: 'bg-cp-warning-bg text-cp-warning-text', - icon: 'bg-cp-warning-bg text-cp-warning-text', + badge: 'bg-cp-warning-bg text-cp-warning-text-on-bg', + icon: 'bg-cp-warning-bg text-cp-warning-text-on-bg', }, info: { text: 'text-cp-info-text', dot: 'bg-cp-info', - badge: 'bg-cp-info-bg text-cp-info-text', - icon: 'bg-cp-info-bg text-cp-info-text', + badge: 'bg-cp-info-bg text-cp-info-text-on-bg', + icon: 'bg-cp-info-bg text-cp-info-text-on-bg', }, normal: { text: 'text-cp-text-secondary', diff --git a/frontend/src/views/accounts/composables/useAccountConnectionTest.ts b/frontend/src/views/accounts/composables/useAccountConnectionTest.ts index b9c20fadc..06c7214fb 100644 --- a/frontend/src/views/accounts/composables/useAccountConnectionTest.ts +++ b/frontend/src/views/accounts/composables/useAccountConnectionTest.ts @@ -191,7 +191,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise Promise Promise props.editing ? '编辑 API Key' : '创建 API Key' >

-

+

该密钥具有网关访问权限,请仅发送给可信调用方

diff --git a/frontend/src/views/api-keys/components/ApiKeyScopeCell.vue b/frontend/src/views/api-keys/components/ApiKeyScopeCell.vue index 1fb38f099..f22ccef3e 100644 --- a/frontend/src/views/api-keys/components/ApiKeyScopeCell.vue +++ b/frontend/src/views/api-keys/components/ApiKeyScopeCell.vue @@ -11,7 +11,7 @@ defineProps<{