create-pactor-app 1.21.0 → 1.22.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,69 @@
1
+ # 登录页 DSL:内联在 sitemap 中下发(public 页,匿名可见)。
2
+ # 登录 / 登出是普通 HTTP 调用,用内置 request 动作表达;
3
+ # 登录成功整页重载,sitemap 端点按已登录会话重新裁剪内容。
4
+ dsl: "1.0"
5
+ page:
6
+ id: login
7
+ title: 登录
8
+
9
+ state:
10
+ submitting: false
11
+
12
+ body:
13
+ type: Form
14
+ events:
15
+ onSubmit:
16
+ - action: setState
17
+ params: { submitting: true }
18
+ - id: loginResult
19
+ action: request
20
+ params:
21
+ method: POST
22
+ url: http://localhost:9011/api/auth/login
23
+ body:
24
+ username: ${event.username}
25
+ password: ${event.password}
26
+ - action: setState
27
+ params: { submitting: false }
28
+ - action: if
29
+ params:
30
+ condition: ${actions.loginResult.token}
31
+ then:
32
+ - action: app.reload
33
+ params: { to: / }
34
+ else:
35
+ - action: message
36
+ params:
37
+ messageType: error
38
+ text: 登录失败:用户名或密码错误
39
+ children:
40
+ - type: Alert
41
+ props:
42
+ variant: info
43
+ message: 演示账号 admin / pactor123,guest / guest123
44
+ description: admin 可见「报表」菜单(report:view);guest 仅可见首页。登录后 sitemap 按角色重新下发。
45
+ style: { marginBottom: 16 }
46
+ - type: Input
47
+ props:
48
+ name: username
49
+ label: 用户名
50
+ placeholder: 请输入用户名
51
+ rules:
52
+ - { required: true, message: 请输入用户名 }
53
+ - type: Input
54
+ props:
55
+ name: password
56
+ label: 密码
57
+ type: password
58
+ placeholder: 请输入密码
59
+ rules:
60
+ - { required: true, message: 请输入密码 }
61
+ - type: Flex
62
+ props: { justify: end }
63
+ children:
64
+ - type: Button
65
+ props:
66
+ text: 登 录
67
+ variant: primary
68
+ submit: true
69
+ loading: ${state.submitting}
@@ -0,0 +1,23 @@
1
+ # 月报详情页 DSL:内联在 sitemap 中下发(需 report:view)。
2
+ # 隐藏页(不出现在菜单),经列表页跳转进入;${route.params.id} 演示路径参数。
3
+ dsl: "1.0"
4
+ page:
5
+ id: monthly-detail
6
+ title: 月报详情
7
+
8
+ body:
9
+ type: Page
10
+ props: { title: 月报详情 }
11
+ children:
12
+ - type: Alert
13
+ props:
14
+ variant: info
15
+ message: 路径参数演示:id = ${route.params.id}
16
+ description: 隐藏页(不出现在菜单),经列表页跳转进入。
17
+ style: { marginBottom: 16 }
18
+ - type: Button
19
+ props: { text: 返回月报 }
20
+ events:
21
+ onClick:
22
+ - action: navigate
23
+ params: { to: /reports/monthly }
@@ -0,0 +1,41 @@
1
+ # 月报页 DSL:懒加载页——sitemap 中仅声明 id / path / version(monthly@7),
2
+ # 本文件由 /api/pages/monthly 按需下发(见 src/server/sitemap.ts 的 lazyPages)。
3
+ dsl: "1.0"
4
+ page:
5
+ id: monthly
6
+ title: 月报
7
+
8
+ body:
9
+ type: Page
10
+ props: { title: 本月经营月报 }
11
+ children:
12
+ - type: Flex
13
+ props: { gap: 16, style: { marginBottom: 16 } }
14
+ children:
15
+ - type: Card
16
+ props: { title: 营收 }
17
+ children:
18
+ - type: Statistic
19
+ props: { title: 本月(万元), value: 128.6 }
20
+ - type: Card
21
+ props: { title: 新增客户 }
22
+ children:
23
+ - type: Statistic
24
+ props: { title: 本月(家), value: 342 }
25
+ - type: Card
26
+ props: { title: 转化率 }
27
+ children:
28
+ - type: Statistic
29
+ props: { title: 本月(%), value: 23.8 }
30
+ - type: Alert
31
+ props:
32
+ variant: success
33
+ message: 本页 DSL 由 /api/pages/monthly 懒加载下发
34
+ description: sitemap 中该页仅声明 id / path / version(monthly@7),点击进入时才请求页面 DSL。
35
+ style: { marginBottom: 16 }
36
+ - type: Button
37
+ props: { text: 查看详情(隐藏页 + 路径参数) }
38
+ events:
39
+ onClick:
40
+ - action: navigate
41
+ params: { to: /reports/monthly/2026-09 }
@@ -0,0 +1,23 @@
1
+ # 注册页 DSL:内联在 sitemap 中下发(public 页,匿名可见)。
2
+ # 演示环境不开放注册,仅作多 public 页示例。
3
+ dsl: "1.0"
4
+ page:
5
+ id: register
6
+ title: 注册
7
+
8
+ body:
9
+ type: Page
10
+ props: { title: 注册 }
11
+ children:
12
+ - type: Alert
13
+ props:
14
+ variant: warning
15
+ message: 演示环境不开放注册
16
+ description: 请使用登录页的演示账号体验不同角色的菜单差异。
17
+ style: { marginBottom: 16 }
18
+ - type: Button
19
+ props: { text: 返回登录, variant: primary }
20
+ events:
21
+ onClick:
22
+ - action: navigate
23
+ params: { to: /login }
@@ -0,0 +1,85 @@
1
+ # 用户列表页 DSL:由 /api/pages/users 懒加载下发(见 src/server/sitemap.ts)。
2
+ # 数据来自 /api/users(接口侧鉴权,需登录且具备 user:view 权限)。
3
+ dsl: "1.0"
4
+ page:
5
+ id: users
6
+ title: 用户列表
7
+ permission: user:view
8
+
9
+ state:
10
+ keyword: ""
11
+ role: ""
12
+
13
+ dataSources:
14
+ users:
15
+ type: http
16
+ autoLoad: true
17
+ request:
18
+ method: GET
19
+ url: http://localhost:9011/api/users
20
+ params:
21
+ keyword: ${state.keyword}
22
+ role: ${state.role}
23
+
24
+ body:
25
+ type: Page
26
+ props: { title: 用户列表 }
27
+ children:
28
+ - type: Card
29
+ props: { title: 筛选 }
30
+ children:
31
+ - type: Form
32
+ props: { layout: inline }
33
+ events:
34
+ onSubmit:
35
+ - action: setState
36
+ params:
37
+ keyword: ${event.keyword}
38
+ role: ${event.role}
39
+ - action: reload
40
+ params: { dataSource: users }
41
+ children:
42
+ - type: Input
43
+ props: { name: keyword, label: 关键词, placeholder: 姓名 / 用户名 }
44
+ - type: Select
45
+ props:
46
+ name: role
47
+ label: 角色
48
+ placeholder: 请选择角色
49
+ options:
50
+ - { label: 管理员, value: admin }
51
+ - { label: 成员, value: member }
52
+ - { label: 访客, value: guest }
53
+ - type: Button
54
+ props: { text: 查询, variant: primary, submit: true }
55
+ - type: Card
56
+ props: { title: 用户数据 }
57
+ children:
58
+ - type: Table
59
+ props:
60
+ dataSource: ${data.users}
61
+ loading: ${dataStatus.users.loading}
62
+ rowKey: id
63
+ pagination: { pageSize: 5 }
64
+ columns:
65
+ - { title: 姓名, dataIndex: name }
66
+ - { title: 用户名, dataIndex: username }
67
+ - title: 角色
68
+ dataIndex: role
69
+ cell:
70
+ type: Badge
71
+ props:
72
+ text: ${row.role}
73
+ variant: secondary
74
+ - { title: 邮箱, dataIndex: email }
75
+ - { title: 创建时间, dataIndex: createdAt }
76
+ - title: 操作
77
+ cell:
78
+ type: Button
79
+ props: { text: 查看, variant: primary, size: small }
80
+ events:
81
+ onClick:
82
+ - action: message
83
+ params:
84
+ messageType: info
85
+ text: 用户 ${row.name}(${row.username})
@@ -0,0 +1,148 @@
1
+ # Sitemap 入口数据(design/dynamic-page.md 方案 B):元数据 + 布局 + 页面 + 菜单全量。
2
+ # 本文件是会话无关的超集;可见性由 src/server/sitemap.ts 按会话裁剪:
3
+ # public: true → 匿名可见(登录/注册页)
4
+ # permission: x → 需登录且具备对应权限
5
+ # 无标记 → 需登录
6
+ # 页面 DSL 按 id 约定从 pages/<id>.page.yaml 加载;lazy: true 不随 sitemap
7
+ # 内联下发,客户端经 /api/pages/{id} 懒加载。updatedAt / user / permissions
8
+ # 为会话动态字段,由服务端在响应时注入。
9
+
10
+ id: site
11
+ resourceType: sitemap
12
+ version: 13
13
+ dslVersion: "1.0"
14
+ app: { name: 动态页面 Starter }
15
+ layout: admin
16
+ auth: { loginPath: /login }
17
+
18
+ pages:
19
+ - { id: login, path: /login, title: 登录, version: 1, public: true }
20
+ - { id: register, path: /register, title: 注册, version: 1, public: true }
21
+ - { id: home, path: /, title: 首页, version: 3 }
22
+ - { id: users, path: /users, title: 用户列表, version: 1, permission: user:view, lazy: true }
23
+ - { id: monthly, path: /reports/monthly, title: 月报, version: 7, permission: report:view, lazy: true }
24
+ - { id: monthly-detail, path: /reports/monthly/:id, title: 月报详情, version: 2, permission: report:view }
25
+
26
+ menus:
27
+ - { id: login, type: page, pageId: login, title: 登录, layout: login, public: true, hidden: true }
28
+ - { id: register, type: page, pageId: register, title: 注册, layout: login, public: true, hidden: true }
29
+ - { id: home, type: page, pageId: home, title: 首页, icon: home, layout: admin, order: 1 }
30
+ - { id: users, type: page, pageId: users, title: 用户列表, icon: users, layout: admin, permission: user:view, order: 2 }
31
+ - id: reports
32
+ type: group
33
+ title: 报表
34
+ icon: chart
35
+ order: 3
36
+ children:
37
+ - { id: monthly, type: page, pageId: monthly, title: 报表中心, icon: chart, layout: admin, permission: report:view }
38
+ - { id: monthly-detail, type: page, pageId: monthly-detail, title: 月报详情, layout: admin, hidden: true, activeMenu: monthly, permission: report:view }
39
+ - { id: help, type: link, title: 帮助, path: https://github.com/, external: true, permission: report:view, order: 4 }
40
+
41
+ # 布局 DSL(PageOutlet 为页面内容出口)。
42
+ # 布局 body 内 ${data.session.*} 由客户端注入的 session dataSource 提供
43
+ #(user / app / menus);${route.params.pathname} 为当前路径(客户端注入)。
44
+ # 登录 / 登出是普通 HTTP 调用,DSL 用内置 request 动作表达(跨域绝对地址,
45
+ # 客户端 httpHooks 自动改写进项目内代理前缀),无需客户端注册服务。
46
+ layouts:
47
+ # 管理端外壳:左侧 SitemapMenu 导航 + 顶栏用户菜单 + PageOutlet
48
+ admin:
49
+ dsl: "1.0"
50
+ body:
51
+ type: Flex
52
+ props: { gap: 0, direction: horizontal, style: { height: 100vh } }
53
+ children:
54
+ - type: Flex
55
+ props:
56
+ direction: vertical
57
+ style:
58
+ width: 224
59
+ flexShrink: 0
60
+ borderRight: 1px solid var(--border)
61
+ children:
62
+ - type: Flex
63
+ props:
64
+ style:
65
+ padding: 16px 16px
66
+ fontWeight: 600
67
+ borderBottom: 1px solid var(--border)
68
+ children:
69
+ - type: Text
70
+ props: { text: "${data.session.app.name}" }
71
+ - type: SitemapMenu
72
+ props:
73
+ items: ${data.session.menus}
74
+ collapsible: true
75
+ events:
76
+ onSelect:
77
+ - action: navigate
78
+ params: { to: "${event}" }
79
+ - type: Flex
80
+ props:
81
+ direction: vertical
82
+ style: { flex: 1, minWidth: 0 }
83
+ children:
84
+ - type: Flex
85
+ props:
86
+ justify: end
87
+ align: center
88
+ style:
89
+ padding: 10px 20px
90
+ borderBottom: 1px solid var(--border)
91
+ children:
92
+ - type: DropdownMenu
93
+ props:
94
+ items:
95
+ - { label: 个人设置, value: settings, icon: settings }
96
+ - { label: 退出登录, value: logout, icon: log-out }
97
+ events:
98
+ onSelect:
99
+ - action: if
100
+ params:
101
+ condition: ${event == 'logout'}
102
+ then:
103
+ - action: request
104
+ params:
105
+ method: POST
106
+ url: http://localhost:9011/api/auth/logout
107
+ - action: app.reload
108
+ params: { to: /login }
109
+ else:
110
+ - action: message
111
+ params: { text: 演示环境未实现个人设置 }
112
+ children:
113
+ - type: Button
114
+ props:
115
+ text: ${data.session.user.name}
116
+ variant: text
117
+ - type: Flex
118
+ props:
119
+ direction: vertical
120
+ style: { flex: 1, minHeight: 0, overflow: auto }
121
+ children:
122
+ - type: PageOutlet
123
+ # 登录 / 注册页外壳:居中卡片
124
+ login:
125
+ dsl: "1.0"
126
+ body:
127
+ type: Flex
128
+ props:
129
+ direction: vertical
130
+ justify: center
131
+ align: center
132
+ style: { minHeight: 100vh, padding: 24 }
133
+ children:
134
+ - type: Flex
135
+ props:
136
+ direction: vertical
137
+ style:
138
+ width: 380
139
+ padding: 24
140
+ border: 1px solid var(--border)
141
+ borderRadius: 12
142
+ children:
143
+ - type: PageOutlet
144
+ # 裸布局:无外壳,页面铺满
145
+ blank:
146
+ dsl: "1.0"
147
+ body:
148
+ type: PageOutlet
@@ -10,7 +10,6 @@ popstate 前进后退)接管全部路由:
10
10
  <SitemapApp
