feat(字体): 添加应用字体和代码字体自定义功能

- 在设置面板中新增应用字体和代码字体选择器
- 支持霞鹜文楷、思源宋体、站酷快乐体等多种中文字体
- 代码编辑器支持 Fira Code 等编程字体
- 通过 CSS 变量动态切换字体,确保界面一致性
This commit is contained in:
cfq 2026-01-26 18:56:59 +08:00
parent d774524881
commit ef33564e99
4 changed files with 73 additions and 13 deletions

View File

@ -5,7 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=Fira+Code:wght@400;500&family=Noto+Serif+SC:wght@400;700&family=ZCOOL+KuaiLe&display=swap" rel="stylesheet">
<!-- 引入霞鹜文楷 -->
<link rel="stylesheet" href="https://npm.elemecdn.com/lxgw-wenkai-screen-web/style.css" media="print" onload="this.media='all'">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -14,7 +14,18 @@ import { SettingOutlined } from '@ant-design/icons-vue';
const { setRootPath } = useFileTree(); const { setRootPath } = useFileTree();
const { loadConfig } = useConfig(); const { loadConfig } = useConfig();
const { getAntdTheme, primaryColor, toggleTheme, isDark, toggleDarkMode, setGlobalTheme } = useTheme(); const {
getAntdTheme,
primaryColor,
toggleTheme,
isDark,
toggleDarkMode,
setGlobalTheme,
fontFamily,
codeFontFamily,
setFontFamily,
setCodeFontFamily
} = useTheme();
const route = ref(""); const route = ref("");
const showSearch = ref(false); const showSearch = ref(false);
@ -271,6 +282,22 @@ const { state } = useFileTree(); // 需要获取 rootPath 用于保存
<div class="settings-label">暗黑模式</div> <div class="settings-label">暗黑模式</div>
<a-switch v-model:checked="isDark" @change="toggleDarkMode" /> <a-switch v-model:checked="isDark" @change="toggleDarkMode" />
</div> </div>
<div class="settings-item">
<div class="settings-label">应用字体</div>
<a-select :value="fontFamily" style="width: 100%" @change="setFontFamily">
<a-select-option value="default">系统默认</a-select-option>
<a-select-option value="lxgw">霞鹜文楷 (LXGW WenKai)</a-select-option>
<a-select-option value="serif">思源宋体 (Noto Serif)</a-select-option>
<a-select-option value="zcool">站酷快乐体 (演示用)</a-select-option>
</a-select>
</div>
<div class="settings-item">
<div class="settings-label">代码字体</div>
<a-select :value="codeFontFamily" style="width: 100%" @change="setCodeFontFamily">
<a-select-option value="default">系统默认 (Monospace)</a-select-option>
<a-select-option value="fira">Fira Code</a-select-option>
</a-select>
</div>
</a-modal> </a-modal>
<!-- 历史记录弹窗 --> <!-- 历史记录弹窗 -->

View File

