@morya-ui/setup 0.2.5

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/bin/morya-ui-setup.js +14 -0
  4. package/package.json +35 -0
  5. package/src/cli.mjs +200 -0
  6. package/src/copy-template.mjs +62 -0
  7. package/src/fs-utils.mjs +23 -0
  8. package/src/install.mjs +63 -0
  9. package/src/mcp.mjs +50 -0
  10. package/src/package-json.mjs +30 -0
  11. package/src/styles.mjs +109 -0
  12. package/template/.agents/skills/morya-ui-pages/SKILL.md +150 -0
  13. package/template/.agents/skills/morya-ui-pages/evals/evals.json +53 -0
  14. package/template/.agents/skills/morya-ui-pages/references/component-index.md +72 -0
  15. package/template/.agents/skills/morya-ui-pages/references/design-system.md +90 -0
  16. package/template/.agents/skills/morya-ui-pages/references/feedback.md +66 -0
  17. package/template/.agents/skills/morya-ui-pages/references/optional-companions.md +29 -0
  18. package/template/.agents/skills/morya-ui-pages/references/page-layouts.md +76 -0
  19. package/template/.agents/skills/morya-ui-pages/references/review-checklist.md +49 -0
  20. package/template/.agents/skills/morya-ui-pages/references/surfaces.md +89 -0
  21. package/template/.agents/skills/morya-ui-pages/references/visual-craft.md +83 -0
  22. package/template/.cursor/rules/coding-style.mdc +41 -0
  23. package/template/.cursor/rules/component-usage.mdc +41 -0
  24. package/template/.cursor/rules/design-system.mdc +17 -0
  25. package/template/.cursor/rules/page-layout.mdc +67 -0
  26. package/template/DESIGN.md +121 -0
  27. package/template/design-tokens/tokens.css +55 -0
  28. package/template/design-tokens/tokens.json +77 -0
  29. package/template/docs/components.md +144 -0
  30. package/template/docs/feedback-message-vs-toast.md +103 -0
  31. package/template/docs/golden-pages/dashboard-page.vue +103 -0
  32. package/template/docs/golden-pages/empty-state.vue +66 -0
  33. package/template/docs/golden-pages/form-page.vue +107 -0
  34. package/template/docs/golden-pages/landing-page.vue +328 -0
  35. package/template/docs/golden-pages/list-page.vue +127 -0
  36. package/template/docs/golden-pages/login-page.vue +191 -0
  37. package/template/scripts/check-raw-colors.mjs +74 -0
  38. package/template/src/examples/DashboardPageExample.vue +103 -0
  39. package/template/src/examples/EmptyStateExample.vue +66 -0
  40. package/template/src/examples/FormPageExample.vue +107 -0
  41. package/template/src/examples/LandingPageExample.vue +328 -0
  42. package/template/src/examples/ListPageExample.vue +127 -0
  43. package/template/src/examples/LoginPageExample.vue +191 -0
