🐛 fix(driver-manager): 统一驱动管理页明暗主题底色

Refs #440
This commit is contained in:
Syngnat
2026-05-08 20:28:41 +08:00
parent 1616ba8ae4
commit ab420e3d24
3 changed files with 140 additions and 15 deletions

View File

@@ -4,6 +4,7 @@ import { DeleteOutlined, DownloadOutlined, FileSearchOutlined, FolderOpenOutline
import { EventsOn } from '../../wailsjs/runtime/runtime';
import { useStore } from '../store';
import { normalizeOpacityForPlatform, resolveAppearanceValues } from '../utils/appearance';
import { buildDriverManagerWorkbenchTheme } from '../utils/driverManagerWorkbenchTheme';
import {
DRIVER_LOCAL_IMPORT_BUTTON_LABEL,
DRIVER_LOCAL_IMPORT_DIRECTORY_HELP,
@@ -178,6 +179,10 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
const darkMode = theme === 'dark';
const resolvedAppearance = resolveAppearanceValues(appearance);
const opacity = normalizeOpacityForPlatform(resolvedAppearance.opacity);
const driverManagerTheme = useMemo(
() => buildDriverManagerWorkbenchTheme(darkMode, opacity),
[darkMode, opacity],
);
const [loading, setLoading] = useState(false);
const [downloadDir, setDownloadDir] = useState('');
const [networkChecking, setNetworkChecking] = useState(false);
@@ -201,6 +206,33 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
downloadDirRef.current = downloadDir;
}, [downloadDir]);
const modalBodyStyle = useMemo<React.CSSProperties>(() => ({
maxHeight: 'calc(100vh - 220px)',
overflowY: 'auto',
overflowX: 'hidden',
paddingRight: 18,
background: driverManagerTheme.pageBg,
color: driverManagerTheme.titleText,
}), [driverManagerTheme]);
const managerSectionStyle = useMemo<React.CSSProperties>(() => ({
border: driverManagerTheme.sectionBorder,
borderRadius: 8,
background: driverManagerTheme.sectionBg,
}), [driverManagerTheme]);
const managerStatStyle = useMemo<React.CSSProperties>(() => ({
border: driverManagerTheme.statBorder,
borderRadius: 8,
background: driverManagerTheme.statBg,
}), [driverManagerTheme]);
const managerUpdateNoteStyle = useMemo<React.CSSProperties>(() => ({
border: driverManagerTheme.updateNoteBorder,
borderRadius: 8,
background: driverManagerTheme.updateNoteBg,
}), [driverManagerTheme]);
const appendOperationLog = useCallback((
driverType: string,
text: string,
@@ -1029,6 +1061,12 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
row.needsUpdate ? 'driver-manager-card-warning' : '',
row.connectable ? 'driver-manager-card-ready' : '',
].filter(Boolean).join(' ')}
style={{
border: row.needsUpdate
? driverManagerTheme.cardWarningBorder
: (row.connectable ? driverManagerTheme.cardReadyBorder : driverManagerTheme.cardBorder),
background: driverManagerTheme.cardBg,
}}
>
<div className="driver-manager-card-main">
<div className="driver-manager-card-info">
@@ -1043,7 +1081,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
{affectedText ? <Text type="secondary">{affectedText}</Text> : null}
</div>
{row.needsUpdate && issueText ? (
<div className="driver-manager-update-note">
<div className="driver-manager-update-note" style={managerUpdateNoteStyle}>
<Text strong type="warning"></Text>
<Paragraph
className="driver-manager-note-text"
@@ -1115,12 +1153,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
style={{ top: 24 }}
className="driver-manager-modal"
styles={{
body: {
maxHeight: 'calc(100vh - 220px)',
overflowY: 'auto',
overflowX: 'hidden',
paddingRight: 18,
},
body: modalBodyStyle,
}}
destroyOnHidden
footer={(
@@ -1137,26 +1170,26 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
</Space>
)}
>
<div className="driver-manager-shell">
<div className="driver-manager-header">
<div className="driver-manager-shell" data-driver-theme={driverManagerTheme.isDark ? 'dark' : 'light'}>
<div className="driver-manager-header" style={managerSectionStyle}>
<div className="driver-manager-heading">
<Text type="secondary"> MySQL / Redis / Oracle / PostgreSQL </Text>
<Text type="secondary">GoNavi agent </Text>
</div>
<div className="driver-manager-stats">
<div className="driver-manager-stat">
<div className="driver-manager-stat" style={managerStatStyle}>
<span>{statusSummary.total}</span>
<Text type="secondary"></Text>
</div>
<div className="driver-manager-stat">
<div className="driver-manager-stat" style={managerStatStyle}>
<span>{statusSummary.enabled}</span>
<Text type="secondary"></Text>
</div>
<div className="driver-manager-stat driver-manager-stat-warning">
<span>{statusSummary.needsUpdate}</span>
<div className="driver-manager-stat driver-manager-stat-warning" style={managerStatStyle}>
<span style={{ color: driverManagerTheme.warningText }}>{statusSummary.needsUpdate}</span>
<Text type="secondary"></Text>
</div>
<div className="driver-manager-stat">
<div className="driver-manager-stat" style={managerStatStyle}>
<span>{statusSummary.notEnabled}</span>
<Text type="secondary"></Text>
</div>
@@ -1239,7 +1272,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
/>
)}
<div className="driver-manager-directory-panel">
<div className="driver-manager-directory-panel" style={managerSectionStyle}>
<Collapse
size="small"
ghost

