From 83b2c8a45db8fd80af8c52dabc44485ad9e8a53e Mon Sep 17 00:00:00 2001 From: tbphp Date: Wed, 29 Jul 2026 01:51:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E5=AE=8C=E5=96=84=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E5=8A=A8=E4=BD=9C=E4=B8=8E=E5=8F=8D=E9=A6=88=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/ui/AppButton.test.ts | 17 ++++ web/src/components/ui/AppButton.vue | 6 +- web/src/components/ui/EmptyState.vue | 16 +++- web/src/components/ui/FormField.test.ts | 38 ++++++++ web/src/components/ui/FormField.vue | 34 +++++++- web/src/components/ui/IconButton.vue | 17 +++- web/src/components/ui/OperationNotice.test.ts | 36 ++++++++ web/src/components/ui/OperationNotice.vue | 86 +++++++++++++++++++ web/src/components/ui/QueryFeedback.vue | 9 +- web/src/components/ui/SkeletonBlock.vue | 53 ++++++++++++ web/src/components/ui/Surface.test.ts | 23 +++++ web/src/components/ui/Surface.vue | 46 ++++++++++ web/src/components/ui/SurfaceCard.vue | 8 +- .../ui/accessibility-styles.test.ts | 2 +- .../components/ui/feedback-contracts.test.ts | 34 ++++++++ web/src/styles/base.css | 19 +++- 16 files changed, 428 insertions(+), 16 deletions(-) create mode 100644 web/src/components/ui/OperationNotice.test.ts create mode 100644 web/src/components/ui/OperationNotice.vue create mode 100644 web/src/components/ui/SkeletonBlock.vue create mode 100644 web/src/components/ui/Surface.test.ts create mode 100644 web/src/components/ui/Surface.vue create mode 100644 web/src/components/ui/feedback-contracts.test.ts diff --git a/web/src/components/ui/AppButton.test.ts b/web/src/components/ui/AppButton.test.ts index fc83279a..4e9c8468 100644 --- a/web/src/components/ui/AppButton.test.ts +++ b/web/src/components/ui/AppButton.test.ts @@ -39,4 +39,21 @@ describe('AppButton', () => { await button.trigger('click') expect(onClick).not.toHaveBeenCalled() }) + + it('exposes danger and size as semantic classes', () => { + const wrapper = mount(AppButton, { + props: { + variant: 'danger', + size: 'lg', + }, + slots: { + default: 'Delete permanently', + }, + }) + + expect(wrapper.get('button').classes()).toEqual( + expect.arrayContaining(['app-button--danger', 'app-button--lg']), + ) + expect(wrapper.text()).toBe('Delete permanently') + }) }) diff --git a/web/src/components/ui/AppButton.vue b/web/src/components/ui/AppButton.vue index c9ad0369..f4b16216 100644 --- a/web/src/components/ui/AppButton.vue +++ b/web/src/components/ui/AppButton.vue @@ -4,13 +4,15 @@ withDefaults( type?: 'button' | 'submit' | 'reset' disabled?: boolean busy?: boolean - variant?: 'primary' | 'secondary' | 'ghost' + variant?: 'primary' | 'secondary' | 'ghost' | 'danger' + size?: 'sm' | 'md' | 'lg' }>(), { type: 'button', disabled: false, busy: false, variant: 'primary', + size: 'md', }, ) @@ -18,7 +20,7 @@ withDefaults(