@omit-design/preset-mobile 0.1.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.
Files changed (57) hide show
  1. package/PATTERNS.md +134 -0
  2. package/README.md +29 -0
  3. package/catalog.tsx +365 -0
  4. package/components/OmAppBar.tsx +63 -0
  5. package/components/OmButton.tsx +41 -0
  6. package/components/OmCard.tsx +28 -0
  7. package/components/OmCouponCard.tsx +71 -0
  8. package/components/OmDialog.tsx +96 -0
  9. package/components/OmEmptyState.tsx +32 -0
  10. package/components/OmHeader.tsx +21 -0
  11. package/components/OmInput.tsx +38 -0
  12. package/components/OmListRow.tsx +30 -0
  13. package/components/OmMenuCard.tsx +47 -0
  14. package/components/OmNumpad.tsx +82 -0
  15. package/components/OmOrderFooter.tsx +78 -0
  16. package/components/OmPage.tsx +30 -0
  17. package/components/OmProductCard.tsx +75 -0
  18. package/components/OmSearchBar.tsx +51 -0
  19. package/components/OmSelect.tsx +47 -0
  20. package/components/OmSettingRow.tsx +95 -0
  21. package/components/OmSheet.tsx +49 -0
  22. package/components/OmStatCard.tsx +30 -0
  23. package/components/OmTabBar.tsx +26 -0
  24. package/components/OmTag.tsx +28 -0
  25. package/components/index.ts +30 -0
  26. package/components/inspect-attrs.ts +34 -0
  27. package/components/om-app-bar.css +83 -0
  28. package/components/om-coupon-card.css +107 -0
  29. package/components/om-dialog.css +81 -0
  30. package/components/om-empty-state.css +55 -0
  31. package/components/om-input.css +43 -0
  32. package/components/om-menu-card.css +68 -0
  33. package/components/om-numpad.css +49 -0
  34. package/components/om-order-footer.css +121 -0
  35. package/components/om-page.css +43 -0
  36. package/components/om-product-card.css +124 -0
  37. package/components/om-search-bar.css +39 -0
  38. package/components/om-select.css +28 -0
  39. package/components/om-setting-row.css +82 -0
  40. package/components/om-sheet.css +73 -0
  41. package/components/om-stat-card.css +40 -0
  42. package/components/om-tag.css +51 -0
  43. package/index.ts +14 -0
  44. package/package.json +48 -0
  45. package/preset.manifest.ts +62 -0
  46. package/templates/dashboard.tmpl.tsx +90 -0
  47. package/templates/detail-view.tmpl.tsx +60 -0
  48. package/templates/dialog-view.tmpl.tsx +34 -0
  49. package/templates/form-view.tmpl.tsx +52 -0
  50. package/templates/list-view.tmpl.tsx +58 -0
  51. package/templates/sheet-action.tmpl.tsx +52 -0
  52. package/templates/tab-view.tmpl.tsx +51 -0
  53. package/templates/welcome-view.tmpl.tsx +38 -0
  54. package/theme/baseline.ts +32 -0
  55. package/theme/presets/light.ts +8 -0
  56. package/theme/variables.css +183 -0
  57. package/tokens/index.ts +51 -0