11
11
  sources={sitemapSources} // sitemap 接口(相对路径)
12
12
  baseUrl={apiBaseUrl} // 外部接口基准地址(相对路径补全)
13
- pageEndpoint={pageEndpoint} // 懒加载页面端点
14
13
  httpAdapter={httpHooks} // 框架 createHttpHooks:跨域改写 + 响应拆包
15
14
  />
16
15
  ```
@@ -38,7 +37,7 @@ src/
38
37
  main.tsx # React 入口(导入 @pactor-app/ui/styles.css,必需)
39
38
  pactor/
40
39
  App.tsx # SitemapApp 装配(纯前端模式:零路由配置;HTTP 钩子用框架 createHttpHooks)
41
- config.ts # sitemapSources / apiBaseUrl / pageEndpoint
40
+ config.ts # sitemapSources / apiBaseUrl
42
41
  vite.config.ts # react 插件(无代理:接口服务端已开通 CORS,直连)
43
42
  ```
44
43
 
@@ -61,7 +60,7 @@ vite.config.ts # react 插件(无代理:接口服务端已开通 C
61
60
 
62
61
  外部接口以 `apiBaseUrl`(`src/pactor/config.ts`,缺省
63
62
  `http://localhost:9011`,即 next-app-starter 的 dev server)为基准:
