@morya-ui/mcp 0.2.9 → 0.3.1

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.
@@ -0,0 +1,178 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:详情页
4
+ * @see DESIGN.md · morya-ui-pages references/page-layouts.md · visual-craft § Ops polish
5
+ */
6
+ import {
7
+ MBreadcrumb,
8
+ MButton,
9
+ MCard,
10
+ MConfigProvider,
11
+ MDivider,
12
+ MLayout,
13
+ MLayoutContent,
14
+ MLayoutHeader,
15
+ MPageContent,
16
+ MPageHeader,
17
+ MPageSection,
18
+ MSpace,
19
+ MStatus,
20
+ MTable,
21
+ MTag,
22
+ zhCN,
23
+ } from 'morya-ui'
24
+
25
+ const profile = {
26
+ name: '林晓',
27
+ email: 'linxiao@example.com',
28
+ role: '成员',
29
+ dept: '产品设计',
30
+ joinedAt: '2025-03-12',
31
+ status: 'active' as const,
32
+ }
33
+
34
+ const activityColumns = [
35
+ { key: 'time', label: '时间', width: 160 },
36
+ { key: 'action', label: '动作' },
37
+ { key: 'channel', label: '来源', width: 100 },
38
+ ]
39
+
40
+ const activityRows = [
41
+ { id: '1', time: '2026-09-20 14:22', action: '更新了个人资料', channel: 'Web' },
42
+ { id: '2', time: '2026-09-18 09:05', action: '重置了登录密码', channel: 'Web' },
43
+ { id: '3', time: '2026-09-12 18:41', action: '加入「产品设计」部门', channel: 'Admin' },
44
+ ]
45
+ </script>
46
+
47
+ <template>
48
+ <MConfigProvider :locale="zhCN">
49
+ <MLayout fill-viewport>
50
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
51
+ <MBreadcrumb
52
+ :model="[
53
+ { label: '首页', to: '/' },
54
+ { label: '用户管理', to: '/users' },
55
+ { label: profile.name },
56
+ ]"
57
+ />
58
+ </MLayoutHeader>
59
+
60
+ <MLayoutContent>
61
+ <MPageContent>
62
+ <MPageHeader
63
+ :title="profile.name"
64
+ description="查看账号摘要、属性与近期活动。编辑走同页弹窗或独立表单页,勿做成营销落地。"
65
+ >
66
+ <template #actions>
67
+ <MSpace>
68
+ <MStatus
69
+ :label="profile.status === 'active' ? '启用' : '停用'"
70
+ :severity="profile.status === 'active' ? 'success' : 'secondary'"
71
+ />
72
+ <MButton severity="primary">
73
+ 编辑
74
+ </MButton>
75
+ <MButton severity="secondary">
76
+ 返回
77
+ </MButton>
78
+ <MButton severity="danger" text>
79
+ 删除
80
+ </MButton>
81
+ </MSpace>
82
+ </template>
83
+ </MPageHeader>
84
+
85
+ <MPageSection title="摘要">
86
+ <MSpace wrap style="gap: var(--m-space-2)">
87
+ <MTag :value="profile.role" />
88
+ <MTag :value="profile.dept" severity="info" />
89
+ </MSpace>
90
+ <p class="detail-lead">
91
+ 工作邮箱 {{ profile.email }} · 入职 {{ profile.joinedAt }}
92
+ </p>
93
+ </MPageSection>
94
+
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>
128
+
129
+ <MDivider />
130
+
131
+ <MCard title="近期活动">
132
+ <MTable
133
+ :columns="activityColumns"
134
+ :rows="activityRows"
135
+ row-key="id"
136
+ striped
137
+ bordered
138
+ aria-label="近期活动"
139
+ />
140
+ </MCard>
141
+ </MPageContent>
142
+ </MLayoutContent>
143
+ </MLayout>
144
+ </MConfigProvider>
145
+ </template>
146
+
147
+ <style scoped>
148
+ .detail-lead {
149
+ margin: var(--m-space-3) 0 0;
150
+ color: var(--m-color-text-muted);
151
+ font-size: var(--m-font-size-sm);
152
+ }
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
+ </style>
@@ -0,0 +1,166 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:列表内短表单弹窗(form-in-dialog)
4
+ * 留在列表页,用 MDialog 承载 ≤8 字段的新建/编辑;长表单仍走 form-page / Drawer。
5
+ * @see DESIGN.md · page-layouts.md · pattern form-in-dialog
6
+ */
7
+ import {
8
+ MBreadcrumb,
9
+ MButton,
10
+ MConfigProvider,
11
+ MDialog,
12
+ MForm,
13
+ MFormItem,
14
+ MInput,
15
+ MLayout,
16
+ MLayoutContent,
17
+ MLayoutHeader,
18
+ MPageContent,
19
+ MPageToolbar,
20
+ MSelect,
21
+ MSpace,
22
+ MStatus,
23
+ MTable,
24
+ message,
25
+ zhCN,
26
+ } from 'morya-ui'
27
+ import { reactive, ref } from 'vue'
28
+
29
+ const dialogOpen = ref(false)
30
+ const submitting = ref(false)
31
+ const editingId = ref<string | null>(null)
32
+
33
+ const model = reactive({
34
+ name: '',
35
+ email: '',
36
+ role: undefined as string | undefined,
37
+ })
38
+
39
+ const roleOptions = [
40
+ { label: '管理员', value: 'admin' },
41
+ { label: '成员', value: 'member' },
42
+ ]
43
+
44
+ const columns = [
45
+ { key: 'name', label: '姓名' },
46
+ { key: 'email', label: '邮箱' },
47
+ { key: 'status', label: '状态', width: 100 },
48
+ { key: 'actions', label: '操作', width: 120 },
49
+ ]
50
+
51
+ const rows = ref([
52
+ { id: '1', name: '林晓', email: 'linxiao@example.com', status: 'active', role: 'member' },
53
+ { id: '2', name: '周然', email: 'zhouran@example.com', status: 'inactive', role: 'admin' },
54
+ ])
55
+
56
+ const dialogTitle = () => (editingId.value ? '编辑用户' : '新建用户')
57
+
58
+ function resetModel() {
59
+ model.name = ''
60
+ model.email = ''
61
+ model.role = undefined
62
+ editingId.value = null
63
+ }
64
+
65
+ function openCreate() {
66
+ resetModel()
67
+ dialogOpen.value = true
68
+ }
69
+
70
+ function openEdit(row: (typeof rows.value)[number]) {
71
+ editingId.value = row.id
72
+ model.name = row.name
73
+ model.email = row.email
74
+ model.role = row.role
75
+ dialogOpen.value = true
76
+ }
77
+
78
+ function closeDialog() {
79
+ dialogOpen.value = false
80
+ resetModel()
81
+ }
82
+
83
+ async function onSave() {
84
+ submitting.value = true
85
+ try {
86
+ // await api.save(model)
87
+ message.success(editingId.value ? '已保存' : '已创建')
88
+ closeDialog()
89
+ } finally {
90
+ submitting.value = false
91
+ }
92
+ }
93
+ </script>
94
+
95
+ <template>
96
+ <MConfigProvider :locale="zhCN">
97
+ <MLayout fill-viewport>
98
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
99
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '用户管理' }]" />
100
+ </MLayoutHeader>
101
+
102
+ <MLayoutContent>
103
+ <MPageContent>
104
+ <MPageToolbar title="用户管理">
105
+ <template #actions>
106
+ <MButton severity="primary" @click="openCreate">
107
+ 新建用户
108
+ </MButton>
109
+ </template>
110
+ </MPageToolbar>
111
+
112
+ <MTable
113
+ :columns="columns"
114
+ :rows="rows"
115
+ row-key="id"
116
+ striped
117
+ bordered
118
+ aria-label="用户列表"
119
+ >
120
+ <template #cell-status="{ value }">
121
+ <MStatus
122
+ :label="value === 'active' ? '启用' : '停用'"
123
+ :severity="value === 'active' ? 'success' : 'secondary'"
124
+ />
125
+ </template>
126
+ <template #cell-actions="{ row }">
127
+ <MButton severity="secondary" size="small" text @click="openEdit(row)">
128
+ 编辑
129
+ </MButton>
130
+ </template>
131
+ </MTable>
132
+ </MPageContent>
133
+ </MLayoutContent>
134
+ </MLayout>
135
+
136
+ <MDialog
137
+ v-model="dialogOpen"
138
+ :header="dialogTitle()"
139
+ width="32rem"
140
+ @close="resetModel"
141
+ >
142
+ <MForm @submit.prevent="onSave">
143
+ <MFormItem label="姓名" name="name" required>
144
+ <MInput v-model="model.name" placeholder="请输入姓名" fluid />
145
+ </MFormItem>
146
+ <MFormItem label="邮箱" name="email" required>
147
+ <MInput v-model="model.email" type="email" placeholder="name@example.com" fluid />
148
+ </MFormItem>
149
+ <MFormItem label="角色" name="role" required>
150
+ <MSelect v-model="model.role" :options="roleOptions" placeholder="请选择角色" fluid />
151
+ </MFormItem>
152
+ </MForm>
153
+
154
+ <template #footer>
155
+ <MSpace style="justify-content: flex-end; width: 100%">
156
+ <MButton severity="secondary" text :disabled="submitting" @click="closeDialog">
157
+ 取消
158
+ </MButton>
159
+ <MButton severity="primary" :loading="submitting" @click="onSave">
160
+ 保存
161
+ </MButton>
162
+ </MSpace>
163
+ </template>
164
+ </MDialog>
165
+ </MConfigProvider>
166
+ </template>
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:结果 / 阻断页(System)
4
+ * 默认展示 403;换成 success / 404 / 500 时只改 status + 文案 + #footer 动作。
5
+ * 无数据空态请用 empty-state / MEmpty,不要用 MResult。
6
+ * @see DESIGN.md · surfaces § System · MResult API(操作用 #footer)
7
+ */
8
+ import {
9
+ MBreadcrumb,
10
+ MButton,
11
+ MConfigProvider,
12
+ MLayout,
13
+ MLayoutContent,
14
+ MLayoutHeader,
15
+ MPageContent,
16
+ MResult,
17
+ MSpace,
18
+ zhCN,
19
+ } from 'morya-ui'
20
+ </script>
21
+
22
+ <template>
23
+ <MConfigProvider :locale="zhCN">
24
+ <MLayout fill-viewport>
25
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
26
+ <MBreadcrumb
27
+ :model="[
28
+ { label: '首页', to: '/' },
29
+ { label: '项目管理', to: '/projects' },
30
+ { label: '无权限' },
31
+ ]"
32
+ />
33
+ </MLayoutHeader>
34
+
35
+ <MLayoutContent>
36
+ <MPageContent>
37
+ <div class="result-shell">
38
+ <MResult
39
+ status="403"
40
+ title="没有权限查看该项目"
41
+ description="当前账号无权访问「青禾书房」工作区。可返回上一页,或联系管理员开通权限。"
42
+ size="large"
43
+ >
44
+ <template #footer>
45
+ <MSpace>
46
+ <MButton severity="primary">
47
+ 返回上一页
48
+ </MButton>
49
+ <MButton severity="secondary" text>
50
+ 联系管理员
51
+ </MButton>
52
+ </MSpace>
53
+ </template>
54
+ </MResult>
55
+ </div>
56
+ </MPageContent>
57
+ </MLayoutContent>
58
+ </MLayout>
59
+ </MConfigProvider>
60
+ </template>
61
+
62
+ <style scoped>
63
+ .result-shell {
64
+ display: grid;
65
+ place-items: center;
66
+ min-height: min(28rem, 70vh);
67
+ padding: var(--m-space-8) var(--m-space-4);
68
+ border-radius: var(--m-radius-lg);
69
+ background:
70
+ radial-gradient(
71
+ 70% 55% at 50% 0%,
72
+ color-mix(in srgb, var(--m-color-primary) 10%, transparent),
73
+ transparent 60%
74
+ ),
75
+ var(--m-color-surface);
76
+ border: 1px dashed color-mix(in srgb, var(--m-color-border) 80%, transparent);
77
+ }
78
+
79
+ @media (prefers-reduced-motion: reduce) {
80
+ .result-shell {
81
+ background: var(--m-color-surface);
82
+ }
83
+ }
84
+ </style>
@@ -0,0 +1,163 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:设置 / 配置页
4
+ * 窄栏 + Tabs 分组 + MPageSection form;保存用 message 单行回执。
5
+ * @see DESIGN.md · page-layouts.md § Settings · visual-craft § Ops polish
6
+ */
7
+ import {
8
+ MBreadcrumb,
9
+ MButton,
10
+ MConfigProvider,
11
+ MForm,
12
+ MFormItem,
13
+ MInput,
14
+ MLayout,
15
+ MLayoutContent,
16
+ MLayoutHeader,
17
+ MPageContent,
18
+ MPageHeader,
19
+ MPageSection,
20
+ MSelect,
21
+ MSpace,
22
+ MSwitch,
23
+ MTabs,
24
+ message,
25
+ zhCN,
26
+ } from 'morya-ui'
27
+ import { reactive, ref } from 'vue'
28
+
29
+ const activeTab = ref('profile')
30
+ const submitting = ref(false)
31
+
32
+ const tabs = [
33
+ { label: '资料', value: 'profile' },
34
+ { label: '通知', value: 'notify' },
35
+ { label: '安全', value: 'security' },
36
+ ]
37
+
38
+ const profile = reactive({
39
+ displayName: '林晓',
40
+ email: 'linxiao@example.com',
41
+ language: 'zh-CN' as string | undefined,
42
+ })
43
+
44
+ const notify = reactive({
45
+ emailDigest: true,
46
+ productUpdates: false,
47
+ })
48
+
49
+ const languageOptions = [
50
+ { label: '简体中文', value: 'zh-CN' },
51
+ { label: 'English', value: 'en-US' },
52
+ ]
53
+
54
+ async function saveProfile() {
55
+ submitting.value = true
56
+ try {
57
+ // await api.saveProfile(profile)
58
+ message.success('资料已保存')
59
+ } finally {
60
+ submitting.value = false
61
+ }
62
+ }
63
+
64
+ async function saveNotify() {
65
+ submitting.value = true
66
+ try {
67
+ // await api.saveNotify(notify)
68
+ message.success('通知偏好已保存')
69
+ } finally {
70
+ submitting.value = false
71
+ }
72
+ }
73
+ </script>
74
+
75
+ <template>
76
+ <MConfigProvider :locale="zhCN">
77
+ <MLayout fill-viewport>
78
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
79
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '系统设置' }]" />
80
+ </MLayoutHeader>
81
+
82
+ <MLayoutContent>
83
+ <MPageContent width="narrow">
84
+ <MPageHeader
85
+ title="系统设置"
86
+ description="按域分组配置;切换 Tab 时保留未保存输入,保存成功用 message 一句话回执。"
87
+ />
88
+
89
+ <MTabs v-model="activeTab" :tabs="tabs">
90
+ <template #default="{ activeValue }">
91
+ <MPageSection v-if="activeValue === 'profile'" variant="form" title="个人资料">
92
+ <MForm @submit.prevent="saveProfile">
93
+ <MFormItem label="显示名称" name="displayName" required>
94
+ <MInput v-model="profile.displayName" fluid />
95
+ </MFormItem>
96
+ <MFormItem label="工作邮箱" name="email" required>
97
+ <MInput v-model="profile.email" type="email" fluid />
98
+ </MFormItem>
99
+ <MFormItem label="界面语言" name="language">
100
+ <MSelect
101
+ v-model="profile.language"
102
+ :options="languageOptions"
103
+ placeholder="选择语言"
104
+ fluid
105
+ />
106
+ </MFormItem>
107
+ <MPageSection variant="actions">
108
+ <MSpace>
109
+ <MButton native-type="submit" severity="primary" :loading="submitting">
110
+ 保存资料
111
+ </MButton>
112
+ <MButton severity="secondary" text native-type="button">
113
+ 恢复默认
114
+ </MButton>
115
+ </MSpace>
116
+ </MPageSection>
117
+ </MForm>
118
+ </MPageSection>
119
+
120
+ <MPageSection v-else-if="activeValue === 'notify'" variant="form" title="通知">
121
+ <MForm @submit.prevent="saveNotify">
122
+ <MFormItem label="邮件摘要" name="emailDigest">
123
+ <MSwitch v-model="notify.emailDigest" />
124
+ </MFormItem>
125
+ <MFormItem label="产品动态" name="productUpdates">
126
+ <MSwitch v-model="notify.productUpdates" />
127
+ </MFormItem>
128
+ <MPageSection variant="actions">
129
+ <MButton native-type="submit" severity="primary" :loading="submitting">
130
+ 保存通知
131
+ </MButton>
132
+ </MPageSection>
133
+ </MForm>
134
+ </MPageSection>
135
+
136
+ <MPageSection v-else variant="form" title="安全">
137
+ <p class="settings-hint">
138
+ 修改密码、两步验证等敏感操作放在此区;危险动作使用 severity="danger" 并配合确认。
139
+ </p>
140
+ <MSpace>
141
+ <MButton severity="secondary">
142
+ 修改密码
143
+ </MButton>
144
+ <MButton severity="danger" text>
145
+ 注销账号
146
+ </MButton>
147
+ </MSpace>
148
+ </MPageSection>
149
+ </template>
150
+ </MTabs>
151
+ </MPageContent>
152
+ </MLayoutContent>
153
+ </MLayout>
154
+ </MConfigProvider>
155
+ </template>
156
+
157
+ <style scoped>
158
+ .settings-hint {
159
+ margin: 0 0 var(--m-space-4);
160
+ color: var(--m-color-text-muted);
161
+ font-size: var(--m-font-size-sm);
162
+ }
163
+ </style>