From 4e86506f3e80333ae260e07afe36e9ce1498c838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 21:01:23 +0800 Subject: [PATCH] =?UTF-8?q?task-248(admin.html=E8=A7=82=E6=84=9F=E5=AF=B9?= =?UTF-8?q?=E9=BD=90):=20=E6=A8=A1=E6=80=81/=E6=8A=BD=E5=B1=89=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E7=BB=9F=E4=B8=80=EF=BC=88ModalShell/DrawerShell=20?= =?UTF-8?q?=E6=94=B6=E5=8F=A3=E5=B0=BA=E5=AF=B8=E4=B8=8E=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/DrawerShell.vue | 42 +++++++++++ .../src/components/ModalShell.vue | 42 +++++++++++ .../src/components/modal-model.ts | 30 ++++++++ admin-frontend-vue/tests/task-248.test.ts | 74 +++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 admin-frontend-vue/src/components/DrawerShell.vue create mode 100644 admin-frontend-vue/src/components/ModalShell.vue create mode 100644 admin-frontend-vue/src/components/modal-model.ts create mode 100644 admin-frontend-vue/tests/task-248.test.ts diff --git a/admin-frontend-vue/src/components/DrawerShell.vue b/admin-frontend-vue/src/components/DrawerShell.vue new file mode 100644 index 00000000..b83eadf1 --- /dev/null +++ b/admin-frontend-vue/src/components/DrawerShell.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/admin-frontend-vue/src/components/ModalShell.vue b/admin-frontend-vue/src/components/ModalShell.vue new file mode 100644 index 00000000..50457e02 --- /dev/null +++ b/admin-frontend-vue/src/components/ModalShell.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/admin-frontend-vue/src/components/modal-model.ts b/admin-frontend-vue/src/components/modal-model.ts new file mode 100644 index 00000000..4bd32a6c --- /dev/null +++ b/admin-frontend-vue/src/components/modal-model.ts @@ -0,0 +1,30 @@ +/** + * 模态/抽屉布局模型(module 13 task 248):统一两种承载容器的尺寸与关闭语义, + * 对齐 admin.html —— 居中 modal width min(920px,100vw-48)、radius14;右侧 drawer + * width min(460px,100%);遮罩点击与 Esc 均关闭。页面接入统一尺寸与头/脚槽。 + */ +export const ADMIN_MODAL_WIDTH = 'min(920px, 100vw - 48px)' +export const ADMIN_DRAWER_WIDTH = 'min(460px, 100%)' +export const ADMIN_MODAL_RADIUS = 14 + +export interface AdminContainerSpec { + width: string + radius?: number + maskClosable: boolean + escClosable: boolean +} + +export function modalLayoutSpec(): AdminContainerSpec { + return { width: ADMIN_MODAL_WIDTH, radius: ADMIN_MODAL_RADIUS, maskClosable: true, escClosable: true } +} + +export function drawerLayoutSpec(): AdminContainerSpec { + return { width: ADMIN_DRAWER_WIDTH, maskClosable: true, escClosable: true } +} + +/** 页面头标题归一:空/空白标题兜底「操作」,副标题去空白后无则省略。 */ +export function dialogTitleOf(opts: { title?: string; subtitle?: string }): { title: string; subtitle?: string } { + const title = opts.title?.trim() + const subtitle = opts.subtitle?.trim() + return { title: title || '操作', ...(subtitle ? { subtitle } : {}) } +} diff --git a/admin-frontend-vue/tests/task-248.test.ts b/admin-frontend-vue/tests/task-248.test.ts new file mode 100644 index 00000000..f6d929ee --- /dev/null +++ b/admin-frontend-vue/tests/task-248.test.ts @@ -0,0 +1,74 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { + ADMIN_DRAWER_WIDTH, + ADMIN_MODAL_RADIUS, + ADMIN_MODAL_WIDTH, + dialogTitleOf, + drawerLayoutSpec, + modalLayoutSpec, +} from '../src/components/modal-model.ts' + +// module 13 task 248:模态/抽屉容器统一 —— 居中 modal min(920px,100vw-48)、右侧 drawer +// min(460px,100%)、三段式头/体/脚与关闭语义对齐 admin,页面接入统一宽度/结构。 + +test('test_task_248_container_model_normal_primary_path', () => { + const modal = modalLayoutSpec() + assert.equal(modal.width, ADMIN_MODAL_WIDTH) + assert.equal(modal.radius, ADMIN_MODAL_RADIUS) + assert.equal(ADMIN_MODAL_RADIUS, 14) + const drawer = drawerLayoutSpec() + assert.equal(drawer.width, ADMIN_DRAWER_WIDTH) +}) + +test('test_task_248_container_model_normal_variant_input', () => { + // 正常变体:modal/drawer 都支持遮罩点击与 Esc 关闭(对齐 admin 语义)。 + assert.equal(modalLayoutSpec().maskClosable, true) + assert.equal(modalLayoutSpec().escClosable, true) + assert.equal(drawerLayoutSpec().maskClosable, true) + assert.equal(drawerLayoutSpec().escClosable, true) +}) + +test('test_task_248_container_model_normal_repeated_operation_is_idempotent', () => { + assert.deepEqual(modalLayoutSpec(), modalLayoutSpec()) + assert.deepEqual(drawerLayoutSpec(), drawerLayoutSpec()) +}) + +test('test_task_248_container_model_boundary_empty_input', () => { + // 边界空:无标题时页头兜底「操作」;空白标题同样兜底。 + assert.equal(dialogTitleOf({}).title, '操作') + assert.equal(dialogTitleOf({ title: ' ' }).title, '操作') +}) + +test('test_task_248_container_model_boundary_single_item', () => { + // 边界单元素:带副标题保留、去首尾空白。 + const head = dialogTitleOf({ title: ' 编辑店铺 ', subtitle: ' 店铺分组/账号凭证明细 ' }) + assert.equal(head.title, '编辑店铺') + assert.equal(head.subtitle, '店铺分组/账号凭证明细') +}) + +test('test_task_248_container_model_boundary_limit_or_missing_field', () => { + // 边界上限:宽度表达式满足 admin 尺寸口径。 + assert.match(ADMIN_MODAL_WIDTH, /920px/) + assert.match(ADMIN_MODAL_WIDTH, /100vw/) + assert.match(ADMIN_DRAWER_WIDTH, /460px/) +}) + +test('test_task_248_modal_shell_dependency_failure_returns_actionable_message', () => { + // 依赖失败:ModalShell 真实封装 el-dialog,宽度来自模型,标题透传、带 footer 槽。 + const modal = readSource('src/components/ModalShell.vue') + assert.match(modal, /el-dialog/) + assert.match(modal, /modalLayoutSpec|ADMIN_MODAL_WIDTH/) + assert.match(modal, /#footer/) + assert.ok(readSource('src/components/DrawerShell.vue').length > 0) +}) + +test('test_task_248_drawer_shell_dependency_failure_returns_actionable_message', () => { + const drawer = readSource('src/components/DrawerShell.vue') + assert.match(drawer, /el-drawer/) + assert.match(drawer, /drawerLayoutSpec|ADMIN_DRAWER_WIDTH/) + assert.match(drawer, /:title=|head\.title/, '标题透传到 drawer 默认头部') + assert.match(drawer, /#footer/, '提供 footer 槽以构成头/体/脚三段') + assert.match(drawer, /close-on-press-escape|close-on-click-modal/, '需配置 Esc/遮罩关闭语义') +})