@@ -0,0 +1,51 @@
1
+ /* OmTag —— chip / badge / 标签 */
2
+
3
+ .om-tag {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ gap: var(--om-spacing-xs);
8
+ border-radius: var(--om-radius-sm);
9
+ font-weight: 500;
10
+ line-height: 1;
11
+ white-space: nowrap;
12
+ }
13
+
14
+ .om-tag--sm {
15
+ font-size: var(--om-font-size-xs);
16
+ padding: 2px var(--om-spacing-sm);
17
+ height: 20px;
18
+ }
19
+
20
+ .om-tag--md {
21
+ font-size: var(--om-font-size-sm);
22
+ padding: 4px var(--om-spacing-sm);
23
+ height: 24px;
24
+ }
25
+
26
+ /* 颜色通过 data-color 映射到对应 token 色。
27
+ solid:深色填充 + 反色文字;soft:浅色底 + token 色文字;outline:透明底 + 描边。 */
28
+ .om-tag--solid[data-color="primary"] { background: var(--ion-color-primary); color: var(--ion-color-primary-contrast); }
29
+ .om-tag--solid[data-color="success"] { background: var(--ion-color-success); color: var(--ion-color-success-contrast); }
30
+ .om-tag--solid[data-color="warning"] { background: var(--ion-color-warning); color: var(--ion-color-warning-contrast); }
31
+ .om-tag--solid[data-color="danger"] { background: var(--ion-color-danger); color: var(--ion-color-danger-contrast); }
32
+ .om-tag--solid[data-color="tertiary"]{ background: var(--ion-color-tertiary); color: var(--ion-color-tertiary-contrast); }
33
+ .om-tag--solid[data-color="medium"] { background: var(--ion-color-medium); color: var(--ion-color-medium-contrast); }
34
+
35
+ .om-tag--soft[data-color="primary"] { background: var(--om-surface-primary-soft); color: var(--ion-color-primary); }
36
+ .om-tag--soft[data-color="success"] { background: rgba(var(--ion-color-success-rgb), 0.12); color: var(--ion-color-success); }
37
+ .om-tag--soft[data-color="warning"] { background: rgba(var(--ion-color-warning-rgb), 0.12); color: var(--ion-color-warning); }
38
+ .om-tag--soft[data-color="danger"] { background: rgba(var(--ion-color-danger-rgb), 0.12); color: var(--ion-color-danger); }
39
+ .om-tag--soft[data-color="tertiary"] { background: rgba(var(--ion-color-tertiary-rgb), 0.12); color: var(--ion-color-tertiary); }
40
+ .om-tag--soft[data-color="medium"] { background: var(--ion-color-light); color: var(--ion-color-medium-shade); }
41
+
42
+ .om-tag--outline {
43
+ background: transparent;
44
+ border: 1px solid currentColor;
45
+ }
46
+ .om-tag--outline[data-color="primary"] { color: var(--ion-color-primary); }
47
+ .om-tag--outline[data-color="success"] { color: var(--ion-color-success); }
48
+ .om-tag--outline[data-color="warning"] { color: var(--ion-color-warning); }
49
+ .om-tag--outline[data-color="danger"] { color: var(--ion-color-danger); }
50
+ .om-tag--outline[data-color="tertiary"] { color: var(--ion-color-tertiary); }
51
+ .om-tag--outline[data-color="medium"] { color: var(--ion-color-medium-shade); }
package/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * preset-mobile barrel — 业务稿 / App 入口通过单次 `import "@omit-design/preset-mobile"`
3
+ * 就能拿到组件白名单 + 注入 token CSS + Ionic 运行时。
4
+ *
5
+ * CSS import 是故意的 side effect:preset 自包含,engine mount 时不需要关心
6
+ * "哪些 CSS 要注入"。
7
+ */
8
+
9
+ // Token 变量(side-effect)
10
+ import "./theme/variables.css";
11
+
12
+ export * from "./components";
13
+ export { presetMobileManifest } from "./preset.manifest";
14
+ export { presetMobileManifest as default } from "./preset.manifest";
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@omit-design/preset-mobile",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Mobile design preset for omit-design: Om* whitelist components, --om-* / --ion-* token map, Ionic 8 runtime.",
6
+ "main": "./index.ts",
7
+ "sideEffects": [
8
+ "*.css",
9
+ "./index.ts"
10
+ ],
11
+ "exports": {
12
+ ".": "./index.ts",
13
+ "./components": "./components/index.ts",
14
+ "./tokens": "./tokens/index.ts",
15
+ "./preset.manifest": "./preset.manifest.ts",
16
+ "./theme/variables.css": "./theme/variables.css",
17
+ "./templates/*": "./templates/*",
18
+ "./PATTERNS.md": "./PATTERNS.md"
19
+ },
20
+ "files": [
21
+ "index.ts",
22
+ "components",
23
+ "tokens",
24
+ "theme",
25
+ "templates",
26
+ "preset.manifest.ts",
27
+ "catalog.tsx",
28
+ "PATTERNS.md",
29
+ "README.md"
30
+ ],
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/leefanv/omit-design.git",
35
+ "directory": "packages/preset-mobile"
36
+ },
37
+ "homepage": "https://github.com/leefanv/omit-design/tree/main/packages/preset-mobile#readme",
38
+ "bugs": "https://github.com/leefanv/omit-design/issues",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "peerDependencies": {
43
+ "@ionic/react": "^8",
44
+ "ionicons": "^7 || ^8",
45
+ "react": "^19",
46
+ "react-router-dom": "^6"
47
+ }
48
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * preset-mobile manifest — engine 通过这个对象决定怎么对待 mobile 项目。
3
+ * 见 [../engine/src/preset/types.ts](../engine/src/preset/types.ts) 里的 PresetManifest 接口注释。
4
+ */
5
+
6
+ import type { PresetManifest } from "@omit-design/engine/preset";
7
+ import { baseline } from "./theme/baseline";
8
+
9
+ export const presetMobileManifest: PresetManifest = {
10
+ name: "mobile",
11
+ displayName: "Mobile (Ionic)",
12
+ componentPrefix: "Om",
13
+ attributeName: "data-omit-component",
14
+ tokenPrefixes: {
15
+ color: "--ion-color-",
16
+ spacing: "--om-spacing-",
17
+ radius: "--om-radius-",
18
+ font: "--om-font-size-",
19
+ shadow: "--om-shadow-",
20
+ },
21
+ canvas: {
22
+ default: { width: 390, height: 844 },
23
+ presets: [
24
+ { label: "iPhone 14", width: 390, height: 844 },
25
+ { label: "iPhone 14 Pro Max", width: 430, height: 932 },
26
+ { label: "iPhone SE", width: 375, height: 667 },
27
+ { label: "iPhone 12 mini", width: 360, height: 780 },
28
+ { label: "Pixel 7", width: 412, height: 915 },
29
+ { label: "Galaxy S20", width: 360, height: 800 },
30
+ { label: "iPad mini", width: 744, height: 1133 },
31
+ ],
32
+ chrome: "mobile",
33
+ },
34
+ requiresIonic: true,
35
+ a11ySelectors: {
36
+ clickable: [
37
+ "button",
38
+ "a",
39
+ "[role='button']",
40
+ "input",
41
+ "ion-button",
42
+ "ion-item[button]",
43
+ "[data-omit-component='OmButton']",
44
+ "[data-omit-component='OmListRow']",
45
+ ],
46
+ },
47
+ semanticColors: [
48
+ "primary",
49
+ "secondary",
50
+ "tertiary",
51
+ "success",
52
+ "warning",
53
+ "danger",
54
+ "dark",
55
+ "medium",
56
+ "light",
57
+ ],
58
+ themeBaseline: {
59
+ colors: { ...baseline.colors },
60
+ spacing: { ...baseline.spacing },
61
+ },
62
+ };
@@ -0,0 +1,90 @@
1
+ // @pattern: dashboard
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta + hero 文案
4
+ // 2. STATS / TILES 换成真实 mock import
5
+ // 3. 给 TILES 加合适 ionicons + href
6
+
7
+ export const meta = {
8
+ name: "TODO 工作台",
9
+ pattern: "dashboard",
10
+ description: "TODO 一句话描述",
11
+ source: "prd",
12
+ } as const;
13
+
14
+ import { IonBackButton } from "@ionic/react";
15
+ import {
16
+ appsOutline,
17
+ cashOutline,
18
+ receiptOutline,
19
+ } from "ionicons/icons";
20
+ import {
21
+ OmHeader,
22
+ OmMenuCard,
23
+ OmPage,
24
+ OmStatCard,
25
+ } from "@omit-design/preset-mobile";
26
+
27
+ interface Stat {
28
+ label: string;
29
+ value: string;
30
+ caption?: string;
31
+ }
32
+
33
+ interface Tile {
34
+ id: string;
35
+ icon: string;
36
+ label: string;
37
+ href?: string;
38
+ disabled?: boolean;
39
+ badge?: string;
40
+ }
41
+
42
+ // TODO: 替换为真实 mock import
43
+ const STATS: Stat[] = [
44
+ { label: "今日营收", value: "¥0.00", caption: "含已结金额" },
45
+ { label: "客单量", value: "0", caption: "单数" },
46
+ ];
47
+
48
+ const TILES: Tile[] = [
49
+ { id: "pos", icon: cashOutline, label: "POS", href: "/designs/TODO" },
50
+ { id: "orders", icon: receiptOutline, label: "订单", href: "/designs/TODO" },
51
+ ];
52
+
53
+ export function TodoDashboardPage() {
54
+ return (
55
+ <OmPage
56
+ padding="lg"
57
+ header={
58
+ <OmHeader
59
+ title="TODO 工作台"
60
+ start={<IonBackButton defaultHref="/designs/TODO-from" />}
61
+ />
62
+ }
63
+ >
64
+ <div>
65
+ {STATS.map((s) => (
66
+ <OmStatCard
67
+ key={s.label}
68
+ label={s.label}
69
+ value={s.value}
70
+ caption={s.caption}
71
+ />
72
+ ))}
73
+ </div>
74
+
75
+ <div>
76
+ {TILES.map((t) => (
77
+ <OmMenuCard
78
+ key={t.id}
79
+ icon={t.icon ?? appsOutline}
80
+ label={t.label}
81
+ href={t.href}
82
+ disabled={t.disabled}
83
+ badge={t.badge}
84
+ />
85
+ ))}
86
+ </div>
87
+ </OmPage>
88
+ );
89
+ }
90
+ export default TodoDashboardPage;
@@ -0,0 +1,60 @@
1
+ // @pattern: detail-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta + 字段
4
+ // 2. 把 RECORD 换成真实 mock import
5
+ // 3. 调整 IonBackButton 的 defaultHref
6
+ // 4. 主操作改成业务跳转
7
+
8
+ export const meta = {
9
+ name: "TODO 详情页名",
10
+ pattern: "detail-view",
11
+ description: "TODO 一句话描述",
12
+ source: "prd",
13
+ } as const;
14
+
15
+ import { IonBackButton } from "@ionic/react";
16
+ import {
17
+ OmButton,
18
+ OmCard,
19
+ OmHeader,
20
+ OmPage,
21
+ } from "@omit-design/preset-mobile";
22
+
23
+ interface Record {
24
+ id: string;
25
+ title: string;
26
+ amount: number;
27
+ status: string;
28
+ }
29
+
30
+ // TODO: 替换为真实 mock import
31
+ const RECORD: Record = {
32
+ id: "TODO",
33
+ title: "示例标题",
34
+ amount: 0,
35
+ status: "TODO 状态",
36
+ };
37
+
38
+ export function TodoDetailPage() {
39
+ return (
40
+ <OmPage
41
+ padding="lg"
42
+ header={
43
+ <OmHeader
44
+ title="TODO 详情"
45
+ start={<IonBackButton defaultHref="/designs/TODO" />}
46
+ />
47
+ }
48
+ >
49
+ <OmCard title={RECORD.title} subtitle={`#${RECORD.id}`}>
50
+ <p>金额:¥{RECORD.amount.toFixed(2)}</p>
51
+ <p>状态:{RECORD.status}</p>
52
+ </OmCard>
53
+
54
+ {/* TODO: 关联信息分块 —— 增加更多 OmCard */}
55
+
56
+ <OmButton expand="block">主操作</OmButton>
57
+ </OmPage>
58
+ );
59
+ }
60
+ export default TodoDetailPage;
@@ -0,0 +1,34 @@
1
+ // @pattern: dialog-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta
4
+ // 2. icon 用合适的 ionicons
5
+ // 3. cancelHref / confirmHref 指向真实路由
6
+ // 4. iconColor / confirmColor 按场景设(danger / warning / success / primary)
7
+
8
+ export const meta = {
9
+ name: "TODO 对话框",
10
+ pattern: "dialog-view",
11
+ description: "TODO 一句话描述",
12
+ source: "prd",
13
+ } as const;
14
+
15
+ import { informationCircleOutline } from "ionicons/icons";
16
+ import { OmDialog, OmPage } from "@omit-design/preset-mobile";
17
+
18
+ export function TodoDialogPage() {
19
+ return (
20
+ <OmPage padding="none">
21
+ <OmDialog
22
+ icon={informationCircleOutline}
23
+ iconColor="primary"
24
+ title="TODO 标题"
25
+ subtitle="TODO 副标题描述。"
26
+ cancelText="取消"
27
+ cancelHref="/designs/TODO-from"
28
+ confirmText="确认"
29
+ confirmHref="/designs/TODO-next"
30
+ />
31
+ </OmPage>
32
+ );
33
+ }
34
+ export default TodoDialogPage;
@@ -0,0 +1,52 @@
1
+ // @pattern: form-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta + 字段
4
+ // 2. 调整 useState 初始值与字段标签
5
+ // 3. 提交按钮 navigate 到下一张稿
6
+ // 4. IonBackButton defaultHref 改成上一级
7
+
8
+ export const meta = {
9
+ name: "TODO 表单页名",
10
+ pattern: "form-view",
11
+ description: "TODO 一句话描述",
12
+ source: "prd",
13
+ } as const;
14
+
15
+ import { useState } from "react";
16
+ import { useNavigate } from "react-router-dom";
17
+ import { IonBackButton } from "@ionic/react";
18
+ import {
19
+ OmButton,
20
+ OmHeader,
21
+ OmInput,
22
+ OmPage,
23
+ } from "@omit-design/preset-mobile";
24
+
25
+ export function TodoFormPage() {
26
+ const navigate = useNavigate();
27
+ const [field1, setField1] = useState("");
28
+ const [field2, setField2] = useState("");
29
+
30
+ return (
31
+ <OmPage
32
+ padding="lg"
33
+ header={
34
+ <OmHeader
35
+ title="TODO 表单"
36
+ start={<IonBackButton defaultHref="/designs/TODO" />}
37
+ />
38
+ }
39
+ >
40
+ <OmInput label="字段一" value={field1} onChange={setField1} />
41
+ <OmInput label="字段二" value={field2} onChange={setField2} />
42
+
43
+ <OmButton
44
+ expand="block"
45
+ onClick={() => navigate("/designs/TODO-next")}
46
+ >
47
+ 提交
48
+ </OmButton>
49
+ </OmPage>
50
+ );
51
+ }
52
+ export default TodoFormPage;
@@ -0,0 +1,58 @@
1
+ // @pattern: list-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta.name / description
4
+ // 2. 把 ITEMS 换成真实 mock import
5
+ // 3. 替换 IonBackButton 的 defaultHref
6
+ // 4. 调整空态文案
7
+
8
+ export const meta = {
9
+ name: "TODO 列表页名",
10
+ pattern: "list-view",
11
+ description: "TODO 一句话描述",
12
+ source: "prd",
13
+ } as const;
14
+
15
+ import { IonBackButton, IonList } from "@ionic/react";
16
+ import {
17
+ OmEmptyState,
18
+ OmHeader,
19
+ OmListRow,
20
+ OmPage,
21
+ } from "@omit-design/preset-mobile";
22
+
23
+ interface Item {
24
+ id: string;
25
+ title: string;
26
+ detail?: string;
27
+ }
28
+
29
+ // TODO: 替换为 import { items } from ""./mock/<group>" 相对路径";
30
+ const ITEMS: Item[] = [
31
+ { id: "1", title: "示例条目 1", detail: "副信息" },
32
+ { id: "2", title: "示例条目 2" },
33
+ ];
34
+
35
+ export function TodoListPage() {
36
+ return (
37
+ <OmPage
38
+ padding="none"
39
+ header={
40
+ <OmHeader
41
+ title="TODO 标题"
42
+ start={<IonBackButton defaultHref="/designs/TODO" />}
43
+ />
44
+ }
45
+ >
46
+ {ITEMS.length === 0 ? (
47
+ <OmEmptyState title="暂无数据" description="TODO 空态描述" />
48
+ ) : (
49
+ <IonList lines="none">
50
+ {ITEMS.map((it) => (
51
+ <OmListRow key={it.id} title={it.title} detail={it.detail} />
52
+ ))}
53
+ </IonList>
54
+ )}
55
+ </OmPage>
56
+ );
57
+ }
58
+ export default TodoListPage;
@@ -0,0 +1,52 @@
1
+ // @pattern: sheet-action
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta
4
+ // 2. 替换 ITEMS 为业务菜单项
5
+ // 3. dismissHref 改成来源页路径
6
+
7
+ export const meta = {
8
+ name: "TODO 操作菜单",
9
+ pattern: "sheet-action",
10
+ description: "TODO 一句话描述",
11
+ source: "prd",
12
+ } as const;
13
+
14
+ import { useNavigate } from "react-router-dom";
15
+ import { IonIcon } from "@ionic/react";
16
+ import { ellipsisHorizontalOutline } from "ionicons/icons";
17
+ import { OmPage, OmSheet } from "@omit-design/preset-mobile";
18
+
19
+ interface MenuItem {
20
+ label: string;
21
+ icon: string;
22
+ href: string;
23
+ danger?: boolean;
24
+ }
25
+
26
+ const ITEMS: MenuItem[] = [
27
+ { label: "TODO 项一", icon: ellipsisHorizontalOutline, href: "/designs/TODO" },
28
+ { label: "TODO 项二", icon: ellipsisHorizontalOutline, href: "/designs/TODO" },
29
+ ];
30
+
31
+ export function TodoSheetActionPage() {
32
+ const navigate = useNavigate();
33
+ return (
34
+ <OmPage padding="none">
35
+ <OmSheet title="TODO 标题" dismissHref="/designs/TODO-from">
36
+ <div>
37
+ {ITEMS.map((it) => (
38
+ <button
39
+ key={it.label}
40
+ type="button"
41
+ onClick={() => navigate(it.href)}
42
+ >
43
+ <IonIcon icon={it.icon} />
44
+ {it.label}
45
+ </button>
46
+ ))}
47
+ </div>
48
+ </OmSheet>
49
+ </OmPage>
50
+ );
51
+ }
52
+ export default TodoSheetActionPage;
@@ -0,0 +1,51 @@
1
+ // @pattern: tab-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta + 品牌文案
4
+ // 2. 主体内容替换为业务(列表/表单/空态)
5
+ // 3. TABS 列表通常项目级常量 —— 抽到 shell 文件复用更好
6
+ //
7
+ // 注:tab-view 通常由 MemberShell 等 shell 文件统一管理 OmTabBar。
8
+ // 本 template 展示扁平结构,首批落地后可重构为 shell。
9
+
10
+ export const meta = {
11
+ name: "TODO Tab 页",
12
+ pattern: "tab-view",
13
+ description: "TODO 一句话描述",
14
+ source: "prd",
15
+ } as const;
16
+
17
+ import {
18
+ cubeOutline,
19
+ peopleOutline,
20
+ receiptOutline,
21
+ settingsOutline,
22
+ } from "ionicons/icons";
23
+ import {
24
+ OmAppBar,
25
+ OmPage,
26
+ OmTabBar,
27
+ type OmTabItem,
28
+ } from "@omit-design/preset-mobile";
29
+
30
+ const TABS: OmTabItem[] = [
31
+ { tab: "stored", href: "/designs/TODO-stored", label: "寄存", icon: cubeOutline },
32
+ { tab: "orders", href: "/designs/TODO-orders", label: "订单", icon: receiptOutline },
33
+ { tab: "member", href: "/designs/TODO-member", label: "会员", icon: peopleOutline },
34
+ { tab: "settings", href: "/designs/TODO-settings", label: "设置", icon: settingsOutline },
35
+ ];
36
+
37
+ export function TodoTabPage() {
38
+ return (
39
+ <OmPage
40
+ padding="lg"
41
+ header={<OmAppBar variant="brand" brandTitle="TODO 品牌" />}
42
+ >
43
+ <div>
44
+ {/* TODO: 主体内容 —— 表单 / 列表 / 空态 */}
45
+ <h1>TODO Tab 页主体</h1>
46
+ </div>
47
+ <OmTabBar items={TABS} />
48
+ </OmPage>
49
+ );
50
+ }
51
+ export default TodoTabPage;
@@ -0,0 +1,38 @@
1
+ // @pattern: welcome-view
2
+ // TEMPLATE — 复制到 design/<filename>/<filename>.tsx 后:
3
+ // 1. 替换 meta
4
+ // 2. 接入真实品牌 logo 组件(可选)
5
+ // 3. CTA navigate 到下一张稿
6
+ // 4. tagline / version 走 mock 或常量
7
+
8
+ export const meta = {
9
+ name: "TODO 欢迎页",
10
+ pattern: "welcome-view",
11
+ description: "TODO 一句话描述",
12
+ source: "prd",
13
+ } as const;
14
+
15
+ import { useNavigate } from "react-router-dom";
16
+ import { OmButton, OmPage } from "@omit-design/preset-mobile";
17
+
18
+ export function TodoWelcomePage() {
19
+ const navigate = useNavigate();
20
+
21
+ return (
22
+ <OmPage padding="none">
23
+ {/* TODO: 替换为品牌 logo 组件 */}
24
+ <div>
25
+ <h1>Welcome</h1>
26
+ <p>TODO 欢迎语</p>
27
+ </div>
28
+
29
+ <OmButton
30
+ expand="block"
31
+ onClick={() => navigate("/designs/TODO-next")}
32
+ >
33
+ 开始
34
+ </OmButton>
35
+ </OmPage>
36
+ );
37
+ }
38
+ export default TodoWelcomePage;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * preset-mobile theme baseline —— theme editor 用这个值作 published 基准。
3
+ * 历史名 `lightPreset`,2026-04-25 重命名为 baseline 与 desktop 对齐;
4
+ * 老符号 `lightPreset` 仍 re-export 兼容([presets/light.ts](./presets/light.ts))。
5
+ *
6
+ * ⚠️ 改 [variables.css](./variables.css) 时同步改这里,否则 baseline 偏移。
7
+ * 改完 bump engine/theme-editor/store.ts 里的 `version`。
8
+ */
9
+ export const baseline = {
10
+ name: "Default Light",
11
+ colors: {
12
+ primary: "#0d8ce9",
13
+ secondary: "#00cfff",
14
+ tertiary: "#e56e35",
15
+ success: "#22c55e",
16
+ warning: "#e56e35",
17
+ danger: "#da342e",
18
+ dark: "#1f2024",
19
+ medium: "#90909a",
20
+ light: "#dbd9e0",
21
+ },
22
+ spacing: {
23
+ xs: "4px",
24
+ sm: "8px",
25
+ md: "12px",
26
+ lg: "16px",
27
+ xl: "24px",
28
+ "2xl": "32px",
29
+ },
30
+ } as const;
31
+
32
+ export type MobileThemeBaseline = typeof baseline;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @deprecated 用 [../baseline.ts](../baseline.ts) 的 `baseline` 代替,与 desktop 对齐命名。
3
+ * 留作向后兼容(旧 import 路径仍生效)。
4
+ */
5
+ import { baseline } from "../baseline";
6
+
7
+ export const lightPreset = baseline;
8
+ export type ThemePreset = typeof baseline;