@ -49,16 +49,13 @@ let detachPaste = null
let removeActionListener = null let removeActionListener = null
const liveEditorTheme = computed(() => { const liveEditorTheme = computed(() => {
const contentFont = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif'
const monospaceFont = 'SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace'
return EditorView.theme({ return EditorView.theme({
'&': { '&': {
backgroundColor: 'var(--card-background)', backgroundColor: 'var(--card-background)',
color: 'var(--text-color)' color: 'var(--text-color)'
}, },
'.cm-scroller': { '.cm-scroller': {
fontFamily: contentFont fontFamily: 'var(--app-font-family)'
}, },
'.cm-content': { '.cm-content': {
padding: '30px 40px', padding: '30px 40px',
@ -67,6 +64,8 @@ const liveEditorTheme = computed(() => {
margin: '0 auto', margin: '0 auto',
caretColor: 'var(--primary-color)' caretColor: 'var(--primary-color)'
}, },
// ... monospaceFont
'.cm-selectionBackground, ::selection': { '.cm-selectionBackground, ::selection': {
backgroundColor: 'var(--primary-color-bg)' backgroundColor: 'var(--primary-color-bg)'
}, },
@ -138,7 +137,7 @@ const liveEditorTheme = computed(() => {
// //
'.cm-md-monospace': { '.cm-md-monospace': {
fontFamily: monospaceFont, fontFamily: 'var(--editor-font-family)',
backgroundColor: 'var(--hover-background)', backgroundColor: 'var(--hover-background)',
padding: '2px 4px', padding: '2px 4px',
borderRadius: '4px', borderRadius: '4px',
@ -147,7 +146,7 @@ const liveEditorTheme = computed(() => {
// //
'.cm-line.cm-md-fenced-code': { '.cm-line.cm-md-fenced-code': {
fontFamily: monospaceFont, fontFamily: 'var(--editor-font-family)',
backgroundColor: 'var(--hover-background)', backgroundColor: 'var(--hover-background)',
paddingLeft: '16px', paddingLeft: '16px',
fontSize: '0.9em' fontSize: '0.9em'
@ -160,7 +159,7 @@ const liveEditorTheme = computed(() => {
'.cm-md-code-info': { '.cm-md-code-info': {
opacity: 0.5, opacity: 0.5,
fontSize: '0.85em', fontSize: '0.85em',
fontFamily: contentFont, fontFamily: 'var(--app-font-family)',
float: 'right', float: 'right',
marginRight: '8px' marginRight: '8px'
}, },
@ -186,7 +185,7 @@ const liveEditorTheme = computed(() => {
}, },
// //
'.cm-line.cm-md-table-source': { '.cm-line.cm-md-table-source': {
fontFamily: monospaceFont, fontFamily: 'var(--editor-font-family)',
whiteSpace: 'pre' whiteSpace: 'pre'
}, },

View File

@ -3,6 +3,8 @@ import { theme } from 'ant-design-vue';
const primaryColor = ref('#1890ff'); const primaryColor = ref('#1890ff');
const isDark = ref(false); const isDark = ref(false);
const fontFamily = ref('default');
const codeFontFamily = ref('default');
export function useTheme() { export function useTheme() {
const toggleTheme = (color) => { const toggleTheme = (color) => {
@ -15,15 +17,41 @@ export function useTheme() {
updateCSSVariables(); updateCSSVariables();
}; };
const setFontFamily = (font) => {
fontFamily.value = font;
updateCSSVariables();
};
const setCodeFontFamily = (font) => {
codeFontFamily.value = font;
updateCSSVariables();
};
const updateCSSVariables = () => { const updateCSSVariables = () => {
const root = document.documentElement; const root = document.documentElement;
root.dataset.theme = isDark.value ? 'dark' : 'light'; root.dataset.theme = isDark.value ? 'dark' : 'light';
root.style.setProperty('--primary-color', primaryColor.value); root.style.setProperty('--primary-color', primaryColor.value);
// 生成一个浅色背景用于选中态 // 生成一个浅色背景用于选中态
// 简单变淡处理,或者直接使用 opacity
root.style.setProperty('--primary-color-bg', primaryColor.value + '1A'); // 10% opacity root.style.setProperty('--primary-color-bg', primaryColor.value + '1A'); // 10% opacity
// 字体设置
let fontValue = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
if (fontFamily.value === 'lxgw') {
fontValue = '"LXGW WenKai Screen", sans-serif';
} else if (fontFamily.value === 'serif') {
fontValue = '"Noto Serif SC", serif';
} else if (fontFamily.value === 'zcool') {
fontValue = '"ZCOOL KuaiLe", cursive';
}
root.style.setProperty('--app-font-family', fontValue);
let codeFontValue = 'SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace';
if (codeFontFamily.value === 'fira') {
codeFontValue = '"Fira Code", monospace';
}
root.style.setProperty('--editor-font-family', codeFontValue);
if (isDark.value) { if (isDark.value) {
root.style.setProperty('--app-background', '#141414'); root.style.setProperty('--app-background', '#141414');
root.style.setProperty('--card-background', '#1f1f1f'); root.style.setProperty('--card-background', '#1f1f1f');
@ -79,6 +107,10 @@ export function useTheme() {
toggleTheme, toggleTheme,
toggleDarkMode, toggleDarkMode,
getAntdTheme, getAntdTheme,
setGlobalTheme setGlobalTheme,
fontFamily,
codeFontFamily,
setFontFamily,
setCodeFontFamily
}; };
} }