View File

@@ -0,0 +1,31 @@
import { describe, expect, it } from 'vitest';
import { buildDriverManagerWorkbenchTheme } from './driverManagerWorkbenchTheme';
describe('driverManagerWorkbenchTheme', () => {
it('builds a dark driver manager theme with dark surfaces', () => {
const theme = buildDriverManagerWorkbenchTheme(true, 0.72);
expect(theme.isDark).toBe(true);
expect(theme.pageBg).toBe('rgb(31, 31, 31)');
expect(theme.sectionBg).toBe('rgb(31, 31, 31)');
expect(theme.cardBg).toBe('rgb(31, 31, 31)');
expect(theme.statBg).toBe('rgb(31, 31, 31)');
expect(theme.updateNoteBg).toBe('rgb(31, 31, 31)');
expect(theme.titleText).toBe('#f5f7ff');
expect(theme.warningText).toBe('#f6c453');
});
it('builds a light driver manager theme with light surfaces', () => {
const theme = buildDriverManagerWorkbenchTheme(false, 0.92);
expect(theme.isDark).toBe(false);
expect(theme.pageBg).toBe('rgb(255, 255, 255)');
expect(theme.sectionBg).toBe('rgb(255, 255, 255)');
expect(theme.cardBg).toBe('rgb(255, 255, 255)');
expect(theme.statBg).toBe('rgb(255, 255, 255)');
expect(theme.updateNoteBg).toBe('rgb(255, 255, 255)');
expect(theme.titleText).toBe('rgba(5, 5, 5, 0.92)');
expect(theme.warningText).toBe('#d48806');
});
});

View File

@@ -0,0 +1,61 @@
export type DriverManagerWorkbenchTheme = {
isDark: boolean;
pageBg: string;
sectionBg: string;
sectionBorder: string;
cardBg: string;
cardBorder: string;
cardWarningBorder: string;
cardReadyBorder: string;
statBg: string;
statBorder: string;
updateNoteBg: string;
updateNoteBorder: string;
mutedText: string;
titleText: string;
warningText: string;
};
export const buildDriverManagerWorkbenchTheme = (darkMode: boolean, _opacity: number): DriverManagerWorkbenchTheme => {
if (darkMode) {
const darkSurface = 'rgb(31, 31, 31)';
return {
isDark: true,
pageBg: darkSurface,
sectionBg: darkSurface,
sectionBorder: '1px solid rgba(255, 255, 255, 0.08)',
cardBg: darkSurface,
cardBorder: '1px solid rgba(255, 255, 255, 0.08)',
cardWarningBorder: '1px solid rgba(250, 173, 20, 0.35)',
cardReadyBorder: '1px solid rgba(82, 196, 26, 0.22)',
statBg: darkSurface,
statBorder: '1px solid rgba(255, 255, 255, 0.08)',
updateNoteBg: darkSurface,
updateNoteBorder: '1px solid rgba(250, 173, 20, 0.24)',
mutedText: 'rgba(255, 255, 255, 0.62)',
titleText: '#f5f7ff',
warningText: '#f6c453',
};
}
const lightSurface = 'rgb(255, 255, 255)';
return {
isDark: false,
pageBg: lightSurface,
sectionBg: lightSurface,
sectionBorder: '1px solid rgba(5, 5, 5, 0.08)',
cardBg: lightSurface,
cardBorder: '1px solid rgba(5, 5, 5, 0.08)',
cardWarningBorder: '1px solid rgba(250, 173, 20, 0.35)',
cardReadyBorder: '1px solid rgba(82, 196, 26, 0.22)',
statBg: lightSurface,
statBorder: '1px solid rgba(5, 5, 5, 0.08)',
updateNoteBg: lightSurface,
updateNoteBorder: '1px solid rgba(250, 173, 20, 0.24)',
mutedText: 'rgba(5, 5, 5, 0.62)',
titleText: 'rgba(5, 5, 5, 0.92)',
warningText: '#d48806',
};
};