style: 更新品牌标识与侧栏切换图标
This commit is contained in:
+13
-13
@@ -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<MenuOption[]>(() => {
|
||||
:class="{ 'is-collapsed': collapsed }"
|
||||
aria-label="短剧工作台首页"
|
||||
>
|
||||
<Clapperboard :size="narrow ? NAV_ICON_SIZE : 24" /><span
|
||||
<StudioLogo :size="narrow ? 23 : 27" /><span class="admin-brand-copy"
|
||||
>短剧工作台<small>SHORT DRAMA STUDIO</small></span
|
||||
>
|
||||
</RouterLink>
|
||||
@@ -249,7 +250,9 @@ const menuOptions = computed<MenuOption[]>(() => {
|
||||
aria-controls="main-navigation"
|
||||
@click="toggleNav"
|
||||
>
|
||||
<template #icon><Menu :size="20" /></template>
|
||||
<template #icon
|
||||
><component :is="collapsed ? PanelLeftOpen : PanelLeftClose" :size="20"
|
||||
/></template>
|
||||
</NButton>
|
||||
<span class="truncate text-sm">{{ route.meta.title || '工作空间' }}</span>
|
||||
</div>
|
||||
@@ -362,20 +365,17 @@ const menuOptions = computed<MenuOption[]>(() => {
|
||||
@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<MenuOption[]>(() => {
|
||||
.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<MenuOption[]>(() => {
|
||||
.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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { Film, Sparkles } from '@lucide/vue'
|
||||
|
||||
withDefaults(defineProps<{ size?: number }>(), { size: 28 })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="studio-logo" :style="{ width: `${size}px`, height: `${size}px` }" aria-hidden="true">
|
||||
<Film class="studio-logo-film" :size="size" :stroke-width="1.8" />
|
||||
<Sparkles class="studio-logo-ai" :size="Math.round(size * 0.46)" :stroke-width="2.4" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.studio-logo {
|
||||
position: relative;
|
||||
display: inline-grid;
|
||||
flex: 0 0 auto;
|
||||
place-items: center;
|
||||
color: var(--app-ink);
|
||||
}
|
||||
.studio-logo-film {
|
||||
display: block;
|
||||
}
|
||||
.studio-logo-ai {
|
||||
position: absolute;
|
||||
top: -12%;
|
||||
right: -15%;
|
||||
color: var(--app-accent-text);
|
||||
fill: var(--app-subtle);
|
||||
}
|
||||
</style>
|
||||
@@ -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'
|
||||
|
||||
@@ -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('确认按钮有稳定根元素承接调用方间距,生产按钮显式保留上下留白', () => {
|
||||
|
||||
Reference in New Issue
Block a user