64
- `sitemapSources` / `pageEndpoint` 只写相对路径,由 `baseUrl` 补全为
63
+ `sitemapSources` 只写相对路径,由 `baseUrl` 补全为
65
64
  外部绝对地址。**假定服务端已开通 CORS,浏览器直连,无需项目内代理**——
66
65
  接口方(next-app-starter)已在路由层放开跨源并支持凭据
67
66
  (Allow-Origin 回显 + Allow-Credentials + 预检响应),cookie 会话跨源
@@ -85,8 +84,8 @@ vite.config.ts # react 插件(无代理:接口服务端已开通 C
85
84
  - `menus`:导航 / 路由树(`pageId` 引用页面、`layout` 引用布局、
86
85
  `public` 免登录;`path` / `title` 缺省从引用页面继承)
87
86
  - `auth.loginPath`:未登录重定向目标
88
- 3. 懒加载页:实现 `GET {pageEndpoint}/{id}` 返回 PageResource envelope
89
- (缺省 `/api/pages/{id}`)。
87
+ 3. 懒加载页:实现 `GET /api/pages/{id}` 返回 PageResource envelope
88
+ (路径为框架缺省;需自定义时给 `SitemapApp` 传 `pageEndpoint` prop)。
90
89
  4. 登录 / 登出等接口调用直接在 DSL 中用内置 `request` 动作表达(URL
91
90
  指向你的真实接口);有客户端服务层时可用内置 `service.call` 动作
