@morya-ui/mcp 0.3.8 → 0.4.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.
@@ -4,6 +4,7 @@
4
4
  * @see DESIGN.md · morya-ui-pages references/page-layouts.md · visual-craft § Ops polish
5
5
  */
6
6
  import {
7
+ MAlert,
7
8
  MBreadcrumb,
8
9
  MCard,
9
10
  MConfigProvider,
@@ -16,6 +17,7 @@ import {
16
17
  MEmpty,
17
18
  MPageHeader,
18
19
  MPageStat,
20
+ MStatistic,
19
21
  MStatus,
20
22
  MTable,
21
23
  MTag,
@@ -81,6 +83,13 @@ function statusSeverity(s: string) {
81
83
  description="关注今日活跃与待处理工单,异常优先下钻。"
82
84
  />
83
85
 
86
+ <MAlert
87
+ severity="info"
88
+ title="报表管道延迟约 15 分钟"
89
+ description="实时告警仍可用;看板数字以管道落库为准。"
90
+ closable
91
+ />
92
+
84
93
  <MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
85
94
  <MGridItem v-for="item in stats" :key="item.label" :span="1">
86
95
  <MPageStat
@@ -99,13 +108,18 @@ function statusSeverity(s: string) {
99
108
  <MGridItem :span="1">
100
109
  <MCard title="趋势概览" shadow="always">
101
110
  <MEmpty
102
- description="接入图表组件后展示近 7 日活跃与转化。"
103
- icon="chart-bar"
111
+ title="还没有趋势数据"
112
+ description="接入图表后将展示近 7 日活跃与转化。"
113
+ illustration="activity-low"
104
114
  />
105
115
  </MCard>
106
116
  </MGridItem>
107
117
  <MGridItem :span="1">
108
118
  <MCard title="最近工单" shadow="always">
119
+ <div class="dashboard-plain-stats">
120
+ <MStatistic title="本周新建" :value="128" />
121
+ <MStatistic title="平均处理时长" :value="2.4" suffix="h" :precision="1" />
122
+ </div>
109
123
  <MTable
110
124
  :columns="recentColumns"
111
125
  :rows="recentRows"
@@ -136,3 +150,12 @@ function statusSeverity(s: string) {
136
150
  </MLayout>
137
151
  </MConfigProvider>
138
152
  </template>
153
+
154
+ <style scoped>
155
+ .dashboard-plain-stats {
156
+ display: flex;
157
+ flex-wrap: wrap;
158
+ gap: var(--m-space-6);
159
+ margin-bottom: var(--m-space-4);
160
+ }
161
+ </style>
@@ -8,16 +8,20 @@ import {
8
8
  MButton,
9
9
  MCard,
10
10
  MConfigProvider,
11
+ MDescriptions,
12
+ MDescriptionsItem,
11
13
  MDivider,
12
14
  MLayout,
13
15
  MLayoutContent,
14
16
  MLayoutHeader,
17
+ MList,
18
+ MListItem,
19
+ MListItemMeta,
15
20
  MPageContent,
16
21
  MPageHeader,
17
22
  MPageSection,
18
23
  MSpace,
19
24
  MStatus,
20
- MTable,
21
25
  MTag,
22
26
  zhCN,
23
27
  } from 'morya-ui'
@@ -31,12 +35,6 @@ const profile = {
31
35
  status: 'active' as const,
32
36
  }
33
37
 
34
- const activityColumns = [
35
- { key: 'time', label: '时间', width: 160 },
36
- { key: 'action', label: '动作' },
37
- { key: 'channel', label: '来源', width: 100 },
38
- ]
39
-
40
38
  const activityRows = [
41
39
  { id: '1', time: '2026-09-20 14:22', action: '更新了个人资料', channel: 'Web' },
42
40
  { id: '2', time: '2026-09-18 09:05', action: '重置了登录密码', channel: 'Web' },
@@ -92,51 +90,47 @@ const activityRows = [
92
90
  </p>
93
91
  </MPageSection>
94
92
 
95
- <MCard title="基本信息">
96
- <dl class="detail-props">
97
- <div>
98
- <dt>姓名</dt>
99
- <dd>{{ profile.name }}</dd>
100
- </div>
101
- <div>
102
- <dt>邮箱</dt>
103
- <dd>{{ profile.email }}</dd>
104
- </div>
105
- <div>
106
- <dt>角色</dt>
107
- <dd>{{ profile.role }}</dd>
108
- </div>
109
- <div>
110
- <dt>部门</dt>
111
- <dd>{{ profile.dept }}</dd>
112
- </div>
113
- <div>
114
- <dt>入职日期</dt>
115
- <dd>{{ profile.joinedAt }}</dd>
116
- </div>
117
- <div>
118
- <dt>状态</dt>
119
- <dd>
120
- <MStatus
121
- :label="profile.status === 'active' ? '启用' : '停用'"
122
- :severity="profile.status === 'active' ? 'success' : 'secondary'"
123
- />
124
- </dd>
125
- </div>
126
- </dl>
127
- </MCard>
93
+ <MDescriptions title="基本信息" bordered :column="2">
94
+ <MDescriptionsItem label="姓名">
95
+ {{ profile.name }}
96
+ </MDescriptionsItem>
97
+ <MDescriptionsItem label="邮箱">
98
+ {{ profile.email }}
99
+ </MDescriptionsItem>
100
+ <MDescriptionsItem label="角色">
101
+ {{ profile.role }}
102
+ </MDescriptionsItem>
103
+ <MDescriptionsItem label="部门">
104
+ {{ profile.dept }}
105
+ </MDescriptionsItem>
106
+ <MDescriptionsItem label="入职日期">
107
+ {{ profile.joinedAt }}
108
+ </MDescriptionsItem>
109
+ <MDescriptionsItem label="状态">
110
+ <MStatus
111
+ :label="profile.status === 'active' ? '启用' : '停用'"
112
+ :severity="profile.status === 'active' ? 'success' : 'secondary'"
113
+ />
114
+ </MDescriptionsItem>
115
+ </MDescriptions>
128
116
 
129
117
  <MDivider />
130
118
 
131
119
  <MCard title="近期活动">
132
- <MTable
133
- :columns="activityColumns"
134
- :rows="activityRows"
120
+ <MList
121
+ :items="activityRows"
135
122
  row-key="id"
136
- striped
137
- bordered
138
- aria-label="近期活动"
139
- />
123
+ size="small"
124
+ >
125
+ <template #item="{ item }">
126
+ <MListItem>
127
+ <MListItemMeta
128
+ :title="item.action"
129
+ :description="`${item.time} · ${item.channel}`"
130
+ />
131
+ </MListItem>
132
+ </template>
133
+ </MList>
140
134
  </MCard>
141
135
  </MPageContent>
142
136
  </MLayoutContent>
@@ -150,29 +144,4 @@ const activityRows = [
150
144
  color: var(--m-color-text-muted);
151
145
  font-size: var(--m-font-size-sm);
152
146
  }
153
-
154
- .detail-props {
155
- display: grid;
156
- grid-template-columns: repeat(2, minmax(0, 1fr));
157
- gap: var(--m-space-4) var(--m-space-6);
158
- margin: 0;
159
- }
160
-
161
- .detail-props dt {
162
- margin: 0 0 var(--m-space-1);
163
- color: var(--m-color-text-muted);
164
- font-size: var(--m-font-size-xs);
165
- }
166
-
167
- .detail-props dd {
168
- margin: 0;
169
- color: var(--m-color-text);
170
- font-size: var(--m-font-size-md);
171
- }
172
-
173
- @media (max-width: 640px) {
174
- .detail-props {
175
- grid-template-columns: 1fr;
176
- }
177
- }
178
147
  </style>
@@ -38,7 +38,7 @@ import {
38
38
  <MEmpty
39
39
  title="还没有课程"
40
40
  description="创建第一门课程后,学员就能在目录里看到它。也可以稍后从模板导入。"
41
- icon="book"
41
+ illustration="no-content"
42
42
  >
43
43
  <template #extra>
44
44
  <MButton severity="primary">
@@ -4,7 +4,15 @@
4
4
  * @see DESIGN.md · page-layouts.md · surfaces § Express
5
5
  * 首屏单一任务;控件用 M*;色彩只走 --m-*;避开 AI 默认脸。
6
6
  */
7
- import { MAccordion, MButton, MConfigProvider, MTag, zhCN } from 'morya-ui'
7
+ import {
8
+ MAccordion,
9
+ MButton,
10
+ MConfigProvider,
11
+ MParagraph,
12
+ MTag,
13
+ MTitle,
14
+ zhCN,
15
+ } from 'morya-ui'
8
16
 
9
17
  const faqTabs = [
10
18
  { value: 'stack', header: '必须用特定框架吗?' },
@@ -28,10 +36,12 @@ const faqTabs = [
28
36
 
29
37
  <section class="landing-hero" aria-labelledby="hero-title">
30
38
  <p class="landing-hero__brand">流水线 CI</p>
31
- <h1 id="hero-title">把每次提交变成可回放的交付</h1>
32
- <p class="landing-hero__lead">
39
+ <MTitle id="hero-title" class="landing-hero__title">
40
+ 把每次提交变成可回放的交付
41
+ </MTitle>
42
+ <MParagraph class="landing-hero__lead" type="secondary">
33
43
  为工程团队准备的构建与发布编排。少点配置,多看结果。
34
- </p>
44
+ </MParagraph>
35
45
  <div class="landing-hero__cta">
36
46
  <MButton label="开始使用" />
37
47
  <MButton label="查看文档" severity="secondary" text />
@@ -47,8 +57,12 @@ const faqTabs = [
47
57
  </section>
48
58
 
49
59
  <section id="why" class="landing-section">
50
- <h2>为何选择</h2>
51
- <p class="landing-section__lead">面向真实仓库节奏,而不是演示用的仪表盘皮肤。</p>
60
+ <MTitle :level="2">
61
+ 为何选择
62
+ </MTitle>
63
+ <MParagraph class="landing-section__lead" type="secondary">
64
+ 面向真实仓库节奏,而不是演示用的仪表盘皮肤。
65
+ </MParagraph>
52
66
  <ul class="landing-points">
53
67
  <li>
54
68
  <strong>可回放</strong>
@@ -66,44 +80,62 @@ const faqTabs = [
66
80
  </section>
67
81
 
68
82
  <section id="capabilities" class="landing-section landing-section--alt">
69
- <h2>能力</h2>
70
- <p class="landing-section__lead">用组件库控件表达交互,不引入第二套 UI。</p>
83
+ <MTitle :level="2">
84
+ 能力
85
+ </MTitle>
86
+ <MParagraph class="landing-section__lead" type="secondary">
87
+ 用组件库控件表达交互,不引入第二套 UI。
88
+ </MParagraph>
71
89
  <div class="landing-cards">
72
90
  <article>
73
91
  <MTag value="构建" severity="info" />
74
- <h3>并行矩阵</h3>
75
- <p>按系统与 Node 版本展开任务,失败任务可单独重跑。</p>
92
+ <MTitle :level="3">
93
+ 并行矩阵
94
+ </MTitle>
95
+ <MParagraph type="secondary">
96
+ 按系统与 Node 版本展开任务,失败任务可单独重跑。
97
+ </MParagraph>
76
98
  </article>
77
99
  <article>
78
100
  <MTag value="发布" severity="success" />
79
- <h3>环境门禁</h3>
80
- <p>生产发布需要审批与变更说明,记录谁在何时放行。</p>
101
+ <MTitle :level="3">
102
+ 环境门禁
103
+ </MTitle>
104
+ <MParagraph type="secondary">
105
+ 生产发布需要审批与变更说明,记录谁在何时放行。
106
+ </MParagraph>
81
107
  </article>
82
108
  <article>
83
109
  <MTag value="观测" />
84
- <h3>耗时对比</h3>
85
- <p>同分支历史耗时并排,找出突然变慢的步骤。</p>
110
+ <MTitle :level="3">
111
+ 耗时对比
112
+ </MTitle>
113
+ <MParagraph type="secondary">
114
+ 同分支历史耗时并排,找出突然变慢的步骤。
115
+ </MParagraph>
86
116
  </article>
87
117
  </div>
88
118
  </section>
89
119
 
90
120
  <section id="faq" class="landing-section">
91
- <h2>常见问题</h2>
121
+ <MTitle :level="2">
122
+ 常见问题
123
+ </MTitle>
92
124
  <MAccordion :tabs="faqTabs" default-value="stack">
93
125
  <template #stack>
94
- <p class="landing-faq">
126
+ <MParagraph class="landing-faq" type="secondary">
95
127
  任意可容器化的仓库即可。示例以 Vue / Node 为主,不绑定单一前端脚手架。
96
- </p>
128
+ </MParagraph>
97
129
  </template>
98
130
  <template #hosting>
99
- <p class="landing-faq">
131
+ <MParagraph class="landing-faq" type="secondary">
100
132
  可自托管 runner,也可使用托管队列。密钥不进入日志明文。
101
- </p>
133
+ </MParagraph>
102
134
  </template>
103
135
  <template #migrate>
104
- <p class="landing-faq">
136
+ <MParagraph class="landing-faq" type="secondary">
105
137
  从现有 YAML 映射阶段与缓存键;保留原有制品路径可降低切换成本。
106
- </p>
138
+ </MParagraph>
107
139
  </template>
108
140
  </MAccordion>
109
141
  </section>
@@ -168,10 +200,10 @@ const faqTabs = [
168
200
  color: var(--m-color-text-muted);
169
201
  }
170
202
 
171
- .landing-hero h1 {
203
+ .landing-hero__title {
172
204
  margin: 0;
173
205
  max-width: 14em;
174
- font-size: clamp(2.25rem, 5vw, 3.5rem);
206
+ font-size: clamp(2.25rem, 5vw, 3.5rem) !important;
175
207
  font-weight: 700;
176
208
  line-height: 1.12;
177
209
  letter-spacing: -0.035em;
@@ -182,7 +214,6 @@ const faqTabs = [
182
214
  max-width: 36rem;
183
215
  font-size: 1.125rem;
184
216
  line-height: 1.55;
185
- color: var(--m-color-text-muted);
186
217
  }
187
218
 
188
219
  .landing-hero__cta {
@@ -260,7 +291,7 @@ const faqTabs = [
260
291
  max-width: none;
261
292
  }
262
293
 
263
- .landing-section h2 {
294
+ .landing-section :deep(.m-typography-title--h2) {
264
295
  margin: 0 0 var(--m-space-3);
265
296
  font-size: 1.75rem;
266
297
  letter-spacing: -0.02em;
@@ -269,7 +300,6 @@ const faqTabs = [
269
300
  .landing-section__lead {
270
301
  margin: 0 0 var(--m-space-5);
271
302
  max-width: 36rem;
272
- color: var(--m-color-text-muted);
273
303
  }
274
304
 
275
305
  .landing-points {
@@ -311,20 +341,14 @@ const faqTabs = [
311
341
  background: var(--m-color-surface);
312
342
  }
313
343
 
314
- .landing-cards h3 {
344
+ .landing-cards :deep(.m-typography-title--h3) {
315
345
  margin: 0;
316
346
  font-size: 1.125rem;
317
347
  }
318
348
 
319
- .landing-cards p {
320
- margin: 0;
321
- color: var(--m-color-text-muted);
322
- line-height: 1.55;
323
- }
324
-
349
+ .landing-cards :deep(.m-typography),
325
350
  .landing-faq {
326
351
  margin: 0;
327
- color: var(--m-color-text-muted);
328
352
  line-height: 1.55;
329
353
  }
330
354
 
@@ -10,7 +10,9 @@ import {
10
10
  MFormItem,
11
11
  MInput,
12
12
  MInputPassword,
13
+ MParagraph,
13
14
  MSpace,
15
+ MTitle,
14
16
  zhCN,
15
17
  } from 'morya-ui'
16
18
  import { reactive, ref } from 'vue'
@@ -23,6 +25,11 @@ const model = reactive({
23
25
  password: '',
24
26
  })
25
27
 
28
+ /** Brand seed for Account surfaces — prefer themeConfig over page-level hex. */
29
+ const themeConfig = {
30
+ seed: { colorPrimary: '#0b6e4f' },
31
+ }
32
+
26
33
  async function onSubmit() {
27
34
  formError.value = ''
28
35
  submitting.value = true
@@ -39,7 +46,11 @@ async function onSubmit() {
39
46
  </script>
40
47
 
41
48
  <template>
42
- <MConfigProvider :locale="zhCN">
49
+ <MConfigProvider
50
+ :locale="zhCN"
51
+ input-variant="filled"
52
+ :theme-config="themeConfig"
53
+ >
43
54
  <div class="login-shell">
44
55
  <aside class="login-brand" aria-label="品牌">
45
56
  <p class="login-brand__mark">青禾书房</p>
@@ -52,8 +63,12 @@ async function onSubmit() {
52
63
  <main class="login-main">
53
64
  <div class="login-panel">
54
65
  <header class="login-panel__header">
55
- <h2>登录</h2>
56
- <p>使用工作邮箱进入后台。</p>
66
+ <MTitle :level="3">
67
+ 登录
68
+ </MTitle>
69
+ <MParagraph type="secondary">
70
+ 使用工作邮箱进入后台。
71
+ </MParagraph>
57
72
  </header>
58
73
 
59
74
  <p v-if="formError" class="login-alert" role="alert">
@@ -178,15 +193,13 @@ async function onSubmit() {
178
193
  margin-bottom: var(--m-space-5);
179
194
  }
180
195
 
181
- .login-panel__header h2 {
182
- margin: 0 0 var(--m-space-2);
183
- font-size: 1.5rem;
184
- font-weight: 650;
196
+ .login-panel__header :deep(.m-typography-title),
197
+ .login-panel__header :deep(.m-typography) {
198
+ margin: 0;
185
199
  }
186
200
 
187
- .login-panel__header p {
188
- margin: 0;
189
- color: var(--m-color-text-muted);
201
+ .login-panel__header :deep(.m-typography-title) {
202
+ margin-bottom: var(--m-space-2);
190
203
  }
191
204
 
192
205
  .login-alert {
@@ -12,10 +12,25 @@ describe('page snippets catalog', () => {
12
12
  'page-header-actions',
13
13
  'list-filters-stack',
14
14
  'list-filters-dense',
15
+ 'detail-descriptions',
16
+ 'empty-block',
15
17
  ]) {
16
18
  expect(findPageSnippet(id)?.id).toBe(id);
17
19
  }
18
20
  });
21
+ it('empty-block prefers illustration', () => {
22
+ const empty = findPageSnippet('empty-block');
23
+ expect(empty.template).toContain('illustration=');
24
+ });
25
+ it('auth-split-shell uses filled inputs', () => {
26
+ const auth = findPageSnippet('auth-split-shell');
27
+ expect(auth.template).toContain('input-variant="filled"');
28
+ });
29
+ it('detail-descriptions uses MDescriptions', () => {
30
+ const detail = findPageSnippet('detail-descriptions');
31
+ expect(detail.template).toContain('MDescriptions');
32
+ expect(detail.imports).toContain('MDescriptionsItem');
33
+ });
19
34
  it('list-filters-stack includes chips, advanced, and #actions', () => {
20
35
  const stack = findPageSnippet('list-filters-stack');
21
36
  expect(stack.template).toContain('MPageFilterChips');
package/dist/decisions.js CHANGED
@@ -1104,7 +1104,7 @@ export const componentDecisions = [
1104
1104
  ],
1105
1105
  recipe: recipe([
1106
1106
  ['title + description', 'title + description'],
1107
- ['icon 或 illustration 按需(默认即可)', 'icon or illustration as needed (default OK)'],
1107
+ ['主区域优先 illustration(no-content / no-result…)', 'Prefer illustration on main regions (no-content / no-result…)'],
1108
1108
  ['表格内放在 #empty', 'Inside tables: #empty slot'],
1109
1109
  ], [['#extra 放下一步按钮(创建…)', '#extra for next-step button (Create…)']]),
1110
1110
  relatedSnippets: ['empty-block'],