@@ -0,0 +1,144 @@
1
+ # Morya UI 组件清单(AI 选型索引)
2
+
3
+ > 完整 API 以文档站 `/components` 或 MCP 为准。下表用于**场景选型**,不是 prop 手册。
4
+
5
+ ## 应用壳层
6
+
7
+ | 组件 | 用途 |
8
+ | --- | --- |
9
+ | `MConfigProvider` | 根配置:locale、主题、密度、浮层挂载、组件默认 props |
10
+ | `MLayout` / `MLayoutHeader` / `MLayoutSider` / `MLayoutContent` / `MLayoutFooter` | 后台整体布局 |
11
+ | `MSidebar` | 独立侧栏容器(非 Layout 子项场景) |
12
+ | `MBreadcrumb` | 页面路径 |
13
+ | `MToolbar` | 顶栏工具区 |
14
+
15
+ ## 表单 · 输入
16
+
17
+ | 组件 | 用途 |
18
+ | --- | --- |
19
+ | `MForm` / `MFormItem` | 表单容器、校验、提交 |
20
+ | `MInput` | 单行文本 |
21
+ | `MInputPassword` | 密码 |
22
+ | `MInputNumber` | 数字 |
23
+ | `MTextarea` | 多行文本 |
24
+ | `MSelect` | 下拉选择(单选/多选/远程/filter) |
25
+ | `MTreeSelect` | 树形选择 |
26
+ | `MCascadeSelect` | 级联选择 |
27
+ | `MDatePicker` | 日期 / 日期范围 |
28
+ | `MAutoComplete` | 自动完成 |
29
+ | `MCheckbox` / `MCheckboxGroup` | 多选 |
30
+ | `MRadio` / `MRadioGroup` | 单选组 |
31
+ | `MSwitch` | 开关 |
32
+ | `MSlider` | 滑块 |
33
+ | `MRating` | 评分 |
34
+ | `MInputTags` | 标签输入 |
35
+ | `MFileUpload` | 文件上传 |
36
+ | `MFloatLabel` | 浮动标签包装 |
37
+ | `MIconField` | 输入框前缀/后缀图标 |
38
+
39
+ ## 表单 · 布局
40
+
41
+ | 组件 | 用途 |
42
+ | --- | --- |
43
+ | `MGrid` / `MGridItem` | 响应式栅格 |
44
+ | `MFlex` | Flex 布局 |
45
+ | `MSpace` | 间距 |
46
+ | `MFluid` | 子项撑满宽度 |
47
+ | `MDivider` | 分隔线 |
48
+ | `MFieldset` | 分组fieldset |
49
+
50
+ ## 数据展示
51
+
52
+ | 组件 | 用途 |
53
+ | --- | --- |
54
+ | `MTable` | 数据表格 |
55
+ | `MTreeTable` | 树形表格 |
56
+ | `MDataView` | 卡片/列表数据视图 |
57
+ | `MTree` | 树 |
58
+ | `MPagination` | 分页 |
59
+ | `MStatus` | 行内状态(圆点 + 文案) |
60
+ | `MTag` / `MChip` / `MBadge` | 标签、徽章 |
61
+ | `MAvatar` / `MAvatarGroup` | 头像 |
62
+ | `MTimeline` | 时间线 |
63
+ | `MMeterGroup` | 多段进度条 |
64
+ | `MVirtualScroller` | 虚拟滚动长列表 |
65
+
66
+ ## 反馈
67
+
68
+ | API / 组件 | 用途 | 优先级 |
69
+ | --- | --- | --- |
70
+ | `message` | **默认**:单行操作回执(已保存 / 已删除) | ★ 首选 |
71
+ | `toast` | 仅当需要 `summary` + `detail` 或异步通知 | 次要 |
72
+ | `<MMessage>` | 表单/认证区**常驻**错误条 | 内嵌场景 |
73
+ | `MEmpty` | 无数据 / 首次使用 / 筛选无结果 | 空态 |
74
+ | `MResult` | 提交结果、403 / 404 / 500 等阻断页 | 结果页 |
75
+ | `MProgressBar` / `MProgressSpinner` | 加载进度 | |
76
+ | `MSkeleton` | 骨架屏 | |
77
+ | `MBlockUI` | 遮罩阻塞 | |
78
+
79
+ 选型细则:[`feedback-message-vs-toast.md`](./feedback-message-vs-toast.md)
80
+
81
+ ## 浮层
82
+
83
+ | 组件 | 用途 |
84
+ | --- | --- |
85
+ | `MDialog` | 模态对话框 |
86
+ | `MDrawer` | 抽屉 |
87
+ | `MConfirmDialog` / `MConfirmPopup` | 二次确认 |
88
+ | `MPopover` | 气泡卡片 |
89
+ | `MTooltip` | 文字提示 |
90
+ | `MDropdown` | **动作**下拉菜单(非表单选项) |
91
+
92
+ ## 导航 · 菜单
93
+
94
+ | 组件 | 用途 |
95
+ | --- | --- |
96
+ | `MMenu` / `MMenubar` / `MTieredMenu` / `MMegaMenu` | 菜单 |
97
+ | `MTabs` | 标签页 |
98
+ | `MStepper` | 步骤条 |
99
+ | `MCommandMenu` | 命令面板 |
100
+ | `MSplitButton` / `MSelectButton` / `MToggleButton` | 复合按钮 |
101
+
102
+ ## 展示 · 媒体
103
+
104
+ | 组件 | 用途 |
105
+ | --- | --- |
106
+ | `MCard` / `MPanel` | 卡片、面板 |
107
+ | `MAccordion` | 折叠面板 |
108
+ | `MCarousel` / `MGallery` | 轮播、图库 |
109
+ | `MIcon` | 图标(Tabler 集) |
110
+ | `MScrollbar` | 主题化滚动条;供组件内置滚动壳与业务显式选用;不要塞进用户可定制插槽 |
111
+
112
+ ## 场景速查
113
+
114
+ | 我要做… | 首选组件 |
115
+ | --- | --- |
116
+ | 用户列表 + 搜索 + 分页 | `MTable` + `MInput` + `MPagination` |
117
+ | 新建/编辑实体 | `MForm` + 字段组件 + `MDialog` 或独立路由页 |
118
+ | 删除确认 | `MConfirmDialog` |
119
+ | 筛选侧栏 | `MDrawer` + 表单控件 |
120
+ | 状态指示(行内) | `MStatus`(圆点 + 文案) |
121
+ | 状态芯片 / 可关闭标签 | `MTag`(`severity`: success/warn/danger/info) |
122
+ | 空数据 / 首次使用 | `MEmpty` |
123
+ | 提交结果 / 403 / 404 / 500 | `MResult` |
124
+ | 主/次按钮组 | `MSpace` + `MButton`(primary / secondary text) |
125
+ | 后台首页 KPI | `MGrid` + `MCard` |
126
+ | 组织架构 | `MTree` 或 `MTreeSelect` |
127
+ | 整页后台页(MCP 生成) | `MLayout fillViewport`(主滚动内置 `MScrollbar`) |
128
+ | 业务局部限高滚动 | 业务自行使用 `MScrollbar`(可选:`get_page_snippet("scrollable-panel")`) |
129
+
130
+ ## 页面标准
131
+
132
+ MCP `get_design_rules` → `standards` 汇总推荐实践(非强制)。`validate_page` 仅给参考建议。
133
+
134
+ ## 常见对照
135
+
136
+ | 错误 | 正确 |
137
+ | --- | --- |
138
+ | 用 `MDropdown` 做表单枚举 | 用 `MSelect` |
139
+ | 手写 `<table>` | 用 `MTable` |
140
+ | 手写 modal div | 用 `MDialog` |
141
+ | `showClear` 与 `clearable` 混用概念 | Select 两者等价;其他组件看文档是否支持 `clearable` |
142
+ | 页面壳手写 `overflow: auto` 替代 Layout 内置滚动 | 依赖 `MLayout` 内置滚动;业务局部滚动再显式用 `MScrollbar` |
143
+ | `MPage*` 外包 `MCard` 或 padded `div` | 直接使用 `MPage*` 组件自带间距与表面 |
144
+ | 嵌套菜单被滚动容器裁剪 | Dropdown / ContextMenu / TieredMenu 侧向子菜单已 Teleport;Menu 水平/折叠飞出层用 Popover |
@@ -0,0 +1,103 @@
1
+ # Message / Toast / MMessage 选型指南
2
+
3
+ > **AI 默认规则:操作反馈优先用 `message` API;只有需要「标题 + 详情」或异步通知时才用 `toast`。**
4
+
5
+ ## 三个不同的东西
6
+
7
+ | 名称 | 形态 | API / 组件 | 典型场景 |
8
+ | --- | --- | --- | --- |
9
+ | **Message 服务** | 顶部居中(可改 placement)单行浮层 | `message.success('已保存')` | **大多数** CRUD / 保存 / 删除回执 |
10
+ | **Toast 服务** | 四角通知,带 `summary` + 可选 `detail` | `toast.success({ summary, detail })` | 需要补充说明的通知、后台任务结果 |
11
+ | **MMessage 组件** | 页面内嵌条,不自动消失 | `<MMessage severity="error">…</MMessage>` | 表单/认证区**常驻**错误、警告 |
12
+
13
+ ## 决策树(按顺序判断)
14
+
15
+ ```
16
+ 用户操作完成,需要即时反馈?
17
+ ├─ 否 → 不需要 Message/Toast(可能用 MConfirmDialog / 字段 errorMessage)
18
+ └─ 是 → 错误需留在表单区域直到用户修正?
19
+ ├─ 是 → 字段 invalid / errorMessage;表单级用 token 告警条(见 login-page)
20
+ └─ 否 → 只有一句短文案(无独立 detail)?
21
+ ├─ 是 → message.success / info / warn / error ← 默认选这个
22
+ └─ 否 → 有 summary + detail,或异步/后台通知感 → toast.*
23
+ ```
24
+
25
+ ## 优先用 Message 的场景(占多数)
26
+
27
+ - 创建 / 更新 / 删除成功:`message.success('已保存')`
28
+ - 移入回收站、批量操作完成(单行概括):`message.info('已移入回收站')`
29
+ - 轻量警告(非表单绑定):`message.warn('请先选择记录')`
30
+ - 请求失败(单行):`message.error('保存失败,请重试')`
31
+ - 复制成功、导入触发等**一句话**反馈
32
+
33
+ ```ts
34
+ import { message } from 'morya-ui'
35
+
36
+ message.success('已创建')
37
+ message.info('已移入回收站')
38
+ message.error('操作失败')
39
+ ```
40
+
41
+ ## 应该用 Toast 的场景(较少)
42
+
43
+ - **同时**需要标题与补充说明:
44
+
45
+ ```ts
46
+ toast.success({
47
+ summary: '选课申请已提交',
48
+ detail: '请等待教务审核',
49
+ })
50
+ ```
51
+
52
+ - 后台任务 / 批量结果含统计明细:
53
+
54
+ ```ts
55
+ toast.success({
56
+ summary: '导入完成',
57
+ detail: '成功 128 条,失败 2 条',
58
+ })
59
+ ```
60
+
61
+ - 登录成功且需欢迎语 + 副文案
62
+ - 需要角落堆叠、用户可能稍后查看的多条**通知**(非即时操作回执)
63
+
64
+ ## 应该用表单区常驻错误的场景
65
+
66
+ - 登录 / 注册表单上方的**持久**错误(用户修正前不消失)
67
+ - 页面级配置错误、需要与表单同区域的警告条
68
+ - **不要**把字段校验错误只丢到 Toast/Message 浮层——优先字段 `errorMessage`
69
+
70
+ 字段级:
71
+
72
+ ```vue
73
+ <MInput v-model="email" invalid :error-message="emailError" />
74
+ ```
75
+
76
+ 表单级(见 `docs/golden-pages/login-page.vue`):用 `--m-*` 样式的 `role="alert"` 条。
77
+ 说明:`<MMessage>` 当前主要是 `message` 服务的挂载宿主,**不要**臆造 `severity` + 默认插槽的 Alert API。
78
+
79
+ ```vue
80
+ <p v-if="error" class="form-alert" role="alert">{{ error }}</p>
81
+ ```
82
+ ## 反模式(AI 禁止)
83
+
84
+ | 反模式 | 应改为 |
85
+ | --- | --- |
86
+ | `toast.add({ summary: '已保存' })` 无 detail | `message.success('已保存')` |
87
+ | 表单校验失败只弹 Toast | 字段 `errorMessage` 或 `<MMessage>` |
88
+ | 删除确认用 Message/Toast | `MConfirmDialog` |
89
+ | 所有反馈都用 Toast | 默认改 Message,仅 detail 场景保留 Toast |
90
+
91
+ ## 与 Naive / Element Plus 的对应
92
+
93
+ | Morya UI | 近似概念 |
94
+ | --- | --- |
95
+ | `message` | Naive `message` / Element `ElMessage` |
96
+ | `toast` | Naive `notification` / Element `ElNotification` |
97
+ | `<MMessage>` | 页面内 `el-alert` / 表单顶部错误条 |
98
+
99
+ ## 相关文档
100
+
101
+ - [Message 组件文档](../../src/components/Message/docs/index.md)
102
+ - [Toast 组件文档](../../src/components/Toast/docs/index.md)
103
+ - [DESIGN.md §6 浮层与反馈](../DESIGN.md#6-浮层与反馈)
@@ -0,0 +1,103 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:仪表盘页
4
+ * @see DESIGN.md §3
5
+ */
6
+ import {
7
+ MBreadcrumb,
8
+ MCard,
9
+ MConfigProvider,
10
+ MGrid,
11
+ MGridItem,
12
+ MLayout,
13
+ MLayoutContent,
14
+ MLayoutHeader,
15
+ MPageContent,
16
+ MPageHeader,
17
+ MPagePlaceholder,
18
+ MPageStat,
19
+ MTable,
20
+ MTag,
21
+ zhCN,
22
+ } from 'morya-ui'
23
+
24
+ const stats = [
25
+ { label: '总用户', value: '12,480', trend: '+8.2%', icon: 'users' },
26
+ { label: '今日活跃', value: '1,926', trend: '+3.1%', icon: 'activity' },
27
+ { label: '待处理工单', value: '47', trend: '-12%', trendSeverity: 'warn' as const, icon: 'clipboard' },
28
+ { label: '系统健康', value: '99.9%', trend: '稳定', trendSeverity: 'secondary' as const, icon: 'heart' },
29
+ ]
30
+
31
+ const recentColumns = [
32
+ { key: 'id', label: '工单号', width: 96 },
33
+ { key: 'title', label: '标题' },
34
+ { key: 'priority', label: '优先级', width: 96 },
35
+ { key: 'status', label: '状态', width: 96 },
36
+ ]
37
+
38
+ const recentRows = [
39
+ { id: 'WO-1024', title: '登录异常反馈', priority: 'high', status: 'open' },
40
+ { id: 'WO-1023', title: '导出任务超时', priority: 'medium', status: 'progress' },
41
+ { id: 'WO-1022', title: '权限配置咨询', priority: 'low', status: 'done' },
42
+ ]
43
+
44
+ function prioritySeverity(p: string) {
45
+ if (p === 'high') return 'danger'
46
+ if (p === 'medium') return 'warn'
47
+ return 'secondary'
48
+ }
49
+
50
+ function statusLabel(s: string) {
51
+ if (s === 'open') return '待处理'
52
+ if (s === 'progress') return '进行中'
53
+ return '已完成'
54
+ }
55
+ </script>
56
+
57
+ <template>
58
+ <MConfigProvider :locale="zhCN">
59
+ <MLayout fill-viewport>
60
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
61
+ <MBreadcrumb :model="[{ label: '首页' }, { label: '仪表盘' }]" />
62
+ </MLayoutHeader>
63
+
64
+ <MLayoutContent>
65
+ <MPageContent density="spacious">
66
+ <MPageHeader title="仪表盘" />
67
+
68
+ <MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
69
+ <MGridItem v-for="item in stats" :key="item.label" :span="1">
70
+ <MPageStat
71
+ :label="item.label"
72
+ :value="item.value"
73
+ :trend="item.trend"
74
+ :trend-severity="item.trendSeverity ?? 'primary'"
75
+ :icon="item.icon"
76
+ />
77
+ </MGridItem>
78
+ </MGrid>
79
+
80
+ <MGrid :cols="2" :x-gap="16" :y-gap="16">
81
+ <MGridItem :span="1">
82
+ <MCard title="趋势概览">
83
+ <MPagePlaceholder aria-label="图表占位" description="图表区域(接入 ECharts / 业务组件)" />
84
+ </MCard>
85
+ </MGridItem>
86
+ <MGridItem :span="1">
87
+ <MCard title="最近工单">
88
+ <MTable :columns="recentColumns" :rows="recentRows" size="small" :paginator="false" bordered>
89
+ <template #cell-priority="{ value }">
90
+ <MTag :value="String(value)" :severity="prioritySeverity(String(value))" />
91
+ </template>
92
+ <template #cell-status="{ value }">
93
+ <MTag :value="statusLabel(String(value))" severity="info" />
94
+ </template>
95
+ </MTable>
96
+ </MCard>
97
+ </MGridItem>
98
+ </MGrid>
99
+ </MPageContent>
100
+ </MLayoutContent>
101
+ </MLayout>
102
+ </MConfigProvider>
103
+ </template>
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:列表空状态(Flow)
4
+ * @see DESIGN.md §3 · morya-ui-pages surfaces § Flow
5
+ * 可嵌在列表页 MPageContent / MTable #empty 中;此处给出完整可运行骨架。
6
+ */
7
+ import {
8
+ MBreadcrumb,
9
+ MButton,
10
+ MConfigProvider,
11
+ MEmpty,
12
+ MLayout,
13
+ MLayoutContent,
14
+ MLayoutHeader,
15
+ MPageContent,
16
+ MPageToolbar,
17
+ zhCN,
18
+ } from 'morya-ui'
19
+ </script>
20
+
21
+ <template>
22
+ <MConfigProvider :locale="zhCN">
23
+ <MLayout fill-viewport>
24
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
25
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '课程' }]" />
26
+ </MLayoutHeader>
27
+
28
+ <MLayoutContent>
29
+ <MPageContent>
30
+ <MPageToolbar title="课程">
31
+ <template #actions>
32
+ <MButton label="新建课程" />
33
+ </template>
34
+ </MPageToolbar>
35
+
36
+ <div class="empty-state-shell">
37
+ <MEmpty
38
+ title="还没有课程"
39
+ description="创建第一门课程后,学员就能在目录里看到它。也可以稍后从模板导入。"
40
+ icon="book"
41
+ >
42
+ <template #extra>
43
+ <MButton label="创建第一门课程" />
44
+ <MButton label="从模板导入" severity="secondary" text />
45
+ </template>
46
+ </MEmpty>
47
+ </div>
48
+ </MPageContent>
49
+ </MLayoutContent>
50
+ </MLayout>
51
+ </MConfigProvider>
52
+ </template>
53
+
54
+ <style scoped>
55
+ .empty-state-shell {
56
+ border: 1px dashed color-mix(in srgb, var(--m-color-border) 80%, var(--m-color-primary));
57
+ border-radius: var(--m-radius-md);
58
+ background:
59
+ radial-gradient(
60
+ 120% 80% at 50% 0%,
61
+ color-mix(in srgb, var(--m-color-primary) 10%, transparent),
62
+ transparent 55%
63
+ ),
64
+ var(--m-color-surface);
65
+ }
66
+ </style>
@@ -0,0 +1,107 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 黄金样例:表单页
4
+ * @see DESIGN.md §3
5
+ */
6
+ import {
7
+ MBreadcrumb,
8
+ MButton,
9
+ MConfigProvider,
10
+ MDatePicker,
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
+ MTextarea,
24
+ zhCN,
25
+ } from 'morya-ui'
26
+ import { reactive, ref } from 'vue'
27
+
28
+ const submitting = ref(false)
29
+
30
+ const model = reactive({
31
+ name: '',
32
+ email: '',
33
+ role: undefined as string | undefined,
34
+ active: true,
35
+ joinedAt: null as string | null,
36
+ bio: '',
37
+ })
38
+
39
+ const roleOptions = [
40
+ { label: '管理员', value: 'admin' },
41
+ { label: '成员', value: 'member' },
42
+ ]
43
+
44
+ async function onSubmit() {
45
+ submitting.value = true
46
+ try {
47
+ // await api.save(model)
48
+ } finally {
49
+ submitting.value = false
50
+ }
51
+ }
52
+ </script>
53
+
54
+ <template>
55
+ <MConfigProvider :locale="zhCN">
56
+ <MLayout fill-viewport>
57
+ <MLayoutHeader padding="var(--m-space-4) var(--m-space-6)">
58
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '用户管理', to: '/users' }, { label: '新建用户' }]" />
59
+ </MLayoutHeader>
60
+
61
+ <MLayoutContent>
62
+ <MPageContent width="narrow">
63
+ <MPageHeader title="新建用户" description="填写基本信息并分配角色。" />
64
+
65
+ <MPageSection variant="form">
66
+ <MForm @submit="onSubmit">
67
+ <MFormItem label="姓名" name="name" required>
68
+ <MInput v-model="model.name" placeholder="请输入姓名" fluid />
69
+ </MFormItem>
70
+
71
+ <MFormItem label="邮箱" name="email" required>
72
+ <MInput v-model="model.email" type="email" placeholder="name@example.com" fluid />
73
+ </MFormItem>
74
+
75
+ <MFormItem label="角色" name="role" required>
76
+ <MSelect v-model="model.role" :options="roleOptions" placeholder="请选择角色" fluid />
77
+ </MFormItem>
78
+
79
+ <MFormItem label="入职日期" name="joinedAt">
80
+ <MDatePicker v-model="model.joinedAt" placeholder="选择日期" fluid />
81
+ </MFormItem>
82
+
83
+ <MFormItem label="启用账号" name="active">
84
+ <MSwitch v-model="model.active" />
85
+ </MFormItem>
86
+
87
+ <MFormItem label="简介" name="bio">
88
+ <MTextarea v-model="model.bio" :rows="4" placeholder="可选" fluid />
89
+ </MFormItem>
90
+
91
+ <MPageSection variant="actions">
92
+ <MSpace>
93
+ <MButton native-type="submit" severity="primary" :loading="submitting">
94
+ 保存
95
+ </MButton>
96
+ <MButton severity="secondary">
97
+ 取消
98
+ </MButton>
99
+ </MSpace>
100
+ </MPageSection>
101
+ </MForm>
102
+ </MPageSection>
103
+ </MPageContent>
104
+ </MLayoutContent>
105
+ </MLayout>
106
+ </MConfigProvider>
107
+ </template>