92
91
  桥接,`session.context` 会话桥由框架内置,均无需自行注册。
@@ -10,10 +10,10 @@
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@pactor-app/core": "^1.20.0",
14
- "@pactor-app/runtime": "^1.20.0",
15
- "@pactor-app/ui": "^1.20.0",
16
- "pactor-app": "^1.20.0",
13
+ "@pactor-app/core": "^1.22.0",
14
+ "@pactor-app/runtime": "^1.22.0",
15
+ "@pactor-app/ui": "^1.22.0",
16
+ "pactor-app": "^1.22.0",
17
17
  "react": "19.2.8",
18
18
  "react-dom": "19.2.8",
19
19
  "react-router-dom": "^7.18.2"
@@ -0,0 +1,6 @@
1
+ # 模板独立安装(pnpm install)时批准运行构建脚本的原生依赖:
2
+ # esbuild(vite 依赖链上的原生二进制)。显式批准而非
3
+ # ignoredBuiltDependencies 静默,避免 strict-dep-builds / CI 环境下报
4
+ # ERR_PNPM_IGNORED_BUILDS
5
+ onlyBuiltDependencies:
6
+ - esbuild
@@ -5,14 +5,10 @@
5
5
  * 取第一个源。
6
6
  *
7
7
  * 本模板不自带后端:接口复用 next-app-starter 的 mock API
8
- * (apiBaseUrl 指向其 dev server,已开通 CORS)。sources / pageEndpoint
9
- * 只写相对路径,由 apiBaseUrl 补全为外部绝对地址,浏览器直连,
10
- * 无需项目内代理。
8
+ * (apiBaseUrl 指向其 dev server,已开通 CORS)。sources 只写相对路径,
9
+ * 由 apiBaseUrl 补全为外部绝对地址,浏览器直连,无需项目内代理。
11
10
  */
12
11
  export const sitemapSources: string[] = ["/api/sitemap"];
13
12
 
14
13
  /** 外部接口基准地址(SitemapApp baseUrl):相对接口路径以此为基准补全 */
15
14
  export const apiBaseUrl = "http://localhost:9011";
16
-
17
- /** 懒加载页面端点(pages[].pageDsl 缺省时按 id 获取) */
18
- export const pageEndpoint = (pageId: string): string => `/api/pages/${pageId}`;