@morya-ui/mcp 0.3.1 → 0.3.2
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/data/catalog.json +278 -118
- package/data/example-coverage.json +24 -14
- package/data/golden-pages/dashboard-page.vue +10 -8
- package/data/golden-pages/empty-state.vue +20 -51
- package/data/golden-pages/form-in-dialog.vue +3 -3
- package/data/golden-pages/list-page.vue +77 -12
- package/dist/__tests__/tools.test.js +1 -1
- package/dist/page-snippets.js +127 -35
- package/dist/tools.js +23 -16
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-
|
|
3
|
-
"catalogGeneratedAt": "2026-09-
|
|
2
|
+
"generatedAt": "2026-09-24T03:18:19.583Z",
|
|
3
|
+
"catalogGeneratedAt": "2026-09-24T03:18:15.247Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"id": "Accordion",
|
|
@@ -198,12 +198,14 @@
|
|
|
198
198
|
"exportName": "MButton",
|
|
199
199
|
"examples": 20,
|
|
200
200
|
"props": {
|
|
201
|
-
"total":
|
|
201
|
+
"total": 26,
|
|
202
202
|
"covered": 21,
|
|
203
203
|
"missing": [
|
|
204
204
|
"variant",
|
|
205
|
-
"
|
|
206
|
-
"
|
|
205
|
+
"ripple",
|
|
206
|
+
"press",
|
|
207
|
+
"nativeType",
|
|
208
|
+
"autofocus"
|
|
207
209
|
]
|
|
208
210
|
},
|
|
209
211
|
"events": {
|
|
@@ -231,7 +233,7 @@
|
|
|
231
233
|
{
|
|
232
234
|
"id": "Card",
|
|
233
235
|
"exportName": "MCard",
|
|
234
|
-
"examples":
|
|
236
|
+
"examples": 10,
|
|
235
237
|
"props": {
|
|
236
238
|
"total": 8,
|
|
237
239
|
"covered": 4,
|
|
@@ -1920,13 +1922,15 @@
|
|
|
1920
1922
|
{
|
|
1921
1923
|
"id": "Page",
|
|
1922
1924
|
"exportName": "MPageContent",
|
|
1923
|
-
"examples":
|
|
1925
|
+
"examples": 10,
|
|
1924
1926
|
"props": {
|
|
1925
|
-
"total":
|
|
1926
|
-
"covered":
|
|
1927
|
+
"total": 24,
|
|
1928
|
+
"covered": 19,
|
|
1927
1929
|
"missing": [
|
|
1930
|
+
"bands",
|
|
1931
|
+
"expandLabel",
|
|
1932
|
+
"collapseLabel",
|
|
1928
1933
|
"headingLevel",
|
|
1929
|
-
"trendSeverity",
|
|
1930
1934
|
"minHeight"
|
|
1931
1935
|
]
|
|
1932
1936
|
},
|
|
@@ -1936,9 +1940,15 @@
|
|
|
1936
1940
|
"missing": []
|
|
1937
1941
|
},
|
|
1938
1942
|
"slots": {
|
|
1939
|
-
"total":
|
|
1940
|
-
"covered":
|
|
1941
|
-
"missing": [
|
|
1943
|
+
"total": 8,
|
|
1944
|
+
"covered": 3,
|
|
1945
|
+
"missing": [
|
|
1946
|
+
"active",
|
|
1947
|
+
"breadcrumb",
|
|
1948
|
+
"tags",
|
|
1949
|
+
"icon",
|
|
1950
|
+
"extra"
|
|
1951
|
+
]
|
|
1942
1952
|
},
|
|
1943
1953
|
"methods": {
|
|
1944
1954
|
"total": 0,
|
|
@@ -3377,6 +3387,6 @@
|
|
|
3377
3387
|
"summary": {
|
|
3378
3388
|
"components": 93,
|
|
3379
3389
|
"componentsWithMissingCoverage": 93,
|
|
3380
|
-
"totalExamples":
|
|
3390
|
+
"totalExamples": 817
|
|
3381
3391
|
}
|
|
3382
3392
|
}
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
MLayoutContent,
|
|
14
14
|
MLayoutHeader,
|
|
15
15
|
MPageContent,
|
|
16
|
+
MEmpty,
|
|
16
17
|
MPageHeader,
|
|
17
|
-
MPagePlaceholder,
|
|
18
18
|
MPageStat,
|
|
19
19
|
MStatus,
|
|
20
20
|
MTable,
|
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
} from 'morya-ui'
|
|
24
24
|
|
|
25
25
|
const stats = [
|
|
26
|
-
{ label: '总用户', value: '12,480', trend: '+8.2%', icon: 'users' },
|
|
27
|
-
{ label: '今日活跃', value: '1,926', trend: '+3.1%', icon: 'activity' },
|
|
28
|
-
{ label: '待处理工单', value: '47', trend: '-12%', trendSeverity: 'warn' as const, icon: 'clipboard' },
|
|
26
|
+
{ label: '总用户', value: '12,480', trend: '+8.2%', trendDirection: 'up' as const, trendLabel: '较上月', icon: 'users' },
|
|
27
|
+
{ label: '今日活跃', value: '1,926', trend: '+3.1%', trendDirection: 'up' as const, trendLabel: '较昨日', icon: 'activity' },
|
|
28
|
+
{ label: '待处理工单', value: '47', trend: '-12%', trendDirection: 'down' as const, trendLabel: '较上周', trendSeverity: 'warn' as const, icon: 'clipboard' },
|
|
29
29
|
{ label: '系统健康', value: '99.9%', trend: '稳定', trendSeverity: 'secondary' as const, icon: 'heart' },
|
|
30
30
|
]
|
|
31
31
|
|
|
@@ -88,6 +88,8 @@ function statusSeverity(s: string) {
|
|
|
88
88
|
:value="item.value"
|
|
89
89
|
:trend="item.trend"
|
|
90
90
|
:trend-severity="item.trendSeverity ?? 'primary'"
|
|
91
|
+
:trend-direction="item.trendDirection"
|
|
92
|
+
:trend-label="item.trendLabel"
|
|
91
93
|
:icon="item.icon"
|
|
92
94
|
/>
|
|
93
95
|
</MGridItem>
|
|
@@ -95,15 +97,15 @@ function statusSeverity(s: string) {
|
|
|
95
97
|
|
|
96
98
|
<MGrid :cols="2" :x-gap="16" :y-gap="16" responsive="screen">
|
|
97
99
|
<MGridItem :span="1">
|
|
98
|
-
<MCard title="趋势概览">
|
|
99
|
-
<
|
|
100
|
-
aria-label="图表占位"
|
|
100
|
+
<MCard title="趋势概览" shadow="always">
|
|
101
|
+
<MEmpty
|
|
101
102
|
description="接入图表组件后展示近 7 日活跃与转化。"
|
|
103
|
+
icon="chart-bar"
|
|
102
104
|
/>
|
|
103
105
|
</MCard>
|
|
104
106
|
</MGridItem>
|
|
105
107
|
<MGridItem :span="1">
|
|
106
|
-
<MCard title="最近工单">
|
|
108
|
+
<MCard title="最近工单" shadow="always">
|
|
107
109
|
<MTable
|
|
108
110
|
:columns="recentColumns"
|
|
109
111
|
:rows="recentRows"
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
MLayoutContent,
|
|
14
14
|
MLayoutHeader,
|
|
15
15
|
MPageContent,
|
|
16
|
-
|
|
16
|
+
MPageHeader,
|
|
17
17
|
zhCN,
|
|
18
18
|
} from 'morya-ui'
|
|
19
19
|
</script>
|
|
@@ -27,61 +27,30 @@ import {
|
|
|
27
27
|
|
|
28
28
|
<MLayoutContent>
|
|
29
29
|
<MPageContent>
|
|
30
|
-
<
|
|
30
|
+
<MPageHeader title="课程" description="管理目录与可见性。">
|
|
31
31
|
<template #actions>
|
|
32
|
-
<MButton
|
|
32
|
+
<MButton severity="primary">
|
|
33
|
+
新建课程
|
|
34
|
+
</MButton>
|
|
33
35
|
</template>
|
|
34
|
-
</
|
|
36
|
+
</MPageHeader>
|
|
35
37
|
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
>
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
<MEmpty
|
|
39
|
+
title="还没有课程"
|
|
40
|
+
description="创建第一门课程后,学员就能在目录里看到它。也可以稍后从模板导入。"
|
|
41
|
+
icon="book"
|
|
42
|
+
>
|
|
43
|
+
<template #extra>
|
|
44
|
+
<MButton severity="primary">
|
|
45
|
+
创建第一门课程
|
|
46
|
+
</MButton>
|
|
47
|
+
<MButton severity="secondary" text>
|
|
48
|
+
从模板导入
|
|
49
|
+
</MButton>
|
|
50
|
+
</template>
|
|
51
|
+
</MEmpty>
|
|
48
52
|
</MPageContent>
|
|
49
53
|
</MLayoutContent>
|
|
50
54
|
</MLayout>
|
|
51
55
|
</MConfigProvider>
|
|
52
56
|
</template>
|
|
53
|
-
|
|
54
|
-
<style scoped>
|
|
55
|
-
.empty-state-shell {
|
|
56
|
-
display: grid;
|
|
57
|
-
place-items: center;
|
|
58
|
-
min-height: 18rem;
|
|
59
|
-
padding: var(--m-space-8) var(--m-space-6);
|
|
60
|
-
border: 1px dashed color-mix(in srgb, var(--m-color-border) 80%, var(--m-color-primary));
|
|
61
|
-
border-radius: var(--m-radius-md);
|
|
62
|
-
background:
|
|
63
|
-
radial-gradient(
|
|
64
|
-
120% 80% at 50% 0%,
|
|
65
|
-
color-mix(in srgb, var(--m-color-primary) 10%, transparent),
|
|
66
|
-
transparent 55%
|
|
67
|
-
),
|
|
68
|
-
var(--m-color-surface);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
72
|
-
.empty-state-shell {
|
|
73
|
-
animation: empty-in 400ms ease both;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@keyframes empty-in {
|
|
78
|
-
from {
|
|
79
|
-
opacity: 0;
|
|
80
|
-
transform: translateY(0.35rem);
|
|
81
|
-
}
|
|
82
|
-
to {
|
|
83
|
-
opacity: 1;
|
|
84
|
-
transform: translateY(0);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
</style>
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
MLayoutContent,
|
|
17
17
|
MLayoutHeader,
|
|
18
18
|
MPageContent,
|
|
19
|
-
|
|
19
|
+
MPageHeader,
|
|
20
20
|
MSelect,
|
|
21
21
|
MSpace,
|
|
22
22
|
MStatus,
|
|
@@ -101,13 +101,13 @@ async function onSave() {
|
|
|
101
101
|
|
|
102
102
|
<MLayoutContent>
|
|
103
103
|
<MPageContent>
|
|
104
|
-
<
|
|
104
|
+
<MPageHeader title="用户管理" description="短表单留在列表内弹窗;字段多时再走独立表单页。">
|
|
105
105
|
<template #actions>
|
|
106
106
|
<MButton severity="primary" @click="openCreate">
|
|
107
107
|
新建用户
|
|
108
108
|
</MButton>
|
|
109
109
|
</template>
|
|
110
|
-
</
|
|
110
|
+
</MPageHeader>
|
|
111
111
|
|
|
112
112
|
<MTable
|
|
113
113
|
:columns="columns"
|
|
@@ -15,18 +15,22 @@ import {
|
|
|
15
15
|
MLayoutSider,
|
|
16
16
|
MMenu,
|
|
17
17
|
MPageContent,
|
|
18
|
+
MPageFilterChips,
|
|
18
19
|
MPageFilters,
|
|
19
|
-
|
|
20
|
+
MPageHeader,
|
|
20
21
|
MSelect,
|
|
21
22
|
MSpace,
|
|
22
23
|
MStatus,
|
|
23
24
|
MTable,
|
|
25
|
+
MTag,
|
|
24
26
|
zhCN,
|
|
25
27
|
} from 'morya-ui'
|
|
26
|
-
import { ref } from 'vue'
|
|
28
|
+
import { computed, ref } from 'vue'
|
|
27
29
|
|
|
28
30
|
const keyword = ref('')
|
|
29
31
|
const status = ref<string | undefined>()
|
|
32
|
+
const department = ref<string | null>(null)
|
|
33
|
+
const filtersExpanded = ref(false)
|
|
30
34
|
const siderCollapsed = ref(false)
|
|
31
35
|
|
|
32
36
|
const menuModel = [
|
|
@@ -41,6 +45,39 @@ const statusOptions = [
|
|
|
41
45
|
{ label: '停用', value: 'inactive' },
|
|
42
46
|
]
|
|
43
47
|
|
|
48
|
+
const departmentOptions = [
|
|
49
|
+
{ label: '研发', value: '研发' },
|
|
50
|
+
{ label: '运营', value: '运营' },
|
|
51
|
+
{ label: '市场', value: '市场' },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const activeFilters = computed(() => {
|
|
55
|
+
const items: Array<{ key: string; label: string }> = []
|
|
56
|
+
if (keyword.value.trim()) {
|
|
57
|
+
items.push({ key: 'keyword', label: `关键词:${keyword.value.trim()}` })
|
|
58
|
+
}
|
|
59
|
+
if (status.value) {
|
|
60
|
+
const label = statusOptions.find((o) => o.value === status.value)?.label ?? status.value
|
|
61
|
+
items.push({ key: 'status', label: `状态:${label}` })
|
|
62
|
+
}
|
|
63
|
+
if (department.value) {
|
|
64
|
+
items.push({ key: 'department', label: `部门:${department.value}` })
|
|
65
|
+
}
|
|
66
|
+
return items
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
function clearFilter(key: string) {
|
|
70
|
+
if (key === 'keyword') keyword.value = ''
|
|
71
|
+
if (key === 'status') status.value = undefined
|
|
72
|
+
if (key === 'department') department.value = null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resetFilters() {
|
|
76
|
+
keyword.value = ''
|
|
77
|
+
status.value = undefined
|
|
78
|
+
department.value = null
|
|
79
|
+
}
|
|
80
|
+
|
|
44
81
|
const columns = [
|
|
45
82
|
{ key: 'name', label: '名称' },
|
|
46
83
|
{ key: 'status', label: '状态', width: 120 },
|
|
@@ -76,7 +113,20 @@ const rows = [
|
|
|
76
113
|
|
|
77
114
|
<MLayoutContent>
|
|
78
115
|
<MPageContent>
|
|
79
|
-
<
|
|
116
|
+
<MPageHeader title="用户管理" description="维护账号、角色与权限。">
|
|
117
|
+
<template #actions>
|
|
118
|
+
<MButton severity="primary">
|
|
119
|
+
新建用户
|
|
120
|
+
</MButton>
|
|
121
|
+
</template>
|
|
122
|
+
</MPageHeader>
|
|
123
|
+
|
|
124
|
+
<MPageFilters
|
|
125
|
+
v-model:expanded="filtersExpanded"
|
|
126
|
+
aria-label="筛选"
|
|
127
|
+
variant="filled"
|
|
128
|
+
collapsible
|
|
129
|
+
>
|
|
80
130
|
<MSpace wrap>
|
|
81
131
|
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
82
132
|
<MSelect
|
|
@@ -86,22 +136,37 @@ const rows = [
|
|
|
86
136
|
clearable
|
|
87
137
|
style="width: 10rem"
|
|
88
138
|
/>
|
|
89
|
-
<MButton severity="
|
|
139
|
+
<MButton severity="secondary">
|
|
90
140
|
查询
|
|
91
141
|
</MButton>
|
|
92
|
-
<MButton severity="secondary">
|
|
142
|
+
<MButton severity="secondary" text @click="resetFilters">
|
|
93
143
|
重置
|
|
94
144
|
</MButton>
|
|
95
145
|
</MSpace>
|
|
146
|
+
<template #advanced>
|
|
147
|
+
<MSpace wrap>
|
|
148
|
+
<MSelect
|
|
149
|
+
v-model="department"
|
|
150
|
+
:options="departmentOptions"
|
|
151
|
+
placeholder="部门"
|
|
152
|
+
clearable
|
|
153
|
+
style="width: 10rem"
|
|
154
|
+
/>
|
|
155
|
+
</MSpace>
|
|
156
|
+
</template>
|
|
96
157
|
</MPageFilters>
|
|
97
158
|
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
159
|
+
<MPageFilterChips v-if="activeFilters.length" label="已选" aria-label="已选筛选">
|
|
160
|
+
<MTag
|
|
161
|
+
v-for="item in activeFilters"
|
|
162
|
+
:key="item.key"
|
|
163
|
+
:value="item.label"
|
|
164
|
+
size="small"
|
|
165
|
+
bordered
|
|
166
|
+
closable
|
|
167
|
+
@close="clearFilter(item.key)"
|
|
168
|
+
/>
|
|
169
|
+
</MPageFilterChips>
|
|
105
170
|
|
|
106
171
|
<MTable
|
|
107
172
|
:columns="columns"
|
|
@@ -149,7 +149,7 @@ describe('@morya-ui/mcp handlers', () => {
|
|
|
149
149
|
});
|
|
150
150
|
it('searches page snippets scope', () => {
|
|
151
151
|
const result = read(handlers.search({ query: 'toolbar', scope: 'snippets', limit: 5 }));
|
|
152
|
-
expect(result.items.some((item) => item.type === 'snippet' && item.id === 'list-toolbar')).toBe(true);
|
|
152
|
+
expect(result.items.some((item) => item.type === 'snippet' && item.id === 'list-batch-toolbar')).toBe(true);
|
|
153
153
|
});
|
|
154
154
|
it('suggests double-border page composition as advisory standard', () => {
|
|
155
155
|
const result = read(handlers.validatePage({
|
package/dist/page-snippets.js
CHANGED
|
@@ -6,7 +6,7 @@ export const pageSnippets = [
|
|
|
6
6
|
description: '在 MPageContent 内追加或替换筛选区,使用 MPageFilters + MSpace 包裹控件。',
|
|
7
7
|
descriptionEn: 'Add or replace a filter region inside MPageContent using MPageFilters and MSpace.',
|
|
8
8
|
pageTypes: ['list'],
|
|
9
|
-
keywords: ['筛选', '搜索', '查询', 'filter', 'search', 'keyword', 'reset'],
|
|
9
|
+
keywords: ['筛选', '搜索', '查询', 'filters', 'filter', 'search', 'keyword', 'reset'],
|
|
10
10
|
imports: ['MPageFilters', 'MSpace', 'MInput', 'MSelect', 'MButton'],
|
|
11
11
|
scriptSetup: `const keyword = ref('')
|
|
12
12
|
const status = ref<string | undefined>()
|
|
@@ -15,7 +15,7 @@ const statusOptions = [
|
|
|
15
15
|
{ label: '启用', value: 'active' },
|
|
16
16
|
{ label: '停用', value: 'inactive' },
|
|
17
17
|
]`,
|
|
18
|
-
template: `<MPageFilters aria-label="筛选">
|
|
18
|
+
template: `<MPageFilters aria-label="筛选" variant="filled">
|
|
19
19
|
<MSpace wrap>
|
|
20
20
|
<MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
|
|
21
21
|
<MSelect
|
|
@@ -25,33 +25,119 @@ const statusOptions = [
|
|
|
25
25
|
clearable
|
|
26
26
|
style="width: 10rem"
|
|
27
27
|
/>
|
|
28
|
-
<MButton severity="
|
|
29
|
-
<MButton severity="secondary">重置</MButton>
|
|
28
|
+
<MButton severity="secondary">查询</MButton>
|
|
29
|
+
<MButton severity="secondary" text>重置</MButton>
|
|
30
30
|
</MSpace>
|
|
31
31
|
</MPageFilters>`,
|
|
32
|
-
rules: ['放在
|
|
33
|
-
rulesEn: ['Place
|
|
32
|
+
rules: ['放在 MPageHeader 之后、表格之前', 'viewport 内仅一个 primary(通常在 Header)', '不要外包 MCard'],
|
|
33
|
+
rulesEn: ['Place after MPageHeader and before the table', 'Keep one primary in the viewport (usually in Header)', 'Do not wrap with MCard'],
|
|
34
34
|
avoid: ['不要手写 .page-filters 类', '不要用 MCard 包筛选区'],
|
|
35
35
|
avoidEn: ['Do not hand-write .page-filters classes', 'Do not wrap filters in MCard'],
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
id: 'list-filters-collapsible',
|
|
39
|
+
title: '列表页折叠筛选',
|
|
40
|
+
titleEn: 'Collapsible list filters',
|
|
41
|
+
description: 'MPageFilters collapsible + #advanced 隐藏次要字段。',
|
|
42
|
+
descriptionEn: 'Hide secondary fields with collapsible MPageFilters and an #advanced slot.',
|
|
43
|
+
pageTypes: ['list'],
|
|
44
|
+
keywords: ['折叠', '高级筛选', 'collapse', 'advanced', 'expand', 'filter'],
|
|
45
|
+
imports: ['MPageFilters', 'MSpace', 'MInput', 'MSelect', 'MButton'],
|
|
46
|
+
scriptSetup: `const filtersExpanded = ref(false)
|
|
47
|
+
const keyword = ref('')
|
|
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>
|
|
71
|
+
</MPageFilters>`,
|
|
72
|
+
rules: ['常用条件放 default,次要条件放 #advanced', '查询/重置保持 secondary'],
|
|
73
|
+
rulesEn: ['Keep primary filters in default; secondary filters in #advanced', 'Query/reset stay secondary'],
|
|
74
|
+
avoid: ['不要用 MCard 包整个筛选区'],
|
|
75
|
+
avoidEn: ['Do not wrap the whole filter bar in MCard'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'list-filter-chips',
|
|
79
|
+
title: '列表页已选筛选',
|
|
80
|
+
titleEn: 'Active filter chips',
|
|
81
|
+
description: 'MPageFilterChips + MTag closable 展示并清除已选条件。',
|
|
82
|
+
descriptionEn: 'Show and clear active filters with MPageFilterChips and closable MTag.',
|
|
83
|
+
pageTypes: ['list'],
|
|
84
|
+
keywords: ['已选', 'chip', 'tag', 'active', 'clear', '筛选'],
|
|
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
|
+
/>
|
|
96
|
+
</MPageFilterChips>`,
|
|
97
|
+
rules: ['放在 MPageFilters 之后、表格之前', '用业务 computed 生成 activeFilters'],
|
|
98
|
+
rulesEn: ['Place after MPageFilters and before the table', 'Derive activeFilters from filter state'],
|
|
99
|
+
avoid: ['不要手写 pill 样式或 dashed 壳'],
|
|
100
|
+
avoidEn: ['Do not hand-write pill or dashed shells'],
|
|
101
|
+
},
|
|
37
102
|
{
|
|
38
103
|
id: 'list-toolbar',
|
|
39
|
-
title: '
|
|
40
|
-
titleEn: 'List page
|
|
41
|
-
description: '
|
|
42
|
-
descriptionEn: 'Page
|
|
104
|
+
title: '列表页页头',
|
|
105
|
+
titleEn: 'List page header',
|
|
106
|
+
description: '页面身份 + 唯一 primary(新建),放在 MPageContent 顶部。',
|
|
107
|
+
descriptionEn: 'Page identity and the single primary action at the top of MPageContent.',
|
|
43
108
|
pageTypes: ['list'],
|
|
44
|
-
keywords: ['
|
|
45
|
-
imports: ['
|
|
46
|
-
template: `<
|
|
109
|
+
keywords: ['页头', '标题', '新建', 'header', 'title', 'create', 'actions'],
|
|
110
|
+
imports: ['MPageHeader', 'MButton'],
|
|
111
|
+
template: `<MPageHeader title="用户管理" description="维护账号、角色与权限。">
|
|
47
112
|
<template #actions>
|
|
48
113
|
<MButton severity="primary">新建用户</MButton>
|
|
49
114
|
</template>
|
|
115
|
+
</MPageHeader>`,
|
|
116
|
+
rules: ['页级标题用 MPageHeader,不要用 MPageToolbar 当第二页头', '主操作放 #actions'],
|
|
117
|
+
rulesEn: ['Use MPageHeader for page identity; do not use MPageToolbar as a second page title', 'Put primary actions in #actions'],
|
|
118
|
+
avoid: ['不要在 Toolbar 上写页面 H1 标题'],
|
|
119
|
+
avoidEn: ['Do not put the page H1 on MPageToolbar'],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'list-batch-toolbar',
|
|
123
|
+
title: '列表页批量操作条',
|
|
124
|
+
titleEn: 'List batch action row',
|
|
125
|
+
description: '表格多选时的批量操作,放在筛选区之后(可选)。',
|
|
126
|
+
descriptionEn: 'Optional batch actions after filters when rows are selectable.',
|
|
127
|
+
pageTypes: ['list'],
|
|
128
|
+
keywords: ['批量', '已选', 'toolbar', 'batch', 'export', 'delete'],
|
|
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>
|
|
50
136
|
</MPageToolbar>`,
|
|
51
|
-
rules: ['
|
|
52
|
-
rulesEn: ['
|
|
53
|
-
avoid: ['
|
|
54
|
-
avoidEn: ['Do not
|
|
137
|
+
rules: ['左侧用 default 插槽展示已选数量', '批量操作用 text/outlined,danger 仅 destructive'],
|
|
138
|
+
rulesEn: ['Use the default slot for selection count', 'Batch actions use text/outlined; danger only for destructive ops'],
|
|
139
|
+
avoid: ['不要用 Toolbar title 充当页面标题'],
|
|
140
|
+
avoidEn: ['Do not use Toolbar title as the page heading'],
|
|
55
141
|
},
|
|
56
142
|
{
|
|
57
143
|
id: 'list-table',
|
|
@@ -271,8 +357,8 @@ const submitting = ref(false)`,
|
|
|
271
357
|
keywords: ['kpi', '指标', 'stat', 'grid', 'metrics'],
|
|
272
358
|
imports: ['MGrid', 'MGridItem', 'MPageStat'],
|
|
273
359
|
scriptSetup: `const metrics = ref([
|
|
274
|
-
{ label: '总用户', value: '12,480', trend: '+8.2%', icon: 'users' },
|
|
275
|
-
{ label: '今日活跃', value: '1,926', trend: '+3.1%', icon: 'activity' },
|
|
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' },
|
|
276
362
|
])`,
|
|
277
363
|
template: `<MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
|
|
278
364
|
<MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
|
|
@@ -280,7 +366,9 @@ const submitting = ref(false)`,
|
|
|
280
366
|
:label="metric.label"
|
|
281
367
|
:value="metric.value"
|
|
282
368
|
:trend="metric.trend"
|
|
283
|
-
|
|
369
|
+
:trend-direction="metric.trendDirection"
|
|
370
|
+
:trend-label="metric.trendLabel"
|
|
371
|
+
:icon="metric.icon"
|
|
284
372
|
/>
|
|
285
373
|
</MGridItem>
|
|
286
374
|
</MGrid>`,
|
|
@@ -293,16 +381,17 @@ const submitting = ref(false)`,
|
|
|
293
381
|
id: 'dashboard-chart-card',
|
|
294
382
|
title: '仪表盘图表卡片',
|
|
295
383
|
titleEn: 'Dashboard chart card',
|
|
296
|
-
description: 'MCard
|
|
297
|
-
descriptionEn: 'MCard
|
|
384
|
+
description: 'MCard shadow="always" + MEmpty 作为图表占位。',
|
|
385
|
+
descriptionEn: 'MCard with shadow="always" and MEmpty for chart pending state.',
|
|
298
386
|
pageTypes: ['dashboard'],
|
|
299
387
|
keywords: ['chart', '图表', 'placeholder', 'card', 'trend'],
|
|
300
|
-
imports: ['MCard', '
|
|
301
|
-
template: `<MCard title="趋势概览">
|
|
388
|
+
imports: ['MCard', 'MEmpty', 'MSkeleton'],
|
|
389
|
+
template: `<MCard title="趋势概览" shadow="always">
|
|
302
390
|
<MSkeleton v-if="loading" height="8rem" />
|
|
303
|
-
<
|
|
391
|
+
<MEmpty
|
|
304
392
|
v-else
|
|
305
393
|
aria-label="图表占位"
|
|
394
|
+
title="暂无图表数据"
|
|
306
395
|
description="图表区域(接入 ECharts / 业务组件)"
|
|
307
396
|
/>
|
|
308
397
|
</MCard>`,
|
|
@@ -380,19 +469,22 @@ const recentRows = ref<Record<string, unknown>[]>([])`,
|
|
|
380
469
|
id: 'detail-toolbar',
|
|
381
470
|
title: '详情页顶栏',
|
|
382
471
|
titleEn: 'Detail page header actions',
|
|
383
|
-
description: '
|
|
384
|
-
descriptionEn: 'Detail page title and edit
|
|
472
|
+
description: '详情页标题、状态与编辑操作。',
|
|
473
|
+
descriptionEn: 'Detail page title, status, and edit actions.',
|
|
385
474
|
pageTypes: ['detail'],
|
|
386
|
-
keywords: ['detail', '详情', 'edit', '
|
|
387
|
-
imports: ['
|
|
388
|
-
template: `<
|
|
475
|
+
keywords: ['detail', '详情', 'edit', 'header'],
|
|
476
|
+
imports: ['MPageHeader', 'MButton', 'MStatus', 'MSpace'],
|
|
477
|
+
template: `<MPageHeader title="示例资源" description="查看摘要与属性。">
|
|
389
478
|
<template #actions>
|
|
390
|
-
<
|
|
479
|
+
<MSpace>
|
|
480
|
+
<MStatus label="正常" severity="success" />
|
|
481
|
+
<MButton severity="primary">编辑</MButton>
|
|
482
|
+
<MButton severity="danger" text>删除</MButton>
|
|
483
|
+
</MSpace>
|
|
391
484
|
</template>
|
|
392
|
-
</
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
rulesEn: ['Keep status Tag near the header area', 'Edit is primary; delete stays low emphasis'],
|
|
485
|
+
</MPageHeader>`,
|
|
486
|
+
rules: ['状态用 MStatus 放在 actions 区', '编辑为 primary,删除用 danger text'],
|
|
487
|
+
rulesEn: ['Use MStatus in the actions area', 'Edit is primary; delete uses danger text'],
|
|
396
488
|
avoid: ['不要把操作散落到多个无关区域'],
|
|
397
489
|
avoidEn: ['Do not scatter actions across unrelated areas'],
|
|
398
490
|
},
|