openxiangda 2.0.0-alpha.99 → 2.0.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.
- package/LICENSE +21 -0
- package/README.md +23 -20
- package/bin/distribution/commands.js +55 -0
- package/bin/distribution/launcher.js +49 -0
- package/bin/distribution/migrate.js +60 -0
- package/bin/distribution/releases.js +52 -0
- package/bin/distribution/skills.js +80 -0
- package/bin/distribution/update.js +68 -0
- package/bin/distribution/workspace.js +85 -0
- package/bin/run.js +9 -11
- package/dist/browser/AuthoritativeSelector.d.ts +3 -2
- package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
- package/dist/browser/AuthoritativeSelector.js +39 -24
- package/dist/browser/AuthoritativeSelector.js.map +1 -1
- package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
- package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
- package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
- package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
- package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
- package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
- package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.js +1 -1
- package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
- package/dist/browser/components/resource/SurfaceFields.d.ts +3 -2
- package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
- package/dist/browser/components/resource/SurfaceFields.js +14 -7
- package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
- package/dist/browser/components/resource/resource-import.d.ts +16 -1
- package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
- package/dist/browser/components/resource/resource-import.js +58 -34
- package/dist/browser/components/resource/resource-import.js.map +1 -1
- package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
- package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
- package/dist/browser/components/todo/ApplicationTodoCenterPage.js +1 -2
- package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
- package/dist/browser/components/workflow/StandardWorkflowPages.js +54 -52
- package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
- package/dist/browser/platform-client.d.ts +3 -2
- package/dist/browser/platform-client.d.ts.map +1 -1
- package/dist/browser/platform-client.js +69 -18
- package/dist/browser/platform-client.js.map +1 -1
- package/dist/browser/record-detail.css +3 -2
- package/dist/browser/runtime.d.ts.map +1 -1
- package/dist/browser/runtime.js +26 -2
- package/dist/browser/runtime.js.map +1 -1
- package/dist/browser/workflow-launch.d.ts +4 -1
- package/dist/browser/workflow-launch.d.ts.map +1 -1
- package/dist/browser/workflow-launch.js +32 -0
- package/dist/browser/workflow-launch.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js.map +1 -1
- package/documentation/AGENTS.md +26 -0
- package/documentation/administration.md +27 -0
- package/documentation/application-foundation.md +162 -0
- package/documentation/appspec.md +152 -0
- package/documentation/backend.md +132 -0
- package/documentation/concepts.md +61 -0
- package/documentation/data-authz.md +62 -0
- package/documentation/delivery.md +110 -0
- package/documentation/development.md +32 -0
- package/documentation/field-components.md +236 -0
- package/documentation/frontend.md +269 -0
- package/documentation/getting-started.md +66 -0
- package/documentation/interaction-patterns.md +56 -0
- package/documentation/manifest.json +120 -0
- package/documentation/product-design.md +142 -0
- package/documentation/public-access.md +167 -0
- package/documentation/reference/cli.md +27 -0
- package/documentation/reference/mcp.md +649 -0
- package/documentation/testing.md +63 -0
- package/documentation/upgrading.md +39 -0
- package/documentation/workflow-events.md +181 -0
- package/launcher-skill/openxiangda/SKILL.md +24 -0
- package/package.json +72 -9
- package/releases/2.0.0.json +50 -0
- package/skills/manifest.json +2 -2
- package/skills/openxiangda-v2/SKILL.md +64 -51
- package/skills/openxiangda-v2/agents/openai.yaml +2 -2
- package/skills/openxiangda-v2/references/administration.md +27 -0
- package/skills/openxiangda-v2/references/application-foundation.md +162 -0
- package/skills/openxiangda-v2/references/appspec.md +132 -47
- package/skills/openxiangda-v2/references/backend.md +101 -248
- package/skills/openxiangda-v2/references/cli.md +27 -0
- package/skills/openxiangda-v2/references/concepts.md +61 -0
- package/skills/openxiangda-v2/references/data-authz.md +36 -388
- package/skills/openxiangda-v2/references/delivery.md +110 -49
- package/skills/openxiangda-v2/references/development.md +32 -0
- package/skills/openxiangda-v2/references/field-components.md +236 -0
- package/skills/openxiangda-v2/references/frontend.md +254 -280
- package/skills/openxiangda-v2/references/getting-started.md +66 -0
- package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
- package/skills/openxiangda-v2/references/mcp.md +649 -0
- package/skills/openxiangda-v2/references/product-design.md +142 -0
- package/skills/openxiangda-v2/references/public-access.md +92 -84
- package/skills/openxiangda-v2/references/testing.md +45 -56
- package/skills/openxiangda-v2/references/upgrading.md +39 -0
- package/skills/openxiangda-v2/references/workflow-events.md +143 -285
- package/skills/openxiangda-v2/references/architecture.md +0 -9
- package/skills/openxiangda-v2/references/commands.md +0 -21
- package/skills/openxiangda-v2/references/discovery.md +0 -15
- package/skills/openxiangda-v2/references/workspace.md +0 -62
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# 前端架构
|
|
2
|
+
|
|
3
|
+
状态:Vite/Refine 已成为 OpenXiangda 2.0 唯一默认前端栈。决策与实测见
|
|
4
|
+
[核心架构](./concepts.md)。
|
|
5
|
+
|
|
6
|
+
## 默认技术栈
|
|
7
|
+
|
|
8
|
+
- Vite 7:开发服务器与生产构建,只监听 `127.0.0.1`;
|
|
9
|
+
- React 19 + React Router:普通应用路由;
|
|
10
|
+
- Refine Core:资源查询、分页、排序和 mutation 状态;
|
|
11
|
+
- Ant Design 6:B 端页面组件。
|
|
12
|
+
|
|
13
|
+
不维护 Umi/Pro 双栈,也不在新模板中依赖已退休的前端框架包或旧身份 Provider。
|
|
14
|
+
|
|
15
|
+
## 数据和权限
|
|
16
|
+
|
|
17
|
+
`modules/` 中的业务模型由编译器派生 DataResource 契约,`openxiangda.config.ts` 声明页面
|
|
18
|
+
capability、应用角色和数据策略。前端只根据当前登录用户完整应用角色并集的
|
|
19
|
+
`capabilityCodes` 隐藏页面、按钮和只读字段;这些只是展示保护,平台 Data API
|
|
20
|
+
每次请求仍按同一角色并集做权威的行、字段和操作授权。
|
|
21
|
+
|
|
22
|
+
自定义角色管理页只调用 `openxiangda/core` 的角色管理 SDK。目录搜索使用
|
|
23
|
+
`searchRoleManagementUsers`,页面按钮按 `loadRoleManagementCatalog()` 返回的
|
|
24
|
+
`roleManagement` 投影显示,但前端显示不能替代服务端复核。不要在应用中复制角色表、
|
|
25
|
+
权限表或通过 NestJS 转发开发者凭据。
|
|
26
|
+
|
|
27
|
+
列表查询必须使用服务端过滤、排序和分页;新增、读取、更新、删除和文件上传都经过
|
|
28
|
+
可替换 Data API adapter。不要调用自定义 Nest CRUD、Function 或 Workflow 来绕过
|
|
29
|
+
Data API。只有真正需要事务或外部系统的动作才使用同源 `/api`。
|
|
30
|
+
|
|
31
|
+
默认仪器模块有 30 个字段,其中 `id/revision` 是 Data API 系统字段,28 个业务
|
|
32
|
+
字段由资源声明。新增、编辑和详情共用同一份字段元数据。五个边界字段使用五个独立
|
|
33
|
+
capability,不使用角色名或影子字段判断。
|
|
34
|
+
|
|
35
|
+
字段策略用 `create` / `update` 分别声明能力;显式空数组表示拒绝。学校管理员通过
|
|
36
|
+
`unrestrictedRoleCodes` 跳过行谓词,但仍受资源和字段能力
|
|
37
|
+
约束;学院管理员创建时可填写五个边界字段,更新时禁止修改 `collegeId`;仪器管理员
|
|
38
|
+
更新时禁止修改这五个字段。表单提交必须从 payload 删除无权字段。
|
|
39
|
+
|
|
40
|
+
DataQuery 使用有界 where 条件树,支持 and/or/not。标准列表的筛选、关键词、分页和导出共用已声明字段与同一查询条件,不在页面重写查询协议。
|
|
41
|
+
|
|
42
|
+
## 标准后台扩展
|
|
43
|
+
|
|
44
|
+
默认 CRUD、统一 Shell、current-user 权限和 Data Provider 都由 `openxiangda/react`
|
|
45
|
+
维护;应用拥有后台信息架构声明。页面实现、路由可达、菜单可见是三个不同合同:
|
|
46
|
+
|
|
47
|
+
- `resource.generated` 与 `frontend.routes` 决定有哪些标准页或 operation route;
|
|
48
|
+
- 编译器把全部可达页生成到 `adminPages`,detail/new/edit/handoff 可以存在但默认不进菜单;
|
|
49
|
+
- `frontend.admin.navigation` 是菜单的唯一权威声明,Shell 只渲染其中引用的页面,最后再按
|
|
50
|
+
current-user 权限过滤。权限不能发现或创建菜单项。
|
|
51
|
+
|
|
52
|
+
生成式资源后台固定使用 `/admin/resources/<resourceCode>` 命名空间,detail、create、
|
|
53
|
+
update 分别追加 `/:id`、`/new`、`/:id/edit`;独立移动后台面使用同一编译目录投影出的
|
|
54
|
+
`/m/admin/resources/<resourceCode>...`。所有桌面生成页由平台放进唯一 `Shell`,页面内部
|
|
55
|
+
跳转也只消费生成路径。普通 `/activities`、`/m/activities` 等产品路径留给 `user`
|
|
56
|
+
route。显式 route 与任何平台生成 route 的路径形状冲突时编译失败;即使动态参数名不同,
|
|
57
|
+
例如 `/:id` 与 `/:recordId`,也视为同一路径。不要添加旧根路径别名、重定向或通过注册顺序
|
|
58
|
+
解决冲突。
|
|
59
|
+
|
|
60
|
+
使用 typed helper 声明业务分组、用户名称、顺序、图标与页面引用,不维护 raw JSON:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import {
|
|
64
|
+
adminNavigationGroup,
|
|
65
|
+
adminOperationPage,
|
|
66
|
+
adminResourcePage,
|
|
67
|
+
defineAdminNavigation,
|
|
68
|
+
defineOpenXiangdaApp,
|
|
69
|
+
} from 'openxiangda/config';
|
|
70
|
+
|
|
71
|
+
export default defineOpenXiangdaApp({
|
|
72
|
+
// ...
|
|
73
|
+
frontend: {
|
|
74
|
+
root: 'apps/web',
|
|
75
|
+
routes: [{
|
|
76
|
+
code: 'instrument-import',
|
|
77
|
+
path: '/admin/operations/instrument-import',
|
|
78
|
+
label: '仪器导入',
|
|
79
|
+
surface: 'admin',
|
|
80
|
+
}],
|
|
81
|
+
user: { applicationTodoCenter: true },
|
|
82
|
+
admin: {
|
|
83
|
+
access: { anyOf: ['app:example:admin:view'] },
|
|
84
|
+
navigation: defineAdminNavigation([
|
|
85
|
+
adminNavigationGroup('instrument-center', '仪器管理', [
|
|
86
|
+
adminResourcePage('instruments'),
|
|
87
|
+
adminOperationPage('instrument-import'),
|
|
88
|
+
], { icon: 'database', order: 100 }),
|
|
89
|
+
]),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
AI 先读取 `openxiangda://workspace/contracts` 的有界索引,再调用
|
|
96
|
+
`contract_describe` 并传入 `{ "selector": "navigation" }`,从 `data.selection.adminNavigationAuthoring.suggestion` 取得确定性的首次建议:
|
|
97
|
+
`proposal` 是有界机器可读声明,`imports` 是 `openxiangda/config` 的 typed helper,
|
|
98
|
+
`expression` 可一次性写入
|
|
99
|
+
`frontend.admin.navigation`,然后由应用正常编辑。该结果明确标记
|
|
100
|
+
`applyMode: "copy-once"` 和 `automaticRuntimeDiscovery: false`;compiler 和 runtime
|
|
101
|
+
从不调用建议器,也不会在以后新增内部资源时偷偷扩展生产菜单。直接使用 compiler API 的
|
|
102
|
+
tooling 也可调用 `renderAdminNavigationSuggestion(config)` 获得同一 proposal。
|
|
103
|
+
|
|
104
|
+
用 `defineApplicationContributions` 将每个生成的 `appRoutes` route 精确绑定到一个本地
|
|
105
|
+
React page。`admin` page 由 runtime 放入唯一的 `Shell`,component 不再次嵌套;`user`
|
|
106
|
+
page 不套 admin Shell,可独立实现移动/用户端布局,但仍共享当前用户、权限和 Router:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import {
|
|
110
|
+
defineApplicationContributions,
|
|
111
|
+
OpenXiangdaApplication,
|
|
112
|
+
} from 'openxiangda/react';
|
|
113
|
+
import {
|
|
114
|
+
adminNavigation,
|
|
115
|
+
adminAccess,
|
|
116
|
+
adminPages,
|
|
117
|
+
appRoutes,
|
|
118
|
+
routeManifest,
|
|
119
|
+
} from '@app/contracts/generated';
|
|
120
|
+
import { InstrumentCalibrationPage } from './operations/InstrumentCalibrationPage';
|
|
121
|
+
|
|
122
|
+
const contributions = defineApplicationContributions(appRoutes, {
|
|
123
|
+
pages: {
|
|
124
|
+
instrumentCalibration: InstrumentCalibrationPage,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
<OpenXiangdaApplication
|
|
129
|
+
adminNavigation={adminNavigation}
|
|
130
|
+
adminAccess={adminAccess}
|
|
131
|
+
adminPages={adminPages}
|
|
132
|
+
routeManifest={routeManifest}
|
|
133
|
+
contributions={contributions}
|
|
134
|
+
/>;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`pages` 的键必须与生成的 `appRoutes` 完全一致。隐藏 route 仍执行同一 `capability` 或
|
|
138
|
+
`access.allOf/anyOf`,子 route 同时继承全部祖先约束。页面代码随应用不可变前端制品构建,
|
|
139
|
+
不能从平台下载 component/module URL。admin operation page 固定使用
|
|
140
|
+
`/admin/operations` 或其子路径;带参数 route 只可直接访问,不能被导航引用。
|
|
141
|
+
`defineAdminContributions` 是保留的 admin-only helper,会主动拒绝 `user` routes。
|
|
142
|
+
|
|
143
|
+
应用登录使用可选 `frontend.authentication` 声明:仅允许现有平台用户、拒绝注册,桌面
|
|
144
|
+
固定 `/login`、移动固定 `/m/login`,并分别引用同设备的静态 user 默认 route。登录面由
|
|
145
|
+
编译器生成到独立 `authenticationSurfaces`,不进入受保护 `appRoutes`。应用通过
|
|
146
|
+
`defineApplicationContributions({ routes: appRoutes, authenticationSurfaces },
|
|
147
|
+
{ pages, authentication })` 绑定独立 PC/移动 renderer;renderer 只拥有品牌视觉和本地
|
|
148
|
+
展示状态,并调用 `ApplicationLoginSurfaceProps` 的平台回调。密码、租户 provider、一次性
|
|
149
|
+
OAuth state/callback、Secure HttpOnly 会话与 refresh family、当前身份和 AuthZ 始终由平台
|
|
150
|
+
唯一持有。禁止调用 v1 auth API、在浏览器保存 Token、把登录页塞入受保护路由或创建第二
|
|
151
|
+
Router/identity provider。生成的 `platformAuthManifest` 是独立登录 QA 清单,不改变用户路由
|
|
152
|
+
覆盖数。
|
|
153
|
+
|
|
154
|
+
## 匿名公开用户页
|
|
155
|
+
|
|
156
|
+
没有平台账号的外部用户不进入应用登录或角色并集。此类页面使用专门的
|
|
157
|
+
[`frontend.publicAccess` 匿名公开访问合同](./public-access.md),由平台生成 HttpOnly 浏览器凭证,
|
|
158
|
+
并通过 `createAnonymousPublicClient` 提供草稿、附件、具名重复校验、幂等提交和同一浏览器的
|
|
159
|
+
本人列表/详情。不要创建 guest 账号、公开一般 Data API、保存本地身份或使用 IP/指纹判断所有权。
|
|
160
|
+
|
|
161
|
+
这也是完整应用的一等组合边界:`OpenXiangdaApplication` 始终唯一持有
|
|
162
|
+
`BrowserRouter`、`RuntimeBoundary`、Refine、generated resource/workflow routes
|
|
163
|
+
和后台 `Shell`。应用不要把它包在第二个 Router/Shell 中。桌面/移动用户端可以绑定不同
|
|
164
|
+
组件并拥有各自布局,但仍共享同一个 runtime identity 和 capability ancestor guard。
|
|
165
|
+
|
|
166
|
+
通用应用级待办中心通过用户 Surface 声明开启:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
frontend: {
|
|
170
|
+
user: { applicationTodoCenter: true },
|
|
171
|
+
// ...
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
编译器生成 `/todos` 和 `/m/todos`。页面只读取当前登录用户的
|
|
176
|
+
Notification Hub 收件人投影;不调用 management API,不复制消息状态库。桌面端使用
|
|
177
|
+
无常驻详情的全宽列表,移动端使用独立卡片列表;`查看详情` 统一进入平台解析后的
|
|
178
|
+
Workflow/custom application route。
|
|
179
|
+
|
|
180
|
+
编译器同时生成必填的 `routeManifest`。它是 Workflow/Todo 标准页的唯一桌面/移动成对路由来源,
|
|
181
|
+
每个 entry 都携带稳定 `routeCode`、参数名、访问能力和 `requiresAuthentication: true`,顶层
|
|
182
|
+
`digest` 绑定完整 catalog。模板把该产物直接传给 `OpenXiangdaApplication`;runtime 在注册
|
|
183
|
+
Router 和页面前校验成对 user surface、参数与访问元数据。应用不得重新声明 `/todos`、
|
|
184
|
+
`/m/todos` 或标准 Workflow 路径,也不得添加别名、重定向或第二份
|
|
185
|
+
路由状态。digest 的计算与合同校验由 compiler/platform preflight 负责,浏览器只接受有效格式并
|
|
186
|
+
fail-closed 校验 entry/pair 元数据。
|
|
187
|
+
|
|
188
|
+
标准 Workflow 详情页直接渲染 Surface 的 `presentation.businessDetail`、`summary`、
|
|
189
|
+
typed timeline 和 operation descriptors。业务字段继续使用平台 `SurfaceFieldValue` 语义;
|
|
190
|
+
父子表、附件、富文本图片和签名不由应用另写 renderer 或拼接 Data API 文件 URL。
|
|
191
|
+
PC canonical 路径为 `/tasks/:taskId` 和 `/workflows/:instanceId`,使用独立全屏页面而不进入
|
|
192
|
+
后台 Shell;旧 admin 详情路径不存在。桌面和移动使用独立
|
|
193
|
+
renderer,但共享同一授权与命令生命周期。主决策操作固定在底部,
|
|
194
|
+
低频操作统一进入“更多操作”;意见输入延迟到动作确认层,页面不常驻空白意见表单。
|
|
195
|
+
页面只显示非系统业务字段与节点内操作,不展示 UUID、revision、事件序列或技术信息区;
|
|
196
|
+
`stale` 不产生常驻提示,真实命令冲突才显示刷新提示。
|
|
197
|
+
应用只有在业务交互确实不能由标准页表达时才声明成对的 custom detail route。
|
|
198
|
+
|
|
199
|
+
标准流程提交使用浏览器客户端的 `loadBusinessProcessReceipt(commandId)` 和
|
|
200
|
+
`pollBusinessProcessCommand(commandId, afterRevision)`(Nest 使用对应的
|
|
201
|
+
`OpenXiangdaBusinessProcessService.receipt/poll`)。首个回执可能是 `accepted`;按
|
|
202
|
+
`nextPoll` 继续读取直到 `terminal`,再消费 typed command/surface。不要把 accepted 当作提交失败,
|
|
203
|
+
也不要对平台端点发起未类型化的 `fetch`。
|
|
204
|
+
|
|
205
|
+
资源用 `mutationOwner: 'native' | 'action' | 'readonly' | 'workflow'` 声明 mutation owner,
|
|
206
|
+
并可用 `generated.list/detail/create/update/delete` 精确选择标准 surface。非 Native owner
|
|
207
|
+
不能生成或向应用角色授予 Native mutation;零可写业务字段不能开放 create/update。
|
|
208
|
+
Workflow definition 用 `launch.mode` 声明 `standalone`、`custom-page`、`hidden-handoff` 或
|
|
209
|
+
`work-center-only`;只有 `standalone` 可进入菜单,`hidden-handoff` 保留同一标准 PC/移动路由
|
|
210
|
+
但不进菜单。缺省 submission 使用 compiler 生成的标准 process operation。action-owned 资源
|
|
211
|
+
则在 `standalone`/`hidden-handoff` 上声明 `submission.kind: 'named-operation'`,把 create/existing
|
|
212
|
+
表单字段、平台幂等键、当前用户、subject id/revision 和响应结果显式绑定到原 named operation。
|
|
213
|
+
标准页仍使用 generated field components,但最终由应用服务端完成业务校验、原子写入并决定
|
|
214
|
+
是否返回 durable process command;缺少或返回 null command 表示本次无需审批。浏览器不提供
|
|
215
|
+
save callback,也不直接调用 prepare/start。`custom-page` 仍只能由 verified Named Action 注入
|
|
216
|
+
`OpenXiangdaBusinessProcessService` 发起。
|
|
217
|
+
|
|
218
|
+
标准资源页提供 `toolbar`、`row`、`detail` 三类 action slot。slot 声明稳定 code、label、
|
|
219
|
+
可选 order 和 capability/access,render 只获得当前 resource、已授权 record/selection 与
|
|
220
|
+
受控 `refresh()`:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
const contributions = defineApplicationContributions(appRoutes, {
|
|
224
|
+
pages: { instrumentCalibration: InstrumentCalibrationPage },
|
|
225
|
+
resources: {
|
|
226
|
+
instruments: {
|
|
227
|
+
row: [{
|
|
228
|
+
code: 'calibrate',
|
|
229
|
+
label: '校准',
|
|
230
|
+
capability: 'instrument.calibration.run',
|
|
231
|
+
render: ({ record, refresh }) => (
|
|
232
|
+
<CalibrationButton recordId={String(record.id)} onDone={refresh} />
|
|
233
|
+
),
|
|
234
|
+
}],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
slot 只扩展动作区域,不接管查询、字段策略、revision、保存或审计。跨资源事务、外部副作用
|
|
241
|
+
和业务不变量仍调用有 `@OpenXiangdaOperation` 门禁的 Nest App Operation;普通 CRUD 继续
|
|
242
|
+
直接使用 Native Data API。UI 隐藏不是服务端授权。
|
|
243
|
+
|
|
244
|
+
学院是应用自有 `colleges` Native Resource,仪器 `collegeId` 保存该资源的系统 UUID。
|
|
245
|
+
人员和部门选择器调用平台 Directory 的分页 search,并用 exact resolve 恢复已选 ID;学院
|
|
246
|
+
选择器调用 membership-bound scope-values search/resolve。空结果和错误直接展示,不回退
|
|
247
|
+
静态人员、部门或学院。组织部门与学院属于不同 owner,不能互相推断。
|
|
248
|
+
|
|
249
|
+
发布态 app/environment 只读取平台为每个 index 注入的
|
|
250
|
+
`openxiangda-runtime-base`、`openxiangda-app-code` 和
|
|
251
|
+
`openxiangda-environment` meta;不得使用 Vite 构建变量或手填环境覆盖它。
|
|
252
|
+
当前用户使用同源 `openxiangda.runtime-authorization/v2` 合同,每个 Data API query/body
|
|
253
|
+
都显式携带 environmentKey。标准客户端读取当前用户角色并集,不保存 Token,也不持久化
|
|
254
|
+
授权结果。该角色并集合同未部署的平台版本
|
|
255
|
+
必须在应用写入前 fail closed。
|
|
256
|
+
|
|
257
|
+
## 本地与浏览器门禁
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
openxiangda dev
|
|
261
|
+
pnpm --filter @app/web check
|
|
262
|
+
pnpm --filter @app/web test
|
|
263
|
+
pnpm --filter @app/web test:e2e
|
|
264
|
+
pnpm --filter @app/web build
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
测试必须真实断言 Vite LAN 不可达、production 警示 DOM 常驻、Data/Directory/App API
|
|
268
|
+
使用同一当前用户角色并集、Perspective 读取投影协议,并约束源码文件数、LOC、构建
|
|
269
|
+
体积和 gzip 体积。
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 安装与开始开发
|
|
2
|
+
|
|
3
|
+
OpenXiangda 2.0 默认生成 React 应用和共享契约。普通 CRUD、标准审批和通知使用远端平台能力;只有真实服务端业务动作才按需增加 NestJS。本地无需启动平台、Docker 或 PostgreSQL。
|
|
4
|
+
|
|
5
|
+
## 准备 {#prerequisites}
|
|
6
|
+
|
|
7
|
+
准备平台地址、具有应用开发权限的账号、Node.js 24 和 pnpm 10.15.1。向平台维护者取得已验证的 OpenXiangda 2.0 精确版本,并核对平台能力是否支持。`openxiangda@latest` 可能属于 1.x;不能用它选择 2.0。
|
|
8
|
+
|
|
9
|
+
## 安装与创建 {#create}
|
|
10
|
+
|
|
11
|
+
以下命令的版本占位符由随包资料替换为该根包的精确版本。网站源码阅读者应先确认要使用的发行版本。
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm dlx openxiangda@2.0.0 skill install --force
|
|
15
|
+
pnpm dlx openxiangda@2.0.0 auth status --base-url <平台地址> --json
|
|
16
|
+
pnpm dlx openxiangda@2.0.0 login --base-url https://platform.example.com
|
|
17
|
+
pnpm dlx openxiangda@2.0.0 create my-app --base-url https://platform.example.com
|
|
18
|
+
cd my-app
|
|
19
|
+
pnpm openxiangda context --json
|
|
20
|
+
pnpm openxiangda dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
将两处示例平台地址替换为同一个目标地址。`create` 先核对目标与当前登录平台,再创建本地目录、安装依赖并初始化远端应用;新应用不会自动沿用文件中最后登录的站点。CI 使用原有成对的 `OPENXIANGDA_BASE_URL` 和 `OPENXIANGDA_TOKEN` 时,可以由该显式地址指定目标。
|
|
24
|
+
|
|
25
|
+
初始化中断后重试同一命令;已有目录会核对原平台绑定,不能通过 `create` 改绑到其他站点。地址不一致时先检查目标并登录正确的平台,不修改 link 文件绕过检查,也不要使用内部 provision 接口另建应用。创建操作应在用户要求创建应用的范围内执行。
|
|
26
|
+
|
|
27
|
+
进入项目后使用 `pnpm openxiangda`,由项目依赖和锁文件决定版本。查看使用资料运行 `pnpm openxiangda docs`;查看单一主题运行 `pnpm openxiangda docs frontend`。安装到其他 AI 工具时使用 `skill install --destination <Skill根目录>`。
|
|
28
|
+
|
|
29
|
+
## 项目结构 {#workspace}
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
apps/web 页面与应用入口
|
|
33
|
+
packages/contracts 平台编译器生成的契约
|
|
34
|
+
modules 业务模型、页面选择和模块声明
|
|
35
|
+
openxiangda.config.ts 应用装配、导航、权限和按需能力
|
|
36
|
+
appspec 需求、架构、变更和验收记录
|
|
37
|
+
AGENTS.md 平台约定与项目自有说明
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
以实际模板输出为准。资源与页面通过模块声明组合,不创建 `platform/data`。`apps/server` 仅在启用后端时初始化;后续保留用户业务代码。
|
|
41
|
+
|
|
42
|
+
AppSpec 随开发持续维护:测试发布前补齐总纲、关联变更与验收计划,部署后记录真实业务结果,生产晋级核对该测试版本的验收报告。新应用业务实现前先完成[产品设计与确认基线](./product-design.md);研究、示例原型和技术检查可用于逐步完善设计。具体步骤见[全流程记录](./appspec.md)。
|
|
43
|
+
|
|
44
|
+
## 连接开发 {#connected-development}
|
|
45
|
+
|
|
46
|
+
`dev` 监听本机回环地址,页面通过同源代理访问平台测试数据。浏览器不持久化平台凭据。终端和页面显示当前环境;只有生产环境时会持续提示生产数据风险。开发数据写入仍是远端真实写入,按任务范围操作。
|
|
47
|
+
|
|
48
|
+
纯 CRUD 修改优先使用标准模型、字段和页面;跨模型事务或外部副作用再选择后端。角色、行和字段权限在平台执行。详见[开发流程](./development.md)、[模型与标准 CRUD](./application-foundation.md)和[按需后端](./backend.md)。
|
|
49
|
+
|
|
50
|
+
## 检查与交付 {#delivery}
|
|
51
|
+
|
|
52
|
+
只检查时运行 `pnpm openxiangda check`。需要部署测试环境时直接运行 `pnpm openxiangda deploy`,它已包含检查、测试和构建;无需再连续重复运行全部脚本。
|
|
53
|
+
|
|
54
|
+
生产必须复用成功测试运行。部署状态、真实角色验收和回滚步骤见[应用交付](./delivery.md)。纯前端发布不要求 Docker;启用后端后才需要官方镜像构建条件。
|
|
55
|
+
|
|
56
|
+
## 接入 MCP {#mcp}
|
|
57
|
+
|
|
58
|
+
MCP 使用相同的项目 CLI:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm exec openxiangda --mcp-stdio --cwd <应用绝对路径>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
先调用 `workspace_context`,再按任务读取 `docs_read` 和当前契约。配置示例与工具参数见[MCP 参考](./reference/mcp.md)。登录、创建和长期 dev 进程继续由 CLI/终端管理。
|
|
65
|
+
|
|
66
|
+
指定站点授权可用 `auth status --base-url <平台地址> --json` 或 MCP `authorization_status` 只读核验,无需工作区。状态为 `authorized` 才证明当前 access 被平台接受;`missing`/`platform_mismatch`/`refresh_required` 需处理会话,`unauthorized` 表示平台拒绝,`unavailable` 表示暂时无法核验,不能当成过期。查询不刷新、不打开浏览器、不修改绑定;应用管理权限需另行核验。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 页面交互模式与体验评审
|
|
2
|
+
|
|
3
|
+
按实际角色和任务选择下面的标准起点,并在 AppSpec 页面设计中记录适用范围、差异和验收。模式是设计建议,不是额外运行库;控件、导航、权限和数据继续消费[平台前端](./frontend.md)、[字段组件](./field-components.md)和[数据权限](./data-authz.md)。
|
|
4
|
+
|
|
5
|
+
## 标准管理页面 {#admin}
|
|
6
|
+
|
|
7
|
+
适用于管理员维护独立业务对象。显式选择 CRUD 视图,沿用 Field Kit、Ant Design 与组件默认外观。页面优先呈现标题、主要新建入口、常用筛选、列表与行操作;复杂筛选按需展开。列按办理任务选择,记录默认排序、分页上限、空值/长文显示与操作条件。辅助模型无需独立导航。
|
|
8
|
+
|
|
9
|
+
列表进入详情再返回时明确关键词、筛选、页码、选择范围与位置是否保留;批量操作说明当前页/已选记录范围,确认内容包含实际对象及影响。新建和编辑复用字段规则,失败定位到字段并保留其他输入。删除只在业务明确需要时提供,由服务端权限和业务约束最终裁定。
|
|
10
|
+
|
|
11
|
+
标准 CRUD 自带的状态可引用共用设计;页面仍需说明角色、数据范围、可见字段、业务校验及差异。AC 至少按实际维护任务验证列表到详情返回、成功写入、禁止写入和适用校验。
|
|
12
|
+
|
|
13
|
+
## PC 用户任务页面 {#pc-task}
|
|
14
|
+
|
|
15
|
+
适用于申请、办理和跨模型任务。先给当前任务与下一步,按使用频率和决策顺序安排信息,不把所有底表铺成菜单。列表、详情和办理页按任务分开;同页编辑/提交/成功等状态在同一 PageSpec 中记录。
|
|
16
|
+
|
|
17
|
+
为每个入口写清直接链接、前置条件、返回位置与成功出口。可编辑区域和只读依据区分,主操作有具体业务动词;确认步骤仅用于需要复核的信息和后果。页面动作对应一个明确的业务契约,提交结果未知时查询原结果,不以新随机幂等键重提。
|
|
18
|
+
|
|
19
|
+
## 移动用户页面 {#mobile-task}
|
|
20
|
+
|
|
21
|
+
使用有作用域的 `openxiangda/mobile` 与平台字段组件。根据现场任务独立组织首页、列表、详情和填写顺序;不把宽表格缩小后当作移动设计。确定常用入口、任务卡片上的必要信息、主动作、导航返回与输入退出规则。
|
|
22
|
+
|
|
23
|
+
逐页检查触摸目标、键盘遮挡、焦点、滚动、安全区和长标题;筛选抽屉的应用/取消含义明确。网络慢时保留输入并阻止误操作,失败后给恢复入口。图片先缩略图、原图按需,附件展示大小限制、进度和失败原因。设计写明目标尺寸和渠道,真实验收在目标端走完主要任务。
|
|
24
|
+
|
|
25
|
+
## 匿名表单与本人记录 {#public-form}
|
|
26
|
+
|
|
27
|
+
外部无平台账号的人使用[匿名公开访问](./public-access.md),不建 guest 角色或开放普通 Data API。设计说明公开入口、收集目的、必填/可选信息、附件规则、提交后结果和本人记录的能力范围。
|
|
28
|
+
|
|
29
|
+
明确同浏览器续填/本人访问的边界与凭证丢失后的实际行为,不承诺跨设备找回能力。所有权由平台填写;前端不接受用户指定其他人的身份。公开页面状态、校验、结果未知与安全提示按所消费契约设计,不虚构额外隐私同意或注册步骤。
|
|
30
|
+
|
|
31
|
+
## 审批与办理详情 {#workflow}
|
|
32
|
+
|
|
33
|
+
复用[标准流程、待办和通知](./workflow-events.md)。详情优先显示当前状态、业务摘要、当前任务及历史;只提供当前用户被授权的操作。区分申请人、办理人和旁观者看到的字段、意见与动作,写清退回可编辑范围、撤回条件、转交等已启用能力。
|
|
34
|
+
|
|
35
|
+
快速重复点击、任务已被他人处理和响应中断要有具体结果与恢复方式。通知详情跳转回标准目标,不复制流程状态或另做审批授权。AC 用真实角色覆盖主流程、退回/拒绝及越权反例。
|
|
36
|
+
|
|
37
|
+
## 每页交互规格检查 {#page-review}
|
|
38
|
+
|
|
39
|
+
把下表应用到每个实际页面。共用行为引用一个权威设计,页面写差异;不适用时说明业务理由,不为凑表增加功能。
|
|
40
|
+
|
|
41
|
+
| 维度 | 编码前的具体决定 |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| 任务与入口 | 稳定页面 ID,REQ/旅程、角色和目标;入口、直链、返回及成功出口 |
|
|
44
|
+
| 信息与字段 | 区域优先级、默认排序/筛选;标签、帮助、默认值、必填、联动、可编辑条件、校验时机和提示位置 |
|
|
45
|
+
| 操作与反馈 | 主次操作、触发条件、文案、隐藏/禁用依据、复核内容、成功反馈和后续动作 |
|
|
46
|
+
| 页面状态 | 初次加载/刷新、正常、首次空数据、筛选无结果、无权限、请求错误和重试 |
|
|
47
|
+
| 写入与恢复 | 未保存、提交中、成功、明确失败、结果未知、他人修改冲突;输入保留与草稿边界 |
|
|
48
|
+
| 数据边界 | 空值、长文本、最大条目、分页、批量选择范围;附件类型/大小/失败及重试 |
|
|
49
|
+
| 权限与多端 | 页面/动作/行/字段、多角色并集;PC 批量与移动任务差异、键盘焦点、触摸和返回 |
|
|
50
|
+
| 原型与验收 | 所选标准模式或原型位置、适用状态;对应 AC、角色、输入条件、操作和可观察结果 |
|
|
51
|
+
|
|
52
|
+
## 评审与验证尺度 {#evaluation}
|
|
53
|
+
|
|
54
|
+
先用用户能理解的方式走一遍“从哪里进来、看到什么、怎样完成、出错怎么恢复”,再核对规则、数据与权限的一致性。原型允许样例数据,必须标注;只有效果图不能证明任务可完成。选定标准模式也需完成本应用的逐页差异设计。
|
|
55
|
+
|
|
56
|
+
实现后用实际界面验证任务成功率和明显阻碍,至少覆盖已纳入范围的角色、入口、重要状态和目标端。记录结果未知、重复操作和并发冲突的服务端读回,不只看 toast;性能记录数据量和请求链路。构建通过、HTTP 200、截图数量或检查表填满均不代替真实业务验收。
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "openxiangda.documentation/v1",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"topics": [
|
|
5
|
+
{
|
|
6
|
+
"id": "getting-started",
|
|
7
|
+
"title": "安装与开始开发",
|
|
8
|
+
"file": "getting-started.md",
|
|
9
|
+
"sha256": "9888b3bcd1a8d3070486b2054c0e586ec19c0e8723d069160810439e1ec8aada"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "product-design",
|
|
13
|
+
"title": "对话发现与详细产品设计",
|
|
14
|
+
"file": "product-design.md",
|
|
15
|
+
"sha256": "acd5aab8f39c4875a7b26335299b9eede4986058a8cf742b51c2a4ffc0d47ebd"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "interaction-patterns",
|
|
19
|
+
"title": "页面交互模式与体验评审",
|
|
20
|
+
"file": "interaction-patterns.md",
|
|
21
|
+
"sha256": "14e3be52f1fce85dc913b3c58fa12d416dac7418d9817de2689f26ad38a7a134"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "development",
|
|
25
|
+
"title": "需求与开发流程",
|
|
26
|
+
"file": "development.md",
|
|
27
|
+
"sha256": "88fd8d58d7148c3bc5cab77a96fe5b41e61d42241f7d5d22b9a5d39397989b49"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "application-foundation",
|
|
31
|
+
"title": "业务模型与标准 CRUD",
|
|
32
|
+
"file": "application-foundation.md",
|
|
33
|
+
"sha256": "747fed29da3ae0c44337dd8413fb4234d0bb961aef2c9a9f9330b7f6f1efe788"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "appspec",
|
|
37
|
+
"title": "需求、设计与交付记录",
|
|
38
|
+
"file": "appspec.md",
|
|
39
|
+
"sha256": "69f3238e8e28538a51128fb3f0e2c4df79820020f74eca93b643ed227e8998c7"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "concepts",
|
|
43
|
+
"title": "架构与能力所有者",
|
|
44
|
+
"file": "concepts.md",
|
|
45
|
+
"sha256": "2c1445e9c86baa864087da585c7d613fff61745cdd9ebb9c83dcd2c3579c3c4a"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "frontend",
|
|
49
|
+
"title": "页面与标准组件扩展",
|
|
50
|
+
"file": "frontend.md",
|
|
51
|
+
"sha256": "d04547c8210723add76b14f9bbcba356ef3e0bdfe762b1bd3b2d92c5e77fd843"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "field-components",
|
|
55
|
+
"title": "字段、表单与移动控件",
|
|
56
|
+
"file": "field-components.md",
|
|
57
|
+
"sha256": "b26da9fb33518203c59a154dd0117f714f3edab5a4d84936b71107c936516893"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "data-authz",
|
|
61
|
+
"title": "数据查询与权限",
|
|
62
|
+
"file": "data-authz.md",
|
|
63
|
+
"sha256": "92d0f11e580f7c2ed8e2a8911119b3ece4401b7ee974f4e41ee645598ec721d5"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "public-access",
|
|
67
|
+
"title": "无账号的匿名公开访问",
|
|
68
|
+
"file": "public-access.md",
|
|
69
|
+
"sha256": "b6f13cec03d26d02b72f972ce870735f78dc065816284f2b944a955fb57ee448"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "workflow-events",
|
|
73
|
+
"title": "审批、事件与通知",
|
|
74
|
+
"file": "workflow-events.md",
|
|
75
|
+
"sha256": "e3cf53985f3d9b731e1e7ab25ccbd9225f380aa861d7055957aaf5cccc3b9082"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "backend",
|
|
79
|
+
"title": "按需后端与业务动作",
|
|
80
|
+
"file": "backend.md",
|
|
81
|
+
"sha256": "6965b67ecf2bca55f110a963d2a73f036c0215d55b0e68aaeec483ff5be20902"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "administration",
|
|
85
|
+
"title": "应用管理与有效配置",
|
|
86
|
+
"file": "administration.md",
|
|
87
|
+
"sha256": "f4ea9d0f385a22bfaa56593abe460e503f990abc3c31d25460c2570ae209ec40"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "testing",
|
|
91
|
+
"title": "检查与真实业务验收",
|
|
92
|
+
"file": "testing.md",
|
|
93
|
+
"sha256": "c948b31d5a9cb4903bf696ce3e14ad556ab6b1c9e03a28fd1275be2a7f58e9f3"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "delivery",
|
|
97
|
+
"title": "部署、生产晋级与恢复",
|
|
98
|
+
"file": "delivery.md",
|
|
99
|
+
"sha256": "361cf6a12a27155647d863b2ac39aeb57b6e6b5454ced6c13d3540891202a4fc"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "upgrading",
|
|
103
|
+
"title": "版本升级与资料刷新",
|
|
104
|
+
"file": "upgrading.md",
|
|
105
|
+
"sha256": "b90f7c7217651ddac0b1bc2f2419dbfe9c6a6732ffac3e17c114a51da062e219"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "cli",
|
|
109
|
+
"title": "CLI 命令参考",
|
|
110
|
+
"file": "reference/cli.md",
|
|
111
|
+
"sha256": "05005a0fb268af776510128132b984bc88ca20c5232190aa1ee26070bce796d4"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "mcp",
|
|
115
|
+
"title": "MCP 配置与工具参考",
|
|
116
|
+
"file": "reference/mcp.md",
|
|
117
|
+
"sha256": "063f4c07f28b7d8d3c0e132d9922ac182d23f28fde1d5e91adaf6015edc17b19"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|