@morya-ui/mcp 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -149
- package/bin/morya-ui-mcp.js +2 -2
- package/data/catalog.json +70 -53
- package/data/example-coverage.json +8 -6
- package/data/golden-pages/dashboard-page.vue +138 -138
- package/data/golden-pages/detail-page.vue +178 -178
- package/data/golden-pages/empty-state.vue +56 -56
- package/data/golden-pages/form-in-dialog.vue +166 -166
- package/data/golden-pages/form-page.vue +107 -107
- package/data/golden-pages/landing-page.vue +358 -358
- package/data/golden-pages/list-page-dense.vue +166 -0
- package/data/golden-pages/list-page-rail.vue +270 -0
- package/data/golden-pages/list-page.vue +220 -216
- package/data/golden-pages/login-page.vue +231 -231
- package/data/golden-pages/result-page.vue +84 -84
- package/data/golden-pages/settings-page.vue +163 -163
- package/data/golden-pages/wizard-form.vue +182 -182
- package/dist/__tests__/tools.test.js +93 -1
- package/dist/decisions.d.ts +14 -0
- package/dist/decisions.js +818 -41
- package/dist/golden-pages.d.ts +13 -0
- package/dist/golden-pages.js +55 -2
- package/dist/index.js +9 -3
- package/dist/page-snippets.js +254 -225
- package/dist/patterns.d.ts +2 -2
- package/dist/patterns.js +20 -9
- package/dist/style-presets.d.ts +52 -0
- package/dist/style-presets.js +322 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.js +336 -216
- package/package.json +16 -12
- package/LICENSE +0 -21
package/dist/page-snippets.js
CHANGED
|
@@ -8,26 +8,26 @@ export const pageSnippets = [
|
|
|
8
8
|
pageTypes: ['list'],
|
|
9
9
|
keywords: ['筛选', '搜索', '查询', 'filters', 'filter', 'search', 'keyword', 'reset'],
|
|
10
10
|
imports: ['MPageFilters', 'MSpace', 'MInput', 'MSelect', 'MButton'],
|
|
11
|
-
scriptSetup: `const keyword = ref('')
|
|
12
|
-
const status = ref<string | undefined>()
|
|
13
|
-
const statusOptions = [
|
|
14
|
-
{ label: '全部', value: '' },
|
|
15
|
-
{ label: '启用', value: 'active' },
|
|
16
|
-
{ label: '停用', value: 'inactive' },
|
|
11
|
+
scriptSetup: `const keyword = ref('')
|
|
12
|
+
const status = ref<string | undefined>()
|
|
13
|
+
const statusOptions = [
|
|
14
|
+
{ label: '全部', value: '' },
|
|
15
|
+
{ label: '启用', value: 'active' },
|
|
16
|
+
{ label: '停用', value: 'inactive' },
|
|
17
17
|
]`,
|
|
18
|
-
template: `<MPageFilters aria-label="筛选" variant="filled">
|
|
19
|
-
<MSpace wrap>
|
|
20
|
-
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
21
|
-
<MSelect
|
|
22
|
-
v-model="status"
|
|
23
|
-
:options="statusOptions"
|
|
24
|
-
placeholder="状态"
|
|
25
|
-
clearable
|
|
26
|
-
style="width: 10rem"
|
|
27
|
-
/>
|
|
28
|
-
<MButton severity="secondary">查询</MButton>
|
|
29
|
-
<MButton severity="secondary" text>重置</MButton>
|
|
30
|
-
</MSpace>
|
|
18
|
+
template: `<MPageFilters aria-label="筛选" variant="filled">
|
|
19
|
+
<MSpace wrap>
|
|
20
|
+
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
21
|
+
<MSelect
|
|
22
|
+
v-model="status"
|
|
23
|
+
:options="statusOptions"
|
|
24
|
+
placeholder="状态"
|
|
25
|
+
clearable
|
|
26
|
+
style="width: 10rem"
|
|
27
|
+
/>
|
|
28
|
+
<MButton severity="secondary">查询</MButton>
|
|
29
|
+
<MButton severity="secondary" text>重置</MButton>
|
|
30
|
+
</MSpace>
|
|
31
31
|
</MPageFilters>`,
|
|
32
32
|
rules: ['放在 MPageHeader 之后、表格之前', 'viewport 内仅一个 primary(通常在 Header)', '不要外包 MCard'],
|
|
33
33
|
rulesEn: ['Place after MPageHeader and before the table', 'Keep one primary in the viewport (usually in Header)', 'Do not wrap with MCard'],
|
|
@@ -43,31 +43,31 @@ const statusOptions = [
|
|
|
43
43
|
pageTypes: ['list'],
|
|
44
44
|
keywords: ['折叠', '高级筛选', 'collapse', 'advanced', 'expand', 'filter'],
|
|
45
45
|
imports: ['MPageFilters', 'MSpace', 'MInput', 'MSelect', 'MButton'],
|
|
46
|
-
scriptSetup: `const filtersExpanded = ref(false)
|
|
47
|
-
const keyword = ref('')
|
|
46
|
+
scriptSetup: `const filtersExpanded = ref(false)
|
|
47
|
+
const keyword = ref('')
|
|
48
48
|
const department = ref<string | null>(null)`,
|
|
49
|
-
template: `<MPageFilters
|
|
50
|
-
v-model:expanded="filtersExpanded"
|
|
51
|
-
aria-label="筛选"
|
|
52
|
-
variant="filled"
|
|
53
|
-
collapsible
|
|
54
|
-
>
|
|
55
|
-
<MSpace wrap>
|
|
56
|
-
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
57
|
-
<MButton severity="secondary">查询</MButton>
|
|
58
|
-
<MButton severity="secondary" text>重置</MButton>
|
|
59
|
-
</MSpace>
|
|
60
|
-
<template #advanced>
|
|
61
|
-
<MSpace wrap>
|
|
62
|
-
<MSelect
|
|
63
|
-
v-model="department"
|
|
64
|
-
:options="[{ label: '研发', value: '研发' }, { label: '运营', value: '运营' }]"
|
|
65
|
-
placeholder="部门"
|
|
66
|
-
clearable
|
|
67
|
-
style="width: 10rem"
|
|
68
|
-
/>
|
|
69
|
-
</MSpace>
|
|
70
|
-
</template>
|
|
49
|
+
template: `<MPageFilters
|
|
50
|
+
v-model:expanded="filtersExpanded"
|
|
51
|
+
aria-label="筛选"
|
|
52
|
+
variant="filled"
|
|
53
|
+
collapsible
|
|
54
|
+
>
|
|
55
|
+
<MSpace wrap>
|
|
56
|
+
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
57
|
+
<MButton severity="secondary">查询</MButton>
|
|
58
|
+
<MButton severity="secondary" text>重置</MButton>
|
|
59
|
+
</MSpace>
|
|
60
|
+
<template #advanced>
|
|
61
|
+
<MSpace wrap>
|
|
62
|
+
<MSelect
|
|
63
|
+
v-model="department"
|
|
64
|
+
:options="[{ label: '研发', value: '研发' }, { label: '运营', value: '运营' }]"
|
|
65
|
+
placeholder="部门"
|
|
66
|
+
clearable
|
|
67
|
+
style="width: 10rem"
|
|
68
|
+
/>
|
|
69
|
+
</MSpace>
|
|
70
|
+
</template>
|
|
71
71
|
</MPageFilters>`,
|
|
72
72
|
rules: ['常用条件放 default,次要条件放 #advanced', '查询/重置保持 secondary'],
|
|
73
73
|
rulesEn: ['Keep primary filters in default; secondary filters in #advanced', 'Query/reset stay secondary'],
|
|
@@ -83,16 +83,16 @@ const department = ref<string | null>(null)`,
|
|
|
83
83
|
pageTypes: ['list'],
|
|
84
84
|
keywords: ['已选', 'chip', 'tag', 'active', 'clear', '筛选'],
|
|
85
85
|
imports: ['MPageFilterChips', 'MTag'],
|
|
86
|
-
template: `<MPageFilterChips label="已选" aria-label="已选筛选">
|
|
87
|
-
<MTag
|
|
88
|
-
v-for="item in activeFilters"
|
|
89
|
-
:key="item.key"
|
|
90
|
-
:value="item.label"
|
|
91
|
-
size="small"
|
|
92
|
-
bordered
|
|
93
|
-
closable
|
|
94
|
-
@close="clearFilter(item.key)"
|
|
95
|
-
/>
|
|
86
|
+
template: `<MPageFilterChips label="已选" aria-label="已选筛选">
|
|
87
|
+
<MTag
|
|
88
|
+
v-for="item in activeFilters"
|
|
89
|
+
:key="item.key"
|
|
90
|
+
:value="item.label"
|
|
91
|
+
size="small"
|
|
92
|
+
bordered
|
|
93
|
+
closable
|
|
94
|
+
@close="clearFilter(item.key)"
|
|
95
|
+
/>
|
|
96
96
|
</MPageFilterChips>`,
|
|
97
97
|
rules: ['放在 MPageFilters 之后、表格之前', '用业务 computed 生成 activeFilters'],
|
|
98
98
|
rulesEn: ['Place after MPageFilters and before the table', 'Derive activeFilters from filter state'],
|
|
@@ -108,10 +108,10 @@ const department = ref<string | null>(null)`,
|
|
|
108
108
|
pageTypes: ['list'],
|
|
109
109
|
keywords: ['页头', '标题', '新建', 'header', 'title', 'create', 'actions'],
|
|
110
110
|
imports: ['MPageHeader', 'MButton'],
|
|
111
|
-
template: `<MPageHeader title="用户管理" description="维护账号、角色与权限。">
|
|
112
|
-
<template #actions>
|
|
113
|
-
<MButton severity="primary">新建用户</MButton>
|
|
114
|
-
</template>
|
|
111
|
+
template: `<MPageHeader title="用户管理" description="维护账号、角色与权限。">
|
|
112
|
+
<template #actions>
|
|
113
|
+
<MButton severity="primary">新建用户</MButton>
|
|
114
|
+
</template>
|
|
115
115
|
</MPageHeader>`,
|
|
116
116
|
rules: ['页级标题用 MPageHeader,不要用 MPageToolbar 当第二页头', '主操作放 #actions'],
|
|
117
117
|
rulesEn: ['Use MPageHeader for page identity; do not use MPageToolbar as a second page title', 'Put primary actions in #actions'],
|
|
@@ -127,12 +127,12 @@ const department = ref<string | null>(null)`,
|
|
|
127
127
|
pageTypes: ['list'],
|
|
128
128
|
keywords: ['批量', '已选', 'toolbar', 'batch', 'export', 'delete'],
|
|
129
129
|
imports: ['MPageToolbar', 'MButton'],
|
|
130
|
-
template: `<MPageToolbar>
|
|
131
|
-
<span style="color:var(--m-color-text-muted);font-size:var(--m-font-size-sm)">已选 0 项</span>
|
|
132
|
-
<template #actions>
|
|
133
|
-
<MButton severity="secondary" text>导出</MButton>
|
|
134
|
-
<MButton severity="danger" text>删除</MButton>
|
|
135
|
-
</template>
|
|
130
|
+
template: `<MPageToolbar>
|
|
131
|
+
<span style="color:var(--m-color-text-muted);font-size:var(--m-font-size-sm)">已选 0 项</span>
|
|
132
|
+
<template #actions>
|
|
133
|
+
<MButton severity="secondary" text>导出</MButton>
|
|
134
|
+
<MButton severity="danger" text>删除</MButton>
|
|
135
|
+
</template>
|
|
136
136
|
</MPageToolbar>`,
|
|
137
137
|
rules: ['左侧用 default 插槽展示已选数量', '批量操作用 text/outlined,danger 仅 destructive'],
|
|
138
138
|
rulesEn: ['Use the default slot for selection count', 'Batch actions use text/outlined; danger only for destructive ops'],
|
|
@@ -143,46 +143,75 @@ const department = ref<string | null>(null)`,
|
|
|
143
143
|
id: 'list-table',
|
|
144
144
|
title: '列表页表格',
|
|
145
145
|
titleEn: 'List page table',
|
|
146
|
-
description: '标准 MTable,直接放在 MPageContent
|
|
147
|
-
descriptionEn: 'Standard MTable
|
|
146
|
+
description: '标准 MTable,直接放在 MPageContent 内。全视口主列表可再加 fill(表体滚、分页贴底);嵌入/短页不要 fill。',
|
|
147
|
+
descriptionEn: 'Standard MTable in MPageContent. Add fill only for full-viewport main lists; skip fill for embedded/short pages.',
|
|
148
148
|
pageTypes: ['list'],
|
|
149
|
-
keywords: [
|
|
149
|
+
keywords: [
|
|
150
|
+
'表格',
|
|
151
|
+
'table',
|
|
152
|
+
'pagination',
|
|
153
|
+
'paginator',
|
|
154
|
+
'empty',
|
|
155
|
+
'columns',
|
|
156
|
+
'rows',
|
|
157
|
+
'fill',
|
|
158
|
+
'撑满',
|
|
159
|
+
'分页',
|
|
160
|
+
],
|
|
150
161
|
imports: ['MTable', 'MEmpty', 'MButton'],
|
|
151
|
-
scriptSetup: `const columns = [
|
|
152
|
-
{ key: 'name', label: '名称' },
|
|
153
|
-
{ key: 'status', label: '状态' },
|
|
154
|
-
{ key: 'updatedAt', label: '更新时间' },
|
|
155
|
-
{ key: 'actions', label: '操作', width: 128 },
|
|
156
|
-
]
|
|
157
|
-
const rows = ref<Record<string, unknown>[]>([])
|
|
162
|
+
scriptSetup: `const columns = [
|
|
163
|
+
{ key: 'name', label: '名称' },
|
|
164
|
+
{ key: 'status', label: '状态' },
|
|
165
|
+
{ key: 'updatedAt', label: '更新时间' },
|
|
166
|
+
{ key: 'actions', label: '操作', width: 128 },
|
|
167
|
+
]
|
|
168
|
+
const rows = ref<Record<string, unknown>[]>([])
|
|
158
169
|
const loading = ref(false)`,
|
|
159
|
-
template:
|
|
160
|
-
|
|
161
|
-
:
|
|
162
|
-
:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
170
|
+
template: `<!-- 全视口主列表再加 fill;嵌入/短页去掉 fill -->
|
|
171
|
+
<MTable
|
|
172
|
+
:columns="columns"
|
|
173
|
+
:rows="rows"
|
|
174
|
+
:loading="loading"
|
|
175
|
+
fill
|
|
176
|
+
paginator
|
|
177
|
+
:rows-per-page="10"
|
|
178
|
+
striped
|
|
179
|
+
bordered
|
|
180
|
+
row-key="id"
|
|
181
|
+
aria-label="数据列表"
|
|
182
|
+
>
|
|
183
|
+
<template #empty>
|
|
184
|
+
<MEmpty
|
|
185
|
+
title="还没有数据"
|
|
186
|
+
description="创建第一条记录后,这里会列出结果。"
|
|
187
|
+
icon="inbox"
|
|
188
|
+
>
|
|
189
|
+
<template #extra>
|
|
190
|
+
<MButton severity="primary">新建</MButton>
|
|
191
|
+
</template>
|
|
192
|
+
</MEmpty>
|
|
193
|
+
</template>
|
|
181
194
|
</MTable>`,
|
|
182
|
-
rules: [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
rules: [
|
|
196
|
+
'先判断是否适合 fill:全视口后台主列表 + 表格是主任务 → MPageContent fill + MTable fill',
|
|
197
|
+
'嵌入表、短页、整页文档滚动 → 不要 fill,内容高度即可',
|
|
198
|
+
'表格直接放在 MPageContent 内;空态用 MEmpty',
|
|
199
|
+
],
|
|
200
|
+
rulesEn: [
|
|
201
|
+
'Decide fill first: full-viewport admin main list → MPageContent fill + MTable fill',
|
|
202
|
+
'Embedded / short / document-scroll pages → skip fill',
|
|
203
|
+
'Place the table directly in MPageContent; use MEmpty for zero-data states',
|
|
204
|
+
],
|
|
205
|
+
avoid: [
|
|
206
|
+
'不要用 MCard 包裹 bordered MTable',
|
|
207
|
+
'不要对嵌入/短页硬套 fill',
|
|
208
|
+
'适合 fill 时不要手写 min-height / calc',
|
|
209
|
+
],
|
|
210
|
+
avoidEn: [
|
|
211
|
+
'Do not wrap a bordered MTable with MCard',
|
|
212
|
+
'Do not force fill on embedded or short pages',
|
|
213
|
+
'Do not hand-write min-height / calc when fill is appropriate',
|
|
214
|
+
],
|
|
186
215
|
},
|
|
187
216
|
{
|
|
188
217
|
id: 'list-row-actions',
|
|
@@ -193,11 +222,11 @@ const loading = ref(false)`,
|
|
|
193
222
|
pageTypes: ['list'],
|
|
194
223
|
keywords: ['行操作', '编辑', '删除', 'actions', 'row', 'cell'],
|
|
195
224
|
imports: ['MSpace', 'MButton'],
|
|
196
|
-
template: `<template #cell-actions>
|
|
197
|
-
<MSpace>
|
|
198
|
-
<MButton severity="secondary" size="small" text>编辑</MButton>
|
|
199
|
-
<MButton severity="danger" size="small" text>删除</MButton>
|
|
200
|
-
</MSpace>
|
|
225
|
+
template: `<template #cell-actions>
|
|
226
|
+
<MSpace>
|
|
227
|
+
<MButton severity="secondary" size="small" text>编辑</MButton>
|
|
228
|
+
<MButton severity="danger" size="small" text>删除</MButton>
|
|
229
|
+
</MSpace>
|
|
201
230
|
</template>`,
|
|
202
231
|
rules: ['行内操作用 size="small" + text/outlined,避免一排实心按钮', '危险操作使用 severity="danger" 并配合确认弹窗'],
|
|
203
232
|
rulesEn: ['Use size="small" with text/outlined row actions', 'Use severity="danger" with confirmation for destructive actions'],
|
|
@@ -213,11 +242,11 @@ const loading = ref(false)`,
|
|
|
213
242
|
pageTypes: ['list'],
|
|
214
243
|
keywords: ['分类', '标签', '可关闭', 'chip', 'category', 'tag-only'],
|
|
215
244
|
imports: ['MTag'],
|
|
216
|
-
template: `<template #cell-category="{ value }">
|
|
217
|
-
<MTag
|
|
218
|
-
:value="String(value ?? '')"
|
|
219
|
-
severity="info"
|
|
220
|
-
/>
|
|
245
|
+
template: `<template #cell-category="{ value }">
|
|
246
|
+
<MTag
|
|
247
|
+
:value="String(value ?? '')"
|
|
248
|
+
severity="info"
|
|
249
|
+
/>
|
|
221
250
|
</template>`,
|
|
222
251
|
rules: ['分类/强调态用 MTag;行内业务状态用 MStatus(list-status-dot)', '不要用 Button 颜色表达状态'],
|
|
223
252
|
rulesEn: ['Use MTag for categories; use MStatus for business status (list-status-dot)', 'Do not use Button colors for status'],
|
|
@@ -233,11 +262,11 @@ const loading = ref(false)`,
|
|
|
233
262
|
pageTypes: ['list'],
|
|
234
263
|
keywords: ['状态', 'status', 'dot', 'cell-status', '在线', '启用', '停用', '业务状态'],
|
|
235
264
|
imports: ['MStatus'],
|
|
236
|
-
template: `<template #cell-status="{ value }">
|
|
237
|
-
<MStatus
|
|
238
|
-
:label="value === 'active' || value === 'online' ? '启用' : '停用'"
|
|
239
|
-
:severity="value === 'active' || value === 'online' ? 'success' : 'secondary'"
|
|
240
|
-
/>
|
|
265
|
+
template: `<template #cell-status="{ value }">
|
|
266
|
+
<MStatus
|
|
267
|
+
:label="value === 'active' || value === 'online' ? '启用' : '停用'"
|
|
268
|
+
:severity="value === 'active' || value === 'online' ? 'success' : 'secondary'"
|
|
269
|
+
/>
|
|
241
270
|
</template>`,
|
|
242
271
|
rules: ['行内轻量业务状态优先 MStatus', '不要用 Button 颜色表达状态'],
|
|
243
272
|
rulesEn: ['Prefer MStatus for lightweight inline business status', 'Do not use Button colors for status'],
|
|
@@ -253,15 +282,15 @@ const loading = ref(false)`,
|
|
|
253
282
|
pageTypes: ['list', 'common'],
|
|
254
283
|
keywords: ['空状态', '无数据', 'empty', 'zero state', 'no data'],
|
|
255
284
|
imports: ['MEmpty', 'MButton'],
|
|
256
|
-
template: `<MEmpty
|
|
257
|
-
title="还没有数据"
|
|
258
|
-
description="创建第一条记录后即可在此查看。"
|
|
259
|
-
icon="database"
|
|
260
|
-
>
|
|
261
|
-
<template #extra>
|
|
262
|
-
<MButton label="新建" />
|
|
263
|
-
<MButton label="导入" severity="secondary" text />
|
|
264
|
-
</template>
|
|
285
|
+
template: `<MEmpty
|
|
286
|
+
title="还没有数据"
|
|
287
|
+
description="创建第一条记录后即可在此查看。"
|
|
288
|
+
icon="database"
|
|
289
|
+
>
|
|
290
|
+
<template #extra>
|
|
291
|
+
<MButton label="新建" />
|
|
292
|
+
<MButton label="导入" severity="secondary" text />
|
|
293
|
+
</template>
|
|
265
294
|
</MEmpty>`,
|
|
266
295
|
rules: ['正常无数据用 MEmpty,不要用错误色', '主 CTA 用 primary,次动作用 text/secondary'],
|
|
267
296
|
rulesEn: ['Use MEmpty for normal emptiness, not error colors', 'Primary CTA + secondary/text for lesser actions'],
|
|
@@ -277,11 +306,11 @@ const loading = ref(false)`,
|
|
|
277
306
|
pageTypes: ['common'],
|
|
278
307
|
keywords: ['结果', '成功', '失败', '404', '403', 'result', 'success', 'error'],
|
|
279
308
|
imports: ['MResult', 'MButton'],
|
|
280
|
-
template: `<MResult status="success" description="订单已创建,可在列表中查看详情。">
|
|
281
|
-
<template #extra>
|
|
282
|
-
<MButton label="查看订单" />
|
|
283
|
-
<MButton label="返回列表" severity="secondary" text />
|
|
284
|
-
</template>
|
|
309
|
+
template: `<MResult status="success" description="订单已创建,可在列表中查看详情。">
|
|
310
|
+
<template #extra>
|
|
311
|
+
<MButton label="查看订单" />
|
|
312
|
+
<MButton label="返回列表" severity="secondary" text />
|
|
313
|
+
</template>
|
|
285
314
|
</MResult>`,
|
|
286
315
|
rules: ['流程终点用 MResult', '提供明确下一步操作'],
|
|
287
316
|
rulesEn: ['Use MResult for terminal outcomes', 'Provide clear next-step actions'],
|
|
@@ -312,15 +341,15 @@ const loading = ref(false)`,
|
|
|
312
341
|
pageTypes: ['form'],
|
|
313
342
|
keywords: ['表单', 'form', 'fields', 'formitem'],
|
|
314
343
|
imports: ['MPageSection', 'MForm', 'MFormItem', 'MInput'],
|
|
315
|
-
scriptSetup: `const model = reactive({ name: '' })
|
|
344
|
+
scriptSetup: `const model = reactive({ name: '' })
|
|
316
345
|
const submitting = ref(false)`,
|
|
317
|
-
template: `<MPageSection variant="form">
|
|
318
|
-
<MForm @submit.prevent="onSubmit">
|
|
319
|
-
<MFormItem label="姓名" name="name" required>
|
|
320
|
-
<MInput v-model="model.name" placeholder="请输入姓名" fluid />
|
|
321
|
-
</MFormItem>
|
|
322
|
-
<!-- 更多字段 -->
|
|
323
|
-
</MForm>
|
|
346
|
+
template: `<MPageSection variant="form">
|
|
347
|
+
<MForm @submit.prevent="onSubmit">
|
|
348
|
+
<MFormItem label="姓名" name="name" required>
|
|
349
|
+
<MInput v-model="model.name" placeholder="请输入姓名" fluid />
|
|
350
|
+
</MFormItem>
|
|
351
|
+
<!-- 更多字段 -->
|
|
352
|
+
</MForm>
|
|
324
353
|
</MPageSection>`,
|
|
325
354
|
rules: ['字段用 MFormItem + fluid 控件', '表单表面由 MPageSection 提供,不要重复 MCard'],
|
|
326
355
|
rulesEn: ['Use MFormItem with fluid controls', 'Let MPageSection provide the surface; do not duplicate MCard'],
|
|
@@ -336,11 +365,11 @@ const submitting = ref(false)`,
|
|
|
336
365
|
pageTypes: ['form'],
|
|
337
366
|
keywords: ['保存', '取消', 'submit', 'actions', 'footer'],
|
|
338
367
|
imports: ['MPageSection', 'MSpace', 'MButton'],
|
|
339
|
-
template: `<MPageSection variant="actions">
|
|
340
|
-
<MSpace>
|
|
341
|
-
<MButton native-type="submit" severity="primary" :loading="submitting">保存</MButton>
|
|
342
|
-
<MButton severity="secondary">取消</MButton>
|
|
343
|
-
</MSpace>
|
|
368
|
+
template: `<MPageSection variant="actions">
|
|
369
|
+
<MSpace>
|
|
370
|
+
<MButton native-type="submit" severity="primary" :loading="submitting">保存</MButton>
|
|
371
|
+
<MButton severity="secondary">取消</MButton>
|
|
372
|
+
</MSpace>
|
|
344
373
|
</MPageSection>`,
|
|
345
374
|
rules: ['提交按钮在前,取消 secondary', '保存中传 loading 防重复提交'],
|
|
346
375
|
rulesEn: ['Submit first, cancel secondary', 'Pass loading while saving to prevent duplicate submits'],
|
|
@@ -356,21 +385,21 @@ const submitting = ref(false)`,
|
|
|
356
385
|
pageTypes: ['dashboard'],
|
|
357
386
|
keywords: ['kpi', '指标', 'stat', 'grid', 'metrics'],
|
|
358
387
|
imports: ['MGrid', 'MGridItem', 'MPageStat'],
|
|
359
|
-
scriptSetup: `const metrics = ref([
|
|
360
|
-
{ label: '总用户', value: '12,480', trend: '+8.2%', trendDirection: 'up' as const, trendLabel: '较上月', icon: 'users' },
|
|
361
|
-
{ label: '今日活跃', value: '1,926', trend: '+3.1%', trendDirection: 'up' as const, trendLabel: '较昨日', icon: 'activity' },
|
|
388
|
+
scriptSetup: `const metrics = ref([
|
|
389
|
+
{ label: '总用户', value: '12,480', trend: '+8.2%', trendDirection: 'up' as const, trendLabel: '较上月', icon: 'users' },
|
|
390
|
+
{ label: '今日活跃', value: '1,926', trend: '+3.1%', trendDirection: 'up' as const, trendLabel: '较昨日', icon: 'activity' },
|
|
362
391
|
])`,
|
|
363
|
-
template: `<MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
|
|
364
|
-
<MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
|
|
365
|
-
<MPageStat
|
|
366
|
-
:label="metric.label"
|
|
367
|
-
:value="metric.value"
|
|
368
|
-
:trend="metric.trend"
|
|
369
|
-
:trend-direction="metric.trendDirection"
|
|
370
|
-
:trend-label="metric.trendLabel"
|
|
371
|
-
:icon="metric.icon"
|
|
372
|
-
/>
|
|
373
|
-
</MGridItem>
|
|
392
|
+
template: `<MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
|
|
393
|
+
<MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
|
|
394
|
+
<MPageStat
|
|
395
|
+
:label="metric.label"
|
|
396
|
+
:value="metric.value"
|
|
397
|
+
:trend="metric.trend"
|
|
398
|
+
:trend-direction="metric.trendDirection"
|
|
399
|
+
:trend-label="metric.trendLabel"
|
|
400
|
+
:icon="metric.icon"
|
|
401
|
+
/>
|
|
402
|
+
</MGridItem>
|
|
374
403
|
</MGrid>`,
|
|
375
404
|
rules: ['KPI 用 MPageStat,不要每页自定义 stat CSS', 'responsive="screen" 便于窄屏降列'],
|
|
376
405
|
rulesEn: ['Use MPageStat for KPIs', 'Use responsive="screen" for narrow layouts'],
|
|
@@ -386,14 +415,14 @@ const submitting = ref(false)`,
|
|
|
386
415
|
pageTypes: ['dashboard'],
|
|
387
416
|
keywords: ['chart', '图表', 'placeholder', 'card', 'trend'],
|
|
388
417
|
imports: ['MCard', 'MEmpty', 'MSkeleton'],
|
|
389
|
-
template: `<MCard title="趋势概览" shadow="always">
|
|
390
|
-
<MSkeleton v-if="loading" height="8rem" />
|
|
391
|
-
<MEmpty
|
|
392
|
-
v-else
|
|
393
|
-
aria-label="图表占位"
|
|
394
|
-
title="暂无图表数据"
|
|
395
|
-
description="图表区域(接入 ECharts / 业务组件)"
|
|
396
|
-
/>
|
|
418
|
+
template: `<MCard title="趋势概览" shadow="always">
|
|
419
|
+
<MSkeleton v-if="loading" height="8rem" />
|
|
420
|
+
<MEmpty
|
|
421
|
+
v-else
|
|
422
|
+
aria-label="图表占位"
|
|
423
|
+
title="暂无图表数据"
|
|
424
|
+
description="图表区域(接入 ECharts / 业务组件)"
|
|
425
|
+
/>
|
|
397
426
|
</MCard>`,
|
|
398
427
|
rules: ['图表区适合 MCard,因为需要 card 标题', '加载中用 MSkeleton'],
|
|
399
428
|
rulesEn: ['Charts belong in MCard when a card title is needed', 'Use MSkeleton while loading'],
|
|
@@ -409,18 +438,18 @@ const submitting = ref(false)`,
|
|
|
409
438
|
pageTypes: ['dashboard'],
|
|
410
439
|
keywords: ['recent', '最近', 'table', 'card', 'list'],
|
|
411
440
|
imports: ['MCard', 'MTable', 'MStatus'],
|
|
412
|
-
scriptSetup: `const recentColumns = [
|
|
413
|
-
{ key: 'id', label: '工单号', width: 96 },
|
|
414
|
-
{ key: 'title', label: '标题' },
|
|
415
|
-
{ key: 'status', label: '状态', width: 110 },
|
|
416
|
-
]
|
|
441
|
+
scriptSetup: `const recentColumns = [
|
|
442
|
+
{ key: 'id', label: '工单号', width: 96 },
|
|
443
|
+
{ key: 'title', label: '标题' },
|
|
444
|
+
{ key: 'status', label: '状态', width: 110 },
|
|
445
|
+
]
|
|
417
446
|
const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
418
|
-
template: `<MCard title="最近工单">
|
|
419
|
-
<MTable :columns="recentColumns" :rows="recentRows" size="small" :paginator="false" bordered row-key="id">
|
|
420
|
-
<template #cell-status="{ value }">
|
|
421
|
-
<MStatus :label="String(value ?? '')" severity="secondary" />
|
|
422
|
-
</template>
|
|
423
|
-
</MTable>
|
|
447
|
+
template: `<MCard title="最近工单">
|
|
448
|
+
<MTable :columns="recentColumns" :rows="recentRows" size="small" :paginator="false" bordered row-key="id">
|
|
449
|
+
<template #cell-status="{ value }">
|
|
450
|
+
<MStatus :label="String(value ?? '')" severity="secondary" />
|
|
451
|
+
</template>
|
|
452
|
+
</MTable>
|
|
424
453
|
</MCard>`,
|
|
425
454
|
rules: ['明细列表放 MCard 内合理', 'size="small" 适合卡片内表格', '行内业务状态用 MStatus'],
|
|
426
455
|
rulesEn: ['Detail lists inside MCard are appropriate', 'Use size="small" for in-card tables', 'Use MStatus for row business status'],
|
|
@@ -436,10 +465,10 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
436
465
|
pageTypes: ['list', 'common'],
|
|
437
466
|
keywords: ['page content', 'stack', 'layout content', '容器'],
|
|
438
467
|
imports: ['MPageContent'],
|
|
439
|
-
template: `<MLayoutContent>
|
|
440
|
-
<MPageContent>
|
|
441
|
-
<!-- MPageFilters → MPageToolbar → MTable -->
|
|
442
|
-
</MPageContent>
|
|
468
|
+
template: `<MLayoutContent>
|
|
469
|
+
<MPageContent>
|
|
470
|
+
<!-- MPageFilters → MPageToolbar → MTable -->
|
|
471
|
+
</MPageContent>
|
|
443
472
|
</MLayoutContent>`,
|
|
444
473
|
rules: ['所有列表区块放在 MPageContent 内', '区块顺序:筛选 → 工具栏 → 表格'],
|
|
445
474
|
rulesEn: ['Keep all list sections inside MPageContent', 'Order: filters → toolbar → table'],
|
|
@@ -455,10 +484,10 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
455
484
|
pageTypes: ['form', 'common'],
|
|
456
485
|
keywords: ['narrow', 'form content', 'max-width'],
|
|
457
486
|
imports: ['MPageContent'],
|
|
458
|
-
template: `<MLayoutContent>
|
|
459
|
-
<MPageContent width="narrow">
|
|
460
|
-
<!-- MPageHeader → MPageSection form → actions -->
|
|
461
|
-
</MPageContent>
|
|
487
|
+
template: `<MLayoutContent>
|
|
488
|
+
<MPageContent width="narrow">
|
|
489
|
+
<!-- MPageHeader → MPageSection form → actions -->
|
|
490
|
+
</MPageContent>
|
|
462
491
|
</MLayoutContent>`,
|
|
463
492
|
rules: ['表单页使用 width="narrow"', '不要手写 max-width CSS'],
|
|
464
493
|
rulesEn: ['Use width="narrow" on form pages', 'Do not hand-write max-width CSS'],
|
|
@@ -474,14 +503,14 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
474
503
|
pageTypes: ['detail'],
|
|
475
504
|
keywords: ['detail', '详情', 'edit', 'header'],
|
|
476
505
|
imports: ['MPageHeader', 'MButton', 'MStatus', 'MSpace'],
|
|
477
|
-
template: `<MPageHeader title="示例资源" description="查看摘要与属性。">
|
|
478
|
-
<template #actions>
|
|
479
|
-
<MSpace>
|
|
480
|
-
<MStatus label="正常" severity="success" />
|
|
481
|
-
<MButton severity="primary">编辑</MButton>
|
|
482
|
-
<MButton severity="danger" text>删除</MButton>
|
|
483
|
-
</MSpace>
|
|
484
|
-
</template>
|
|
506
|
+
template: `<MPageHeader title="示例资源" description="查看摘要与属性。">
|
|
507
|
+
<template #actions>
|
|
508
|
+
<MSpace>
|
|
509
|
+
<MStatus label="正常" severity="success" />
|
|
510
|
+
<MButton severity="primary">编辑</MButton>
|
|
511
|
+
<MButton severity="danger" text>删除</MButton>
|
|
512
|
+
</MSpace>
|
|
513
|
+
</template>
|
|
485
514
|
</MPageHeader>`,
|
|
486
515
|
rules: ['状态用 MStatus 放在 actions 区', '编辑为 primary,删除用 danger text'],
|
|
487
516
|
rulesEn: ['Use MStatus in the actions area', 'Edit is primary; delete uses danger text'],
|
|
@@ -507,29 +536,29 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
507
536
|
'MConfigProvider',
|
|
508
537
|
'zhCN',
|
|
509
538
|
],
|
|
510
|
-
template: `<MConfigProvider :locale="zhCN">
|
|
511
|
-
<MLayout fill-viewport has-sider>
|
|
512
|
-
<MLayoutSider v-model:collapsed="siderCollapsed" bordered :collapsed-width="72">
|
|
513
|
-
<MMenu
|
|
514
|
-
:model="[
|
|
515
|
-
{ key: 'users', label: '用户管理', icon: 'user', to: '/users' },
|
|
516
|
-
{ key: 'roles', label: '角色管理', icon: 'shield', to: '/roles' },
|
|
517
|
-
]"
|
|
518
|
-
:collapsed="siderCollapsed"
|
|
519
|
-
:collapsed-width="72"
|
|
520
|
-
/>
|
|
521
|
-
</MLayoutSider>
|
|
522
|
-
<MLayout>
|
|
523
|
-
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
524
|
-
<MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '当前页' }]" />
|
|
525
|
-
</MLayoutHeader>
|
|
526
|
-
<MLayoutContent>
|
|
527
|
-
<MPageContent>
|
|
528
|
-
<!-- page sections -->
|
|
529
|
-
</MPageContent>
|
|
530
|
-
</MLayoutContent>
|
|
531
|
-
</MLayout>
|
|
532
|
-
</MLayout>
|
|
539
|
+
template: `<MConfigProvider :locale="zhCN">
|
|
540
|
+
<MLayout fill-viewport has-sider>
|
|
541
|
+
<MLayoutSider v-model:collapsed="siderCollapsed" bordered :collapsed-width="72">
|
|
542
|
+
<MMenu
|
|
543
|
+
:model="[
|
|
544
|
+
{ key: 'users', label: '用户管理', icon: 'user', to: '/users' },
|
|
545
|
+
{ key: 'roles', label: '角色管理', icon: 'shield', to: '/roles' },
|
|
546
|
+
]"
|
|
547
|
+
:collapsed="siderCollapsed"
|
|
548
|
+
:collapsed-width="72"
|
|
549
|
+
/>
|
|
550
|
+
</MLayoutSider>
|
|
551
|
+
<MLayout>
|
|
552
|
+
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
553
|
+
<MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '当前页' }]" />
|
|
554
|
+
</MLayoutHeader>
|
|
555
|
+
<MLayoutContent>
|
|
556
|
+
<MPageContent>
|
|
557
|
+
<!-- page sections -->
|
|
558
|
+
</MPageContent>
|
|
559
|
+
</MLayoutContent>
|
|
560
|
+
</MLayout>
|
|
561
|
+
</MLayout>
|
|
533
562
|
</MConfigProvider>`,
|
|
534
563
|
rules: [
|
|
535
564
|
'根布局用 fillViewport',
|
|
@@ -562,19 +591,19 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
562
591
|
'log',
|
|
563
592
|
],
|
|
564
593
|
imports: ['MScrollbar', 'MCard'],
|
|
565
|
-
template: `<MCard title="活动日志">
|
|
566
|
-
<MScrollbar max-height="16rem">
|
|
567
|
-
<ul style="margin:0;padding:0;list-style:none">
|
|
568
|
-
<li v-for="item in logLines" :key="item.id" style="padding:var(--m-space-2) 0;border-bottom:1px solid var(--m-color-border)">
|
|
569
|
-
{{ item.text }}
|
|
570
|
-
</li>
|
|
571
|
-
</ul>
|
|
572
|
-
</MScrollbar>
|
|
594
|
+
template: `<MCard title="活动日志">
|
|
595
|
+
<MScrollbar max-height="16rem">
|
|
596
|
+
<ul style="margin:0;padding:0;list-style:none">
|
|
597
|
+
<li v-for="item in logLines" :key="item.id" style="padding:var(--m-space-2) 0;border-bottom:1px solid var(--m-color-border)">
|
|
598
|
+
{{ item.text }}
|
|
599
|
+
</li>
|
|
600
|
+
</ul>
|
|
601
|
+
</MScrollbar>
|
|
573
602
|
</MCard>`,
|
|
574
|
-
scriptSetup: `const logLines = [
|
|
575
|
-
{ id: '1', text: '用户 admin 登录成功' },
|
|
576
|
-
{ id: '2', text: '导出任务已完成' },
|
|
577
|
-
{ id: '3', text: '配置已保存' },
|
|
603
|
+
scriptSetup: `const logLines = [
|
|
604
|
+
{ id: '1', text: '用户 admin 登录成功' },
|
|
605
|
+
{ id: '2', text: '导出任务已完成' },
|
|
606
|
+
{ id: '3', text: '配置已保存' },
|
|
578
607
|
]`,
|
|
579
608
|
rules: [
|
|
580
609
|
'整页主滚动推荐 MLayout fillViewport',
|