From abbaeabfe9b40d4b03342268d2a0aebc48751ed1 Mon Sep 17 00:00:00 2001 From: linshen <32978552+linshenkx@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:40:09 +0800 Subject: [PATCH] fix(ui): align about popover with naive theme --- .../app-layout/AppHeaderActions.vue | 131 +++++------------- .../unit/components/AppHeaderActions.spec.ts | 17 ++- 2 files changed, 45 insertions(+), 103 deletions(-) diff --git a/packages/ui/src/components/app-layout/AppHeaderActions.vue b/packages/ui/src/components/app-layout/AppHeaderActions.vue index d4e589af..292dc365 100644 --- a/packages/ui/src/components/app-layout/AppHeaderActions.vue +++ b/packages/ui/src/components/app-layout/AppHeaderActions.vue @@ -93,9 +93,7 @@ v-model:show="showAboutPopover" trigger="click" placement="bottom-end" - raw :show-arrow="false" - content-class="about-popover-content" > -
- {{ appVersion }} +
+
+ {{ appVersion }} +
- + - +
@@ -209,7 +209,7 @@ import ActionButtonUI from '../ActionButton.vue' import ThemeToggleUI from '../ThemeToggleUI.vue' import LanguageSwitchDropdown from '../LanguageSwitchDropdown.vue' import UpdaterIcon from '../UpdaterIcon.vue' -import { NButton, NPopover } from 'naive-ui' +import { NButton, NPopover, NTag } from 'naive-ui' interface Props { appVersion: string @@ -277,93 +277,44 @@ const handleOpenDocs = () => { margin-left: 6px; } -:deep(.about-popover-content) { - padding: 0 !important; - background: transparent !important; - box-shadow: none !important; - border: 0 !important; -} - -.about-flyout { +.about-panel { display: flex; flex-direction: column; - align-items: flex-end; - gap: 7px; - padding-top: 8px; + gap: 6px; + min-width: 220px; + max-width: 260px; } -.about-chip { - display: inline-flex; - align-items: center; - gap: 10px; +.about-panel-header { + display: flex; + justify-content: flex-end; +} + +.about-version-tag { max-width: 100%; - box-sizing: border-box; - border-radius: 999px; - border: 1px solid color-mix(in srgb, var(--border-color, rgba(239, 239, 245, 0.82)) 86%, transparent); - background: color-mix(in srgb, var(--card-color, #fff) 94%, transparent); - color: inherit; - backdrop-filter: blur(14px); - box-shadow: - 0 10px 24px rgba(15, 23, 42, 0.08), - 0 2px 8px rgba(15, 23, 42, 0.05); } -.about-chip-version { - align-self: flex-end; - padding: 4px 9px; - font-size: 11px; - line-height: 1; - color: var(--text-color-3); - border-color: color-mix(in srgb, var(--border-color, rgba(239, 239, 245, 0.8)) 68%, transparent); - background: color-mix(in srgb, var(--hover-color, rgba(0, 0, 0, 0.03)) 70%, transparent); - box-shadow: - 0 8px 18px rgba(15, 23, 42, 0.06), - 0 1px 4px rgba(15, 23, 42, 0.03); +.about-link-button { + width: 100%; + justify-content: space-between; + gap: 12px; } -.about-chip-link { - max-width: 100%; - min-width: 0; - padding: 8px 12px 8px 13px; - text-align: left; - cursor: pointer; - transition: - background-color 0.2s ease, - border-color 0.2s ease, - color 0.2s ease, - transform 0.2s ease, - box-shadow 0.2s ease; -} - -.about-chip-link:hover { - background: color-mix(in srgb, var(--hover-color, rgba(0, 0, 0, 0.04)) 78%, transparent); - border-color: color-mix(in srgb, var(--primary-color, #18a058) 22%, var(--border-color, rgba(239, 239, 245, 0.8))); - transform: translateY(-1px); - box-shadow: - 0 12px 28px rgba(15, 23, 42, 0.1), - 0 3px 10px rgba(15, 23, 42, 0.06); -} - -.about-chip-link:focus-visible { - outline: none; - border-color: color-mix(in srgb, var(--primary-color, #18a058) 28%, var(--border-color, rgba(239, 239, 245, 0.8))); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary-color, #18a058) 14%, transparent); -} - -.about-chip-text { +.about-link-copy { display: inline-flex; align-items: baseline; - gap: 7px; + gap: 8px; min-width: 0; + text-align: left; } -.about-chip-label { +.about-link-label { flex-shrink: 0; font-size: 11px; color: var(--text-color-3); } -.about-chip-value { +.about-link-value { min-width: 0; font-size: 12px; font-weight: 600; @@ -373,18 +324,10 @@ const handleOpenDocs = () => { white-space: nowrap; } -.about-chip-icon { +.about-link-icon { width: 12px; height: 12px; flex-shrink: 0; color: var(--text-color-3); - transition: - color 0.2s ease, - transform 0.2s ease; -} - -.about-chip-link:hover .about-chip-icon { - color: var(--text-color-2); - transform: translate(1px, -1px); } diff --git a/packages/ui/tests/unit/components/AppHeaderActions.spec.ts b/packages/ui/tests/unit/components/AppHeaderActions.spec.ts index 679d1bed..72c7fe86 100644 --- a/packages/ui/tests/unit/components/AppHeaderActions.spec.ts +++ b/packages/ui/tests/unit/components/AppHeaderActions.spec.ts @@ -25,8 +25,8 @@ vi.mock('naive-ui', async (importOriginal) => { }), NTag: defineComponent({ name: 'NTag', - setup(_, { slots }) { - return () => h('span', { class: 'n-tag-stub' }, slots.default?.()) + setup(_, { slots, attrs }) { + return () => h('span', { class: 'n-tag-stub', ...attrs }, slots.default?.()) }, }), NText: defineComponent({ @@ -41,7 +41,7 @@ vi.mock('naive-ui', async (importOriginal) => { import AppHeaderActions from '../../../src/components/app-layout/AppHeaderActions.vue' describe('AppHeaderActions about menu layout hooks', () => { - it('renders compact chip-style about actions without a product title row', () => { + it('renders the about popover with naive-themed panel content', () => { const wrapper = mount(AppHeaderActions, { props: { appVersion: 'v2.7.0', @@ -77,12 +77,11 @@ describe('AppHeaderActions about menu layout hooks', () => { }, }) - expect(wrapper.find('.about-menu-title').exists()).toBe(false) - - const chips = wrapper.findAll('.about-chip') - expect(chips).toHaveLength(3) - expect(wrapper.find('.about-chip-version').text()).toContain('v2.7.0') - expect(wrapper.findAll('.about-chip-link')).toHaveLength(2) + expect(wrapper.find('.about-flyout').exists()).toBe(false) + expect(wrapper.findAll('.about-chip')).toHaveLength(0) + expect(wrapper.find('.about-panel').exists()).toBe(true) + expect(wrapper.find('.about-version-tag').text()).toContain('v2.7.0') + expect(wrapper.findAll('.about-link-button')).toHaveLength(2) expect(wrapper.text()).toContain('always200.com') expect(wrapper.text()).toContain('docs.always200.com') })