fix(desktop): 扩充菜单并应用功能页主题
This commit is contained in:
@@ -26,7 +26,7 @@ describe('桌面顶部段落菜单', () => {
|
||||
editor.currentFilePath = '/示例.md'
|
||||
editor.saveStatus = 'saved'
|
||||
const wrapper = mount(TitleBarMenu)
|
||||
expect(wrapper.text()).toContain('文件编辑段落视图')
|
||||
expect(wrapper.text()).toContain('文件编辑段落格式视图主题帮助')
|
||||
await wrapper.get('[data-menu="paragraph"] .menu-trigger').trigger('click')
|
||||
const item = wrapper.get('.import-properties')
|
||||
expect((item.element as HTMLButtonElement).disabled).toBe(false)
|
||||
@@ -51,7 +51,7 @@ describe('桌面顶部段落菜单', () => {
|
||||
it('标题命令传入对应级别并显示快捷键', async () => {
|
||||
const wrapper = mount(TitleBarMenu)
|
||||
await wrapper.get('[data-menu="paragraph"] .menu-trigger').trigger('click')
|
||||
const headings = wrapper.findAll('[data-menu="paragraph"] [role="menuitem"]').filter(item => item.text().startsWith('标题'))
|
||||
const headings = wrapper.findAll('[data-menu="paragraph"] [role="menuitem"]').filter(item => item.text().includes('级标题'))
|
||||
await headings[1].trigger('click')
|
||||
expect(execute).toHaveBeenCalledWith('editor.heading', 2)
|
||||
await wrapper.get('[data-menu="paragraph"] .menu-trigger').trigger('click')
|
||||
|
||||
@@ -4,10 +4,12 @@ import { useEditorStore } from '@/stores/editor'
|
||||
import { useThemeStore } from '@/stores/theme'
|
||||
import { executeEditorCommand, getEditorCommandCapabilities, type EditorCommandId } from '@/services/editorCommandService'
|
||||
import { t } from '@/i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
type MenuName = 'file' | 'edit' | 'paragraph' | 'view'
|
||||
type MenuName = 'file' | 'edit' | 'paragraph' | 'format' | 'view' | 'theme' | 'help'
|
||||
const editor = useEditorStore()
|
||||
const theme = useThemeStore()
|
||||
const router = useRouter()
|
||||
const open = ref<MenuName | null>(null)
|
||||
const bar = ref<HTMLElement | null>(null)
|
||||
const error = ref('')
|
||||
@@ -32,6 +34,8 @@ async function command(id: EditorCommandId, params?: unknown) {
|
||||
async function save() { await editor.save(); close() }
|
||||
function setMode(mode: 'source' | 'wysiwyg') { editor.setMode(mode); close() }
|
||||
function toggleTheme() { theme.toggleTheme(); close() }
|
||||
function applyTheme(id: string) { theme.applyTheme(id); close() }
|
||||
function navigate(path: string) { void router.push(path); close() }
|
||||
|
||||
onMounted(() => window.addEventListener('pointerdown', dismiss))
|
||||
onBeforeUnmount(() => window.removeEventListener('pointerdown', dismiss))
|
||||
@@ -42,6 +46,8 @@ onBeforeUnmount(() => window.removeEventListener('pointerdown', dismiss))
|
||||
<div class="menu-group" data-menu="file">
|
||||
<button class="menu-trigger" :aria-expanded="open === 'file'" aria-haspopup="menu" @click="toggle('file')" @keydown.down.prevent="focusFirst('file')">{{ t('文件', 'File') }}</button>
|
||||
<div v-if="open === 'file'" class="menu-popover" role="menu">
|
||||
<button role="menuitem" @click="navigate('/')"><span>{{ t('选择知识库…', 'Choose knowledge base…') }}</span></button>
|
||||
<span class="menu-separator" role="separator" />
|
||||
<button role="menuitem" :disabled="!editor.currentFilePath || ['saving','conflict','external_changed'].includes(editor.saveStatus)" @click="save">
|
||||
<span>{{ t('保存', 'Save') }}</span><kbd>Ctrl+S</kbd>
|
||||
</button>
|
||||
@@ -58,7 +64,7 @@ onBeforeUnmount(() => window.removeEventListener('pointerdown', dismiss))
|
||||
<button class="menu-trigger" :aria-expanded="open === 'paragraph'" aria-haspopup="menu" @click="toggle('paragraph')" @keydown.down.prevent="focusFirst('paragraph')">{{ t('段落', 'Paragraph') }}</button>
|
||||
<div v-if="open === 'paragraph'" class="menu-popover paragraph-menu" role="menu">
|
||||
<button role="menuitem" :disabled="!enabled('editor.paragraph')" @click="command('editor.paragraph')"><span>{{ t('正文', 'Body text') }}</span></button>
|
||||
<button v-for="level in 3" :key="level" role="menuitem" :disabled="!enabled('editor.heading')" @click="command('editor.heading', level)"><span>{{ t(`标题 ${level}`, `Heading ${level}`) }}</span></button>
|
||||
<button v-for="level in 6" :key="level" role="menuitem" :disabled="!enabled('editor.heading')" @click="command('editor.heading', level)"><span>{{ t(`${level} 级标题`, `Heading ${level}`) }}</span><kbd>Ctrl+{{ level }}</kbd></button>
|
||||
<span class="menu-separator" role="separator" />
|
||||
<button role="menuitem" :disabled="!enabled('editor.bullet-list')" @click="command('editor.bullet-list')"><span>{{ t('无序列表', 'Bullet list') }}</span></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.ordered-list')" @click="command('editor.ordered-list')"><span>{{ t('有序列表', 'Ordered list') }}</span></button>
|
||||
@@ -72,13 +78,44 @@ onBeforeUnmount(() => window.removeEventListener('pointerdown', dismiss))
|
||||
<small v-if="!enabled('editor.import-note-properties')">{{ t('请在无冲突的 Markdown 源码笔记中使用', 'Available in a conflict-free Markdown source note') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group" data-menu="format">
|
||||
<button class="menu-trigger" :aria-expanded="open === 'format'" aria-haspopup="menu" @click="toggle('format')" @keydown.down.prevent="focusFirst('format')">{{ t('格式', 'Format') }}</button>
|
||||
<div v-if="open === 'format'" class="menu-popover" role="menu">
|
||||
<button role="menuitem" :disabled="!enabled('editor.bold')" @click="command('editor.bold')"><span>{{ t('加粗', 'Bold') }}</span><kbd>Ctrl+B</kbd></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.italic')" @click="command('editor.italic')"><span>{{ t('斜体', 'Italic') }}</span><kbd>Ctrl+I</kbd></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.strikethrough')" @click="command('editor.strikethrough')"><span>{{ t('删除线', 'Strikethrough') }}</span></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.inline-code')" @click="command('editor.inline-code')"><span>{{ t('行内代码', 'Inline code') }}</span></button>
|
||||
<span class="menu-separator" role="separator" />
|
||||
<button role="menuitem" :disabled="!enabled('editor.code-block')" @click="command('editor.code-block')"><span>{{ t('代码块', 'Code block') }}</span><kbd>Ctrl+Shift+K</kbd></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.math-block')" @click="command('editor.math-block')"><span>{{ t('公式块', 'Math block') }}</span><kbd>Ctrl+Shift+M</kbd></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.callout')" @click="command('editor.callout', { type: 'NOTE', body: t('提示内容', 'Callout content') })"><span>{{ t('警告框', 'Callout') }}</span></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.horizontal-rule')" @click="command('editor.horizontal-rule')"><span>{{ t('水平分割线', 'Horizontal rule') }}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group" data-menu="view">
|
||||
<button class="menu-trigger" :aria-expanded="open === 'view'" aria-haspopup="menu" @click="toggle('view')" @keydown.down.prevent="focusFirst('view')">{{ t('视图', 'View') }}</button>
|
||||
<div v-if="open === 'view'" class="menu-popover" role="menu">
|
||||
<button role="menuitemradio" :aria-checked="editor.mode === 'wysiwyg'" @click="setMode('wysiwyg')"><span>✓ {{ t('写作模式', 'Writing mode') }}</span></button>
|
||||
<button role="menuitemradio" :aria-checked="editor.mode === 'source'" @click="setMode('source')"><span>✓ {{ t('源码模式', 'Source mode') }}</span></button>
|
||||
<span class="menu-separator" role="separator" />
|
||||
<button role="menuitem" @click="toggleTheme"><span>{{ theme.isDark ? t('浅色主题', 'Light theme') : t('深色主题', 'Dark theme') }}</span></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.heading.fold-all')" @click="command('editor.heading.fold-all')"><span>{{ t('全部折叠标题', 'Fold all headings') }}</span></button>
|
||||
<button role="menuitem" :disabled="!enabled('editor.heading.unfold-all')" @click="command('editor.heading.unfold-all')"><span>{{ t('全部展开标题', 'Unfold all headings') }}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group" data-menu="theme">
|
||||
<button class="menu-trigger" :aria-expanded="open === 'theme'" aria-haspopup="menu" @click="toggle('theme')" @keydown.down.prevent="focusFirst('theme')">{{ t('主题', 'Theme') }}</button>
|
||||
<div v-if="open === 'theme'" class="menu-popover" role="menu">
|
||||
<button v-for="item in theme.allThemes" :key="item.theme_id" role="menuitemradio" :aria-checked="theme.currentThemeId === item.theme_id" @click="applyTheme(item.theme_id)"><span>{{ theme.currentThemeId === item.theme_id ? '✓ ' : '' }}{{ item.name }}</span></button>
|
||||
<span class="menu-separator" role="separator" />
|
||||
<button role="menuitem" @click="toggleTheme"><span>{{ theme.isDark ? t('切换为浅色', 'Switch to light') : t('切换为深色', 'Switch to dark') }}</span></button>
|
||||
<button role="menuitem" @click="navigate('/themes')"><span>{{ t('管理主题…', 'Manage themes…') }}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-group" data-menu="help">
|
||||
<button class="menu-trigger" :aria-expanded="open === 'help'" aria-haspopup="menu" @click="toggle('help')" @keydown.down.prevent="focusFirst('help')">{{ t('帮助', 'Help') }}</button>
|
||||
<div v-if="open === 'help'" class="menu-popover" role="menu">
|
||||
<button role="menuitem" @click="navigate('/logs')"><span>{{ t('运行日志', 'Operation logs') }}</span></button>
|
||||
<button role="menuitem" @click="navigate('/settings')"><span>{{ t('设置与诊断', 'Settings and diagnostics') }}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="error" class="menu-error" role="alert">{{ error }}</span>
|
||||
|
||||
Reference in New Issue
Block a user