@morya-ui/mcp 0.4.4 → 0.5.0

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.
@@ -1,272 +1,272 @@
1
- <script setup lang="ts">
2
- /**
3
- * 黄金样例:列表页 · rail(侧栏强调)
4
- * structure: list · style: rail
5
- * 氛围只在 chrome(侧栏);内容区保持克制。镜像区块顺序。
6
- * @see list-page.vue · style-presets.md · visual-craft § Atmosphere
7
- */
8
- import {
9
- MBreadcrumb,
10
- MButton,
11
- MConfigProvider,
12
- MEmpty,
13
- MInput,
14
- MLayout,
15
- MLayoutContent,
16
- MLayoutHeader,
17
- MLayoutSider,
18
- MMenu,
19
- MPageContent,
20
- MPageFilterChips,
21
- MPageFilters,
22
- MPageHeader,
23
- MSelect,
24
- MSpace,
25
- MStatus,
26
- MTable,
27
- MTag,
28
- zhCN,
29
- } from 'morya-ui'
30
- import { computed, ref } from 'vue'
31
-
32
- const keyword = ref('')
33
- const status = ref<string | undefined>()
34
- const department = ref<string | null>(null)
35
- const filtersExpanded = ref(false)
36
- const siderCollapsed = ref(false)
37
-
38
- const menuModel = [
39
- { key: 'users', label: '用户管理', icon: 'user', to: '/users' },
40
- { key: 'roles', label: '角色管理', icon: 'shield', to: '/roles' },
41
- { key: 'settings', label: '系统设置', icon: 'settings', to: '/settings' },
42
- ]
43
-
44
- const statusOptions = [
45
- { label: '全部', value: '' },
46
- { label: '启用', value: 'active' },
47
- { label: '停用', value: 'inactive' },
48
- ]
49
-
50
- const departmentOptions = [
51
- { label: '研发', value: '研发' },
52
- { label: '运营', value: '运营' },
53
- { label: '市场', value: '市场' },
54
- ]
55
-
56
- const activeFilters = computed(() => {
57
- const items: Array<{ key: string; label: string }> = []
58
- if (keyword.value.trim()) {
59
- items.push({ key: 'keyword', label: `关键词:${keyword.value.trim()}` })
60
- }
61
- if (status.value) {
62
- const label = statusOptions.find((o) => o.value === status.value)?.label ?? status.value
63
- items.push({ key: 'status', label: `状态:${label}` })
64
- }
65
- if (department.value) {
66
- items.push({ key: 'department', label: `部门:${department.value}` })
67
- }
68
- return items
69
- })
70
-
71
- function clearFilter(key: string) {
72
- if (key === 'keyword') keyword.value = ''
73
- if (key === 'status') status.value = undefined
74
- if (key === 'department') department.value = null
75
- }
76
-
77
- function resetFilters() {
78
- keyword.value = ''
79
- status.value = undefined
80
- department.value = null
81
- }
82
-
83
- const columns = [
84
- { key: 'name', label: '名称' },
85
- { key: 'status', label: '状态', width: 120 },
86
- { key: 'updatedAt', label: '更新时间', width: 140 },
87
- { key: 'actions', label: '操作', width: 148 },
88
- ]
89
-
90
- const rows = [
91
- { id: '1', name: '示例项目 A', status: 'active', updatedAt: '2026-09-01' },
92
- { id: '2', name: '示例项目 B', status: 'inactive', updatedAt: '2026-08-28' },
93
- { id: '3', name: '示例项目 C', status: 'active', updatedAt: '2026-08-25' },
94
- { id: '4', name: '示例项目 D', status: 'active', updatedAt: '2026-08-20' },
95
- { id: '5', name: '示例项目 E', status: 'inactive', updatedAt: '2026-08-15' },
96
- ]
97
- </script>
98
-
99
- <template>
100
- <MConfigProvider :locale="zhCN">
101
- <MLayout has-sider fill-viewport>
102
- <MLayoutSider
103
- v-model:collapsed="siderCollapsed"
104
- bordered
105
- :collapsed-width="72"
106
- class="gp-rail-sider"
107
- >
108
- <div class="gp-rail-sider__brand">
109
- <span class="gp-rail-sider__mark" aria-hidden="true" />
110
- <span v-if="!siderCollapsed" class="gp-rail-sider__name">Acme Ops</span>
111
- </div>
112
- <MMenu
113
- :model="menuModel"
114
- :collapsed="siderCollapsed"
115
- :collapsed-width="72"
116
- selected-key="users"
117
- />
118
- </MLayoutSider>
119
-
120
- <MLayout>
121
- <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)" class="gp-rail-header">
122
- <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '用户管理' }]" />
123
- </MLayoutHeader>
124
-
125
- <MLayoutContent>
126
- <MPageContent fill>
127
- <MPageHeader title="用户管理" description="维护账号、角色与权限。">
128
- <template #actions>
129
- <MButton type="primary">
130
- 新建用户
131
- </MButton>
132
- </template>
133
- </MPageHeader>
134
-
135
- <MPageFilters
136
- v-model:expanded="filtersExpanded"
137
- aria-label="筛选"
138
- variant="filled"
139
- collapsible
140
- >
141
- <MSpace wrap>
142
- <MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
143
- <MSelect
144
- v-model="status"
145
- :options="statusOptions"
146
- placeholder="状态"
147
- clearable
148
- style="width: 10rem"
149
- />
150
- </MSpace>
151
- <template #actions>
152
- <MButton>
153
- 查询
154
- </MButton>
155
- <MButton @click="resetFilters" type="text">
156
- 重置
157
- </MButton>
158
- </template>
159
- <template #advanced>
160
- <MSpace wrap>
161
- <MSelect
162
- v-model="department"
163
- :options="departmentOptions"
164
- placeholder="部门"
165
- clearable
166
- style="width: 10rem"
167
- />
168
- </MSpace>
169
- </template>
170
- </MPageFilters>
171
-
172
- <MPageFilterChips v-if="activeFilters.length" label="已选" aria-label="已选筛选">
173
- <MTag
174
- v-for="item in activeFilters"
175
- :key="item.key"
176
- :value="item.label"
177
- size="small"
178
- bordered
179
- closable
180
- @close="clearFilter(item.key)"
181
- />
182
- </MPageFilterChips>
183
-
184
- <MTable
185
- :columns="columns"
186
- :rows="rows"
187
- :rows-per-page="5"
188
- fill
189
- paginator
190
- striped
191
- bordered
192
- row-key="id"
193
- aria-label="用户列表"
194
- >
195
- <template #cell-status="{ value }">
196
- <MStatus
197
- :label="value === 'active' ? '启用' : '停用'"
198
- :type="value === 'active' ? 'success' : 'secondary'"
199
- />
200
- </template>
201
- <template #cell-actions>
202
- <MSpace>
203
- <MButton size="small" type="text">
204
- 编辑
205
- </MButton>
206
- <MButton size="small" type="text" danger>
207
- 删除
208
- </MButton>
209
- </MSpace>
210
- </template>
211
- <template #empty>
212
- <MEmpty
213
- title="还没有用户"
214
- description="创建第一个用户后,即可分配角色与权限。"
215
- icon="user"
216
- >
217
- <template #extra>
218
- <MButton type="primary">
219
- 新建用户
220
- </MButton>
221
- </template>
222
- </MEmpty>
223
- </template>
224
- </MTable>
225
- </MPageContent>
226
- </MLayoutContent>
227
- </MLayout>
228
- </MLayout>
229
- </MConfigProvider>
230
- </template>
231
-
232
- <style scoped>
233
- .gp-rail-sider {
234
- background:
235
- linear-gradient(
236
- 180deg,
237
- color-mix(in srgb, var(--m-color-primary) 14%, var(--m-color-surface)) 0%,
238
- var(--m-color-surface) 42%
239
- );
240
- }
241
-
242
- .gp-rail-sider__brand {
243
- align-items: center;
244
- display: flex;
245
- gap: var(--m-space-2);
246
- min-height: 3rem;
247
- padding: var(--m-space-3) var(--m-space-4);
248
- }
249
-
250
- .gp-rail-sider__mark {
251
- background: var(--m-color-primary);
252
- border-radius: var(--m-radius-sm);
253
- flex-shrink: 0;
254
- height: 1.25rem;
255
- width: 1.25rem;
256
- }
257
-
258
- .gp-rail-sider__name {
259
- color: var(--m-color-text);
260
- font-size: var(--m-font-size-md);
261
- font-weight: 600;
262
- letter-spacing: -0.02em;
263
- overflow: hidden;
264
- text-overflow: ellipsis;
265
- white-space: nowrap;
266
- }
267
-
268
- .gp-rail-header {
269
- border-bottom: 1px solid var(--m-color-border);
270
- box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--m-color-primary) 18%, transparent);
271
- }
272
- </style>
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:列表页 · rail(侧栏强调)
4
+ * structure: list · style: rail
5
+ * 氛围只在 chrome(侧栏);内容区保持克制。镜像区块顺序。
6
+ * @see list-page.vue · style-presets.md · visual-craft § Atmosphere
7
+ */
8
+ import {
9
+ MBreadcrumb,
10
+ MButton,
11
+ MConfigProvider,
12
+ MEmpty,
13
+ MInput,
14
+ MLayout,
15
+ MLayoutContent,
16
+ MLayoutHeader,
17
+ MLayoutSider,
18
+ MMenu,
19
+ MPageContent,
20
+ MPageFilterChips,
21
+ MPageFilters,
22
+ MPageHeader,
23
+ MSelect,
24
+ MSpace,
25
+ MStatus,
26
+ MTable,
27
+ MTag,
28
+ zhCN,
29
+ } from 'morya-ui'
30
+ import { computed, ref } from 'vue'
31
+
32
+ const keyword = ref('')
33
+ const status = ref<string | undefined>()
34
+ const department = ref<string | null>(null)
35
+ const filtersExpanded = ref(false)
36
+ const siderCollapsed = ref(false)
37
+
38
+ const menuModel = [
39
+ { key: 'users', label: '用户管理', icon: 'user', to: '/users' },
40
+ { key: 'roles', label: '角色管理', icon: 'shield', to: '/roles' },
41
+ { key: 'settings', label: '系统设置', icon: 'settings', to: '/settings' },
42
+ ]
43
+
44
+ const statusOptions = [
45
+ { label: '全部', value: '' },
46
+ { label: '启用', value: 'active' },
47
+ { label: '停用', value: 'inactive' },
48
+ ]
49
+
50
+ const departmentOptions = [
51
+ { label: '研发', value: '研发' },
52
+ { label: '运营', value: '运营' },
53
+ { label: '市场', value: '市场' },
54
+ ]
55
+
56
+ const activeFilters = computed(() => {
57
+ const items: Array<{ key: string; label: string }> = []
58
+ if (keyword.value.trim()) {
59
+ items.push({ key: 'keyword', label: `关键词:${keyword.value.trim()}` })
60
+ }
61
+ if (status.value) {
62
+ const label = statusOptions.find((o) => o.value === status.value)?.label ?? status.value
63
+ items.push({ key: 'status', label: `状态:${label}` })
64
+ }
65
+ if (department.value) {
66
+ items.push({ key: 'department', label: `部门:${department.value}` })
67
+ }
68
+ return items
69
+ })
70
+
71
+ function clearFilter(key: string) {
72
+ if (key === 'keyword') keyword.value = ''
73
+ if (key === 'status') status.value = undefined
74
+ if (key === 'department') department.value = null
75
+ }
76
+
77
+ function resetFilters() {
78
+ keyword.value = ''
79
+ status.value = undefined
80
+ department.value = null
81
+ }
82
+
83
+ const columns = [
84
+ { key: 'name', label: '名称' },
85
+ { key: 'status', label: '状态', width: 120 },
86
+ { key: 'updatedAt', label: '更新时间', width: 140 },
87
+ { key: 'actions', label: '操作', width: 148 },
88
+ ]
89
+
90
+ const rows = [
91
+ { id: '1', name: '示例项目 A', status: 'active', updatedAt: '2026-09-01' },
92
+ { id: '2', name: '示例项目 B', status: 'inactive', updatedAt: '2026-08-28' },
93
+ { id: '3', name: '示例项目 C', status: 'active', updatedAt: '2026-08-25' },
94
+ { id: '4', name: '示例项目 D', status: 'active', updatedAt: '2026-08-20' },
95
+ { id: '5', name: '示例项目 E', status: 'inactive', updatedAt: '2026-08-15' },
96
+ ]
97
+ </script>
98
+
99
+ <template>
100
+ <MConfigProvider :locale="zhCN">
101
+ <MLayout has-sider fill-viewport>
102
+ <MLayoutSider
103
+ v-model:collapsed="siderCollapsed"
104
+ bordered
105
+ :collapsed-width="72"
106
+ class="gp-rail-sider"
107
+ >
108
+ <div class="gp-rail-sider__brand">
109
+ <span class="gp-rail-sider__mark" aria-hidden="true" />
110
+ <span v-if="!siderCollapsed" class="gp-rail-sider__name">Acme Ops</span>
111
+ </div>
112
+ <MMenu
113
+ :model="menuModel"
114
+ :collapsed="siderCollapsed"
115
+ :collapsed-width="72"
116
+ selected-key="users"
117
+ />
118
+ </MLayoutSider>
119
+
120
+ <MLayout>
121
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)" class="gp-rail-header">
122
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '用户管理' }]" />
123
+ </MLayoutHeader>
124
+
125
+ <MLayoutContent>
126
+ <MPageContent fill>
127
+ <MPageHeader title="用户管理" description="维护账号、角色与权限。">
128
+ <template #actions>
129
+ <MButton type="primary">
130
+ 新建用户
131
+ </MButton>
132
+ </template>
133
+ </MPageHeader>
134
+
135
+ <MPageFilters
136
+ v-model:expanded="filtersExpanded"
137
+ aria-label="筛选"
138
+ variant="filled"
139
+ collapsible
140
+ >
141
+ <MSpace wrap>
142
+ <MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
143
+ <MSelect
144
+ v-model="status"
145
+ :options="statusOptions"
146
+ placeholder="状态"
147
+ clearable
148
+ style="width: 10rem"
149
+ />
150
+ </MSpace>
151
+ <template #actions>
152
+ <MButton>
153
+ 查询
154
+ </MButton>
155
+ <MButton @click="resetFilters" type="text">
156
+ 重置
157
+ </MButton>
158
+ </template>
159
+ <template #advanced>
160
+ <MSpace wrap>
161
+ <MSelect
162
+ v-model="department"
163
+ :options="departmentOptions"
164
+ placeholder="部门"
165
+ clearable
166
+ style="width: 10rem"
167
+ />
168
+ </MSpace>
169
+ </template>
170
+ </MPageFilters>
171
+
172
+ <MPageFilterChips v-if="activeFilters.length" label="已选" aria-label="已选筛选">
173
+ <MTag
174
+ v-for="item in activeFilters"
175
+ :key="item.key"
176
+ :value="item.label"
177
+ size="small"
178
+ bordered
179
+ closable
180
+ @close="clearFilter(item.key)"
181
+ />
182
+ </MPageFilterChips>
183
+
184
+ <MTable
185
+ :columns="columns"
186
+ :rows="rows"
187
+ :rows-per-page="5"
188
+ fill
189
+ paginator
190
+ striped
191
+ bordered
192
+ row-key="id"
193
+ aria-label="用户列表"
194
+ >
195
+ <template #cell-status="{ value }">
196
+ <MStatus
197
+ :label="value === 'active' ? '启用' : '停用'"
198
+ :type="value === 'active' ? 'success' : 'secondary'"
199
+ />
200
+ </template>
201
+ <template #cell-actions>
202
+ <MSpace>
203
+ <MButton size="small" type="text">
204
+ 编辑
205
+ </MButton>
206
+ <MButton size="small" type="text" danger>
207
+ 删除
208
+ </MButton>
209
+ </MSpace>
210
+ </template>
211
+ <template #empty>
212
+ <MEmpty
213
+ title="还没有用户"
214
+ description="创建第一个用户后,即可分配角色与权限。"
215
+ icon="user"
216
+ >
217
+ <template #extra>
218
+ <MButton type="primary">
219
+ 新建用户
220
+ </MButton>
221
+ </template>
222
+ </MEmpty>
223
+ </template>
224
+ </MTable>
225
+ </MPageContent>
226
+ </MLayoutContent>
227
+ </MLayout>
228
+ </MLayout>
229
+ </MConfigProvider>
230
+ </template>
231
+
232
+ <style scoped>
233
+ .gp-rail-sider {
234
+ background:
235
+ linear-gradient(
236
+ 180deg,
237
+ color-mix(in srgb, var(--m-color-primary) 14%, var(--m-color-surface)) 0%,
238
+ var(--m-color-surface) 42%
239
+ );
240
+ }
241
+
242
+ .gp-rail-sider__brand {
243
+ align-items: center;
244
+ display: flex;
245
+ gap: var(--m-space-2);
246
+ min-height: 3rem;
247
+ padding: var(--m-space-3) var(--m-space-4);
248
+ }
249
+
250
+ .gp-rail-sider__mark {
251
+ background: var(--m-color-primary);
252
+ border-radius: var(--m-radius-sm);
253
+ flex-shrink: 0;
254
+ height: 1.25rem;
255
+ width: 1.25rem;
256
+ }
257
+
258
+ .gp-rail-sider__name {
259
+ color: var(--m-color-text);
260
+ font-size: var(--m-font-size-md);
261
+ font-weight: 600;
262
+ letter-spacing: -0.02em;
263
+ overflow: hidden;
264
+ text-overflow: ellipsis;
265
+ white-space: nowrap;
266
+ }
267
+
268
+ .gp-rail-header {
269
+ border-bottom: 1px solid var(--m-color-border);
270
+ box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--m-color-primary) 18%, transparent);
271
+ }
272
+ </style>