From a442087ea9559e59a6e8f0fd7cd655a37a370053 Mon Sep 17 00:00:00 2001 From: GouJ Date: Tue, 22 Sep 2026 19:16:40 +0800 Subject: [PATCH] =?UTF-8?q?style:=20=E6=9B=B4=E6=96=B0=E5=93=81=E7=89=8C?= =?UTF-8?q?=E6=A0=87=E8=AF=86=E4=B8=8E=E4=BE=A7=E6=A0=8F=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 26 ++++++++++++------------ src/components/ui/StudioLogo.vue | 32 ++++++++++++++++++++++++++++++ src/components/ui/index.ts | 1 + tests/components/ui/layout.test.ts | 23 +++++++++++++++------ 4 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 src/components/ui/StudioLogo.vue diff --git a/src/App.vue b/src/App.vue index 0567808..6975327 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,8 +14,8 @@ import { type MenuOption } from 'naive-ui' import { - Archive, Clapperboard, + Archive, Camera, Images, Palette, @@ -23,11 +23,12 @@ import { FolderOpen, FileText, Layers, - Menu, + PanelLeftClose, + PanelLeftOpen, Settings2, X } from '@lucide/vue' -import { AppDialog } from './components/ui' +import { AppDialog, StudioLogo } from './components/ui' import { useTheme } from './composables/useTheme' import ThemeToggle from './components/ui/ThemeToggle.vue' import { projectAccessKey, type ProjectAccess } from './features/projects/access' @@ -195,7 +196,7 @@ const menuOptions = computed(() => { :class="{ 'is-collapsed': collapsed }" aria-label="短剧工作台首页" > - 短剧工作台SHORT DRAMA STUDIO @@ -249,7 +250,9 @@ const menuOptions = computed(() => { aria-controls="main-navigation" @click="toggleNav" > - + {{ route.meta.title || '工作空间' }} @@ -362,20 +365,17 @@ const menuOptions = computed(() => { @apply flex shrink-0 items-center gap-3 h-[74px] overflow-hidden py-0 px-5 text-ink whitespace-nowrap; min-width: var(--admin-sider-expanded); } -.admin-brand > svg { - @apply shrink-0; -} -.admin-brand span { +.admin-brand-copy { @apply text-[15px] font-semibold; opacity: 1; transition: opacity 0.2s ease, transform 0.2s ease; } -.admin-sider.is-collapsed .admin-brand span { +.admin-sider.is-collapsed .admin-brand-copy { opacity: 0; } -.admin-brand small { +.admin-brand-copy small { @apply block text-[8px] tracking-[1px] font-normal text-muted mt-[3px]; } .admin-topbar { @@ -525,7 +525,7 @@ const menuOptions = computed(() => { .admin-brand { @apply h-12 min-w-0 flex-1 justify-start px-3.5; } - .admin-sider.is-collapsed .admin-brand span, + .admin-sider.is-collapsed .admin-brand-copy, .admin-sider.is-collapsed .n-menu-item-content-header, .admin-sider.is-collapsed .admin-settings-label { opacity: 1; @@ -545,7 +545,7 @@ const menuOptions = computed(() => { .admin-sider.is-collapsed .admin-sider-footer, .admin-sider .n-menu-item-content-header, .admin-menu-label, - .admin-brand span, + .admin-brand-copy, .admin-settings-label { transition: none; } diff --git a/src/components/ui/StudioLogo.vue b/src/components/ui/StudioLogo.vue new file mode 100644 index 0000000..7a2b92f --- /dev/null +++ b/src/components/ui/StudioLogo.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index 539c9ac..00d3345 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -2,6 +2,7 @@ export { default as AppDialog } from './AppDialog.vue' export { default as EmptyState } from './EmptyState.vue' export { default as StatusBadge } from './StatusBadge.vue' +export { default as StudioLogo } from './StudioLogo.vue' export { default as AssetImage } from './AssetImage.vue' export { default as RevealImage } from './RevealImage.vue' export { default as DirectoryItem } from './DirectoryItem.vue' diff --git a/tests/components/ui/layout.test.ts b/tests/components/ui/layout.test.ts index f0627c0..e6ee332 100644 --- a/tests/components/ui/layout.test.ts +++ b/tests/components/ui/layout.test.ts @@ -132,7 +132,9 @@ describe('管理后台组件边界', () => { expect(wrapper.getComponent(ThemeToggle).findComponent(NTooltip).exists()).toBe(false) expect(wrapper.get('.theme-toggle').attributes('title')).toBeUndefined() expect(wrapper.get('.theme-toggle svg').classes()).toContain('lucide-monitor') - expect(wrapper.get('.admin-topbar [aria-controls="main-navigation"] svg').classes()).toContain('lucide-menu') + expect(wrapper.get('.admin-topbar [aria-controls="main-navigation"] svg').classes()).toContain( + 'lucide-panel-left-close' + ) const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' for (const [key, label, icon] of [ ['dark', '暗黑模式', 'moon'], @@ -173,9 +175,14 @@ describe('管理后台组件边界', () => { } expect(firstMenuIcon().type).not.toBe(NPopover) expect(wrapper.get('.admin-sider-footer').findComponent(NPopover).exists()).toBe(false) + expect(wrapper.get('[aria-label="折叠侧栏"] svg').classes()).toContain('lucide-panel-left-close') + expect(wrapper.findAll('.studio-logo svg')).toHaveLength(2) + expect(wrapper.find('.studio-logo .lucide-film').exists()).toBe(true) + expect(wrapper.find('.studio-logo .lucide-sparkles').exists()).toBe(true) await wrapper.get('[aria-label="折叠侧栏"]').trigger('click') await flushPromises() expect(wrapper.find('[aria-label="展开侧栏"]').exists()).toBe(true) + expect(wrapper.get('[aria-label="展开侧栏"] svg').classes()).toContain('lucide-panel-left-open') expect(wrapper.getComponent(NMenu).props('collapsed')).toBe(false) expect(firstMenuIcon().type).not.toBe(NPopover) expect(wrapper.get('.admin-sider').attributes('style')).toContain('width: 64px') @@ -216,7 +223,7 @@ describe('管理后台组件边界', () => { expect(css).toMatch(/\.admin-sider\.is-collapsed \.n-menu-item-content__icon\s*\{[^}]*pointer-events:\s*none/) expect(css).toMatch(/\.admin-sider-footer\s*\{[^}]*flex-shrink:\s*0/) expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-settings-label\s*\{[^}]*opacity:\s*0/) - expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-brand span\s*\{[^}]*opacity:\s*0/) + expect(css).toMatch(/\.admin-sider\.is-collapsed \.admin-brand-copy\s*\{[^}]*opacity:\s*0/) expect(css).toMatch(/@media \(max-width: 800px\)[\s\S]*?\.admin-brand\s*\{[^}]*height:\s*48px/) expect(css).toContain('@media (max-width: 800px)') expect(css).toMatch(/\.admin-sider\s*\{[^}]*transition:\s*width\s+0\.32s/) @@ -296,7 +303,7 @@ describe('管理后台组件边界', () => { const collapsedSettings = wrapper.get('.admin-sider-footer [aria-label="后端连接"]') expect(collapsedSettings.text()).toBe('后端连接') expect(collapsedSettings.classes()).toContain('is-collapsed') - expect(wrapper.get('.admin-topbar [aria-label="打开主菜单"] svg').classes()).toContain('lucide-menu') + expect(wrapper.get('.admin-topbar [aria-label="打开主菜单"] svg').classes()).toContain('lucide-panel-left-open') await wrapper.get('.admin-topbar [aria-label="打开主菜单"]').trigger('click') await flushPromises() expect(wrapper.findAll('.admin-sider')).toHaveLength(1) @@ -307,6 +314,9 @@ describe('管理后台组件边界', () => { expect(wrapper.find('.admin-sider-mask').exists()).toBe(false) expect(wrapper.get('.admin-sider').findAll('.n-menu .n-menu-item')).toHaveLength(9) expect(wrapper.get('.admin-topbar [aria-label="关闭主菜单"]').attributes('aria-expanded')).toBe('true') + expect(wrapper.get('.admin-topbar [aria-label="关闭主菜单"] svg').classes()).toContain( + 'lucide-panel-left-close' + ) const expandedBrand = wrapper.get('.admin-brand') expect(expandedBrand.classes()).not.toContain('is-collapsed') expect(expandedBrand.text()).toContain('短剧工作台') @@ -384,7 +394,7 @@ describe('管理后台组件边界', () => { expect(document.querySelector('.n-drawer-container')).toBeNull() }) - it('完成项目解锁全部左侧链接,移除项目标题栏并将项目名写入浏览器标题', async () => { + it('完成项目解锁全部左侧链接,标题不再显示状态标签和刷新按钮', async () => { const detail = vi.spyOn(projectsApi, 'detail').mockResolvedValue({ id: 'navigation-test', title: '导航测试项目', @@ -424,8 +434,7 @@ describe('管理后台组件边界', () => { wrapper = mount(App, { attachTo: document.body, global: { plugins: [router] } }) await flushPromises() expect(wrapper.find('[aria-label="项目工作流"]').exists()).toBe(false) - expect(wrapper.find('.project-header').exists()).toBe(false) - expect(document.title).toBe('导航测试项目 · 工作空间 · 短剧工作台') + expect(wrapper.get('.project-title').text()).toBe('导航测试项目') for (const path of paths) { await wrapper.get(`.n-menu a[href="/projects/navigation-test/${path}"]`).trigger('click') await flushPromises() @@ -433,6 +442,8 @@ describe('管理后台组件边界', () => { expect(wrapper.get('.project-view .workspace-page').text()).toBe(path) } expect(detail).toHaveBeenCalledOnce() + expect(wrapper.find('.project-header .n-button').exists()).toBe(false) + expect(wrapper.find('.project-header .n-tag').exists()).toBe(false) }) it('确认按钮有稳定根元素承接调用方间距,生产按钮显式保留上下留白', () => {