openxiangda 1.0.137 → 1.0.138
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/lib/cli.js +111 -2
- package/lib/design-gates.js +7 -2
- package/openxiangda-skills/references/component-guide.md +7 -1
- package/openxiangda-skills/references/permission-design-patterns.md +32 -3
- package/openxiangda-skills/references/permissions-settings.md +39 -2
- package/package.json +1 -1
- package/packages/sdk/dist/{ProcessPreview-Ci8_UsbN.d.mts → ProcessPreview-CZJP8n3e.d.mts} +6 -0
- package/packages/sdk/dist/{ProcessPreview-Ci8_UsbN.d.ts → ProcessPreview-CZJP8n3e.d.ts} +6 -0
- package/packages/sdk/dist/components/index.cjs +143 -16
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +8 -2
- package/packages/sdk/dist/components/index.d.ts +8 -2
- package/packages/sdk/dist/components/index.mjs +143 -16
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/index.cjs +169 -25
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +1 -1
- package/packages/sdk/dist/runtime/index.d.ts +1 -1
- package/packages/sdk/dist/runtime/index.mjs +169 -25
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +26 -9
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +10 -2
- package/packages/sdk/dist/runtime/react.d.ts +10 -2
- package/packages/sdk/dist/runtime/react.mjs +26 -9
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
package/lib/cli.js
CHANGED
|
@@ -6963,6 +6963,21 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
6963
6963
|
} else {
|
|
6964
6964
|
errors.push(`${label}: 不支持的 viewType: ${definition.viewType}`);
|
|
6965
6965
|
}
|
|
6966
|
+
for (const [index, group] of (item.permissionGroups || []).entries()) {
|
|
6967
|
+
const actions = group.actions || group.operations;
|
|
6968
|
+
if (actions !== undefined) {
|
|
6969
|
+
if (!Array.isArray(actions)) {
|
|
6970
|
+
errors.push(`${label}.permissionGroups[${index}]: actions/operations 必须是数组`);
|
|
6971
|
+
} else {
|
|
6972
|
+
for (const action of actions) {
|
|
6973
|
+
const normalizedAction = normalizePermissionActionValue(action);
|
|
6974
|
+
if (!DATA_VIEW_PERMISSION_ACTIONS.has(normalizedAction)) {
|
|
6975
|
+
errors.push(`${label}.permissionGroups[${index}]: 不支持的数据视图 action "${action}"`);
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6966
6981
|
validateDataViewPerformance(label, definition, errors, warnings, storageMode);
|
|
6967
6982
|
}
|
|
6968
6983
|
if (kind === 'storageConfigs') {
|
|
@@ -7104,6 +7119,19 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
7104
7119
|
if (kind === 'formPermissionGroups') {
|
|
7105
7120
|
if (!item.name) errors.push(`${label}: 缺少 name`);
|
|
7106
7121
|
if (!item.formCode && !item.formUuid) errors.push(`${label}: 缺少 formCode 或 formUuid`);
|
|
7122
|
+
const actions = item.actions || item.operations;
|
|
7123
|
+
if (actions !== undefined) {
|
|
7124
|
+
if (!Array.isArray(actions)) {
|
|
7125
|
+
errors.push(`${label}: actions/operations 必须是数组`);
|
|
7126
|
+
} else {
|
|
7127
|
+
for (const action of actions) {
|
|
7128
|
+
const normalizedAction = normalizePermissionActionValue(action);
|
|
7129
|
+
if (!FORM_PERMISSION_ACTIONS.has(normalizedAction)) {
|
|
7130
|
+
errors.push(`${label}: 不支持的表单 action "${action}"`);
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7107
7135
|
if (item.dataPermission?.type === 'scope_policy' && !getScopePolicyCode(item.dataPermission)) {
|
|
7108
7136
|
errors.push(`${label}: dataPermission.type=scope_policy 时必须声明 policyCode`);
|
|
7109
7137
|
}
|
|
@@ -7136,9 +7164,33 @@ function validateScopePermissionResources(manifest, errors, warnings) {
|
|
|
7136
7164
|
? (Array.isArray(item.permissionGroups) ? item.permissionGroups : [])
|
|
7137
7165
|
: [item];
|
|
7138
7166
|
for (const group of groups) {
|
|
7167
|
+
const actions = kind === 'dataViews'
|
|
7168
|
+
? normalizeDataViewPermissionGroupActions(group)
|
|
7169
|
+
: normalizeFormPermissionGroupActions(group);
|
|
7139
7170
|
const dataPermission = group?.dataPermission;
|
|
7171
|
+
if (
|
|
7172
|
+
kind === 'formPermissionGroups' &&
|
|
7173
|
+
actions.includes('create') &&
|
|
7174
|
+
(!dataPermission || dataPermission.type !== 'scope_policy')
|
|
7175
|
+
) {
|
|
7176
|
+
warnings.push(`${resourceLabel(kind, item)}: 声明了 create,但未声明 create 对应的 scope_policy;若新增数据必须落在用户业务范围内,请补充 dataPermission.type=scope_policy`);
|
|
7177
|
+
}
|
|
7140
7178
|
if (!dataPermission) continue;
|
|
7141
7179
|
if (dataPermission.type === 'scope_policy') {
|
|
7180
|
+
if (kind === 'formPermissionGroups') {
|
|
7181
|
+
const missing = ['view', 'edit', 'delete', 'export']
|
|
7182
|
+
.filter(action => !actions.includes(action));
|
|
7183
|
+
if (missing.length) {
|
|
7184
|
+
warnings.push(`${resourceLabel(kind, item)}: 使用 scope_policy 但 action 覆盖不完整,缺少 ${missing.join(', ')};请确认每个角色的 action matrix`);
|
|
7185
|
+
}
|
|
7186
|
+
}
|
|
7187
|
+
if (kind === 'dataViews') {
|
|
7188
|
+
const missing = ['query', 'stats', 'export']
|
|
7189
|
+
.filter(action => !actions.includes(action));
|
|
7190
|
+
if (missing.length) {
|
|
7191
|
+
warnings.push(`${resourceLabel(kind, item)}: Data View 使用 scope_policy 但 action 覆盖不完整,缺少 ${missing.join(', ')}`);
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7142
7194
|
const policyCode = getScopePolicyCode(dataPermission);
|
|
7143
7195
|
if (!policyCode) {
|
|
7144
7196
|
errors.push(`${resourceLabel(kind, item)}: scope_policy 缺少 policyCode`);
|
|
@@ -10472,11 +10524,20 @@ function normalizeDataViewManifest(bound, dataView) {
|
|
|
10472
10524
|
definition: normalizedDefinition,
|
|
10473
10525
|
refreshConfig: dataView.refreshConfig || dataView.refresh || normalizedDefinition.refresh,
|
|
10474
10526
|
permissionGroups: Array.isArray(dataView.permissionGroups)
|
|
10475
|
-
? dataView.permissionGroups.map(group =>
|
|
10527
|
+
? dataView.permissionGroups.map(group => normalizeDataViewPermissionGroupManifest(group))
|
|
10476
10528
|
: undefined,
|
|
10477
10529
|
});
|
|
10478
10530
|
}
|
|
10479
10531
|
|
|
10532
|
+
function normalizeDataViewPermissionGroupManifest(group = {}) {
|
|
10533
|
+
const body = stripUndefinedValues({
|
|
10534
|
+
...withoutResourceMeta(group),
|
|
10535
|
+
operations: normalizeDataViewPermissionGroupActions(group),
|
|
10536
|
+
});
|
|
10537
|
+
delete body.actions;
|
|
10538
|
+
return body;
|
|
10539
|
+
}
|
|
10540
|
+
|
|
10480
10541
|
function normalizeRouteManifest(route) {
|
|
10481
10542
|
const code = route.code || route.resourceCode;
|
|
10482
10543
|
return stripUndefinedValues({
|
|
@@ -11161,10 +11222,12 @@ function normalizeFormPermissionGroupManifest(bound, group) {
|
|
|
11161
11222
|
name: group.name || group.code,
|
|
11162
11223
|
type: group.type || 'view',
|
|
11163
11224
|
roles: group.roles || [],
|
|
11225
|
+
operations: normalizeFormPermissionGroupActions(group),
|
|
11164
11226
|
};
|
|
11165
11227
|
delete body.code;
|
|
11166
11228
|
delete body.formCode;
|
|
11167
11229
|
delete body.form;
|
|
11230
|
+
delete body.actions;
|
|
11168
11231
|
return stripUndefinedValues(body);
|
|
11169
11232
|
}
|
|
11170
11233
|
|
|
@@ -11186,6 +11249,52 @@ function sortStringValues(value = []) {
|
|
|
11186
11249
|
return [...(value || [])].map(item => String(item)).sort();
|
|
11187
11250
|
}
|
|
11188
11251
|
|
|
11252
|
+
const FORM_PERMISSION_ACTIONS = new Set([
|
|
11253
|
+
'view',
|
|
11254
|
+
'create',
|
|
11255
|
+
'edit',
|
|
11256
|
+
'delete',
|
|
11257
|
+
'export',
|
|
11258
|
+
'import',
|
|
11259
|
+
'change_records',
|
|
11260
|
+
'workflow',
|
|
11261
|
+
]);
|
|
11262
|
+
|
|
11263
|
+
const DATA_VIEW_PERMISSION_ACTIONS = new Set([
|
|
11264
|
+
'query',
|
|
11265
|
+
'stats',
|
|
11266
|
+
'export',
|
|
11267
|
+
'refresh',
|
|
11268
|
+
]);
|
|
11269
|
+
|
|
11270
|
+
function normalizePermissionActionValue(action) {
|
|
11271
|
+
const value = String(action || '').trim();
|
|
11272
|
+
if (!value) return '';
|
|
11273
|
+
if (value === 'submit') return 'create';
|
|
11274
|
+
return value;
|
|
11275
|
+
}
|
|
11276
|
+
|
|
11277
|
+
function normalizePermissionActionArray(value, allowed, fallback = []) {
|
|
11278
|
+
const source = Array.isArray(value) ? value : fallback;
|
|
11279
|
+
return unique(source.map(normalizePermissionActionValue).filter(action => allowed.has(action)));
|
|
11280
|
+
}
|
|
11281
|
+
|
|
11282
|
+
function normalizeFormPermissionGroupActions(group = {}) {
|
|
11283
|
+
if (group.actions !== undefined || group.operations !== undefined) {
|
|
11284
|
+
return normalizePermissionActionArray(group.actions || group.operations, FORM_PERMISSION_ACTIONS);
|
|
11285
|
+
}
|
|
11286
|
+
return group.type === 'submit'
|
|
11287
|
+
? ['create']
|
|
11288
|
+
: ['view', 'edit', 'delete', 'change_records', 'workflow'];
|
|
11289
|
+
}
|
|
11290
|
+
|
|
11291
|
+
function normalizeDataViewPermissionGroupActions(group = {}) {
|
|
11292
|
+
if (group.actions !== undefined || group.operations !== undefined) {
|
|
11293
|
+
return normalizePermissionActionArray(group.actions || group.operations, DATA_VIEW_PERMISSION_ACTIONS);
|
|
11294
|
+
}
|
|
11295
|
+
return ['query'];
|
|
11296
|
+
}
|
|
11297
|
+
|
|
11189
11298
|
function pickObjectKeys(source = {}, keys = []) {
|
|
11190
11299
|
return keys.reduce((acc, key) => {
|
|
11191
11300
|
acc[key] = source[key];
|
|
@@ -11407,7 +11516,7 @@ function normalizeDataViewPermissionGroupsForPlan(groups = []) {
|
|
|
11407
11516
|
code,
|
|
11408
11517
|
name: group.name || code || '默认权限组',
|
|
11409
11518
|
roles: Array.isArray(group.roles) ? sortStringValues(group.roles) : [],
|
|
11410
|
-
operations:
|
|
11519
|
+
operations: sortStringValues(normalizeDataViewPermissionGroupActions(group)),
|
|
11411
11520
|
fieldPermissions: normalizeFieldPermissionsForPlan(group.fieldPermissions),
|
|
11412
11521
|
dataPermission: group.dataPermission || undefined,
|
|
11413
11522
|
});
|
package/lib/design-gates.js
CHANGED
|
@@ -124,15 +124,19 @@ const DESIGN_GATE_TOPICS = [
|
|
|
124
124
|
'哪些角色可以设置应用角色、分配角色成员、给角色分配接口权限;这些角色是否需要 `app:role:manage`',
|
|
125
125
|
'哪些角色可以维护页面权限组、表单权限组、组织账号;是否需要 `app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`',
|
|
126
126
|
'业务范围字段是什么,哪些可见字段需要派生隐藏 scalar key 供表单权限条件匹配',
|
|
127
|
-
'
|
|
127
|
+
'每个角色的 action matrix 是什么:view/create/edit/delete/export/import/change_records/workflow 是否分别授权',
|
|
128
|
+
'导出/导入是否独立授权;导出范围是否与 view 范围不同,导入是否还需要逐行 create 范围校验',
|
|
129
|
+
'create 是否需要 scope_policy 校验提交数据,防止新增到未授权业务范围',
|
|
130
|
+
'页面/菜单/路由、表单 actions/fieldAccessPolicy/dataPermission/scope_policy 的权限矩阵如何落到 resources',
|
|
128
131
|
'查询参数是否参与授权;如果参与,只能做上下文、筛选或 ticket 输入,敏感数据必须由 public-access grant、角色、表单权限组或 App Function 校验',
|
|
129
132
|
'验收时需要模拟哪些角色、账号状态、业务范围、查询参数篡改和拒绝场景',
|
|
130
133
|
],
|
|
131
134
|
recommendedDefaults: [
|
|
132
135
|
'正式后台优先选择 managed-platform-account;如果平台账号已存在,选择 existing-platform-user-assignment;固定内部门户选择 static-role-permission',
|
|
133
136
|
'query-param-context 仅用于低风险页面上下文、筛选条件或公开 ticket 输入,不作为敏感数据授权依据',
|
|
134
|
-
'角色写 src/resources/roles,页面组写 permissions/page-groups,表单组写 permissions/form-groups,显式声明
|
|
137
|
+
'角色写 src/resources/roles,页面组写 permissions/page-groups,表单组写 permissions/form-groups,显式声明 actions/dataPermission/fieldAccessPolicy;平台内部仍兼容存储为 operations',
|
|
135
138
|
'复杂业务范围优先写 permissions/scope-dimensions、permissions/scope-grant-sources、permissions/data-scope-policies,并在表单权限组或 data-view 权限组使用 dataPermission.type=scope_policy',
|
|
139
|
+
'scope_policy 只表达数据范围,actions 表达操作能力;按钮隐藏只是 UX,后端 action check 才是权威',
|
|
136
140
|
'scope_policy 默认语义是个人授权 + 当前应用角色授权;多维 rules 显式 AND;空授权集合拒绝;管理员绕过但可审计',
|
|
137
141
|
'能管理角色或给别人分配角色的应用角色必须在 roles manifest 声明 apiPermissionCodes;至少包含 app:role:manage,按需加入 app:page-permission-group:manage、app:form-permission-group:manage、app:organization:manage',
|
|
138
142
|
'由校区管理员等委托管理员创建的新角色,如果还具备继续管理账号/角色/权限的能力,也必须同步声明并发布对应 apiPermissionCodes',
|
|
@@ -144,6 +148,7 @@ const DESIGN_GATE_TOPICS = [
|
|
|
144
148
|
'不能只做前端权限隐藏,必须有后端角色、表单权限组、public-access grant 或 App Function 校验',
|
|
145
149
|
'查询参数不能作为敏感授权依据,只能作为上下文、筛选或 ticket 输入',
|
|
146
150
|
'只配菜单可见,不配后端数据权限',
|
|
151
|
+
'只隐藏按钮但没有表单权限组 actions 或 App Function 服务端校验',
|
|
147
152
|
'为了省事授予全部数据再在前端过滤',
|
|
148
153
|
'为每个客户/项目/区域/学院/班级/门店创建一个角色或一个权限组',
|
|
149
154
|
'把应用表单里的业务范围强行同步成平台部门、平台角色或大量权限组',
|
|
@@ -64,7 +64,7 @@ AI 在实现成熟交互前必须先判断是否已有可靠组件或库:
|
|
|
64
64
|
| 富文本编辑 | `EditorField` | 含图片上传集成 | 完整工具栏、平台图床、粘贴清洗 |
|
|
65
65
|
| 数字签名 | `DigitalSignatureField` | 平台级签名 | 签名采集、验证、归档 |
|
|
66
66
|
| 地理位置 | `LocationField` | 接入地图服务 | 定位、地图选点、地址解析 |
|
|
67
|
-
| 数据管理列表 | `DataManagementList` | 接入数据查询 API |
|
|
67
|
+
| 数据管理列表 | `DataManagementList` | 接入数据查询 API | 分页、筛选、导出、批量操作、字段权限、后端 action summary 按钮控制 |
|
|
68
68
|
|
|
69
69
|
### 示例
|
|
70
70
|
|
|
@@ -89,6 +89,12 @@ OSS 浏览器直传所需的 CORS 规则在 `src/resources/storage/<code>.json`
|
|
|
89
89
|
|
|
90
90
|
业务应用只能从 `openxiangda`、`openxiangda/runtime`、`openxiangda/runtime/react` 等公开入口导入组件和 SDK。不要为了减小包体积去引用 `openxiangda/packages/sdk/dist/...` 内部文件;React SPA 模板已提供 vendor chunk 分组,SDK 会把 `antd-mobile`、`dayjs` 等大型 UI 依赖交给应用构建器按路由拆分。
|
|
91
91
|
|
|
92
|
+
`DataManagementList` 默认 `permissionMode="auto"`,进入页面会读取表单 action
|
|
93
|
+
summary,并用 `can.create/export/import/delete/workflow/change_records` 隐藏新增、
|
|
94
|
+
导入、导出、删除、流程等按钮。`readonly=true` 仍会强制关闭新增、编辑、删除和
|
|
95
|
+
导入。`actionOverrides` 只能关闭按钮,不能扩大后端权限;敏感授权必须依赖后端
|
|
96
|
+
form actions、`scope_policy` 或 App Function 服务端校验。
|
|
97
|
+
|
|
92
98
|
> ✅ 一句话原则:**这些场景看到了,直接抄上表,不要自己写。**
|
|
93
99
|
|
|
94
100
|
---
|
|
@@ -27,8 +27,8 @@ Required design:
|
|
|
27
27
|
scalar scope keys, platform role member sync state.
|
|
28
28
|
- `src/resources/roles/*.json`: stable app role codes.
|
|
29
29
|
- `src/resources/permissions/page-groups/*.json`: menu, route, and page access.
|
|
30
|
-
- `src/resources/permissions/form-groups/*.json`:
|
|
31
|
-
|
|
30
|
+
- `src/resources/permissions/form-groups/*.json`: `actions`, data scope or
|
|
31
|
+
`dataPermission.type=scope_policy`, and `fieldAccessPolicy`.
|
|
32
32
|
- `apiPermissionCodes` on roles that are allowed to manage roles, role members,
|
|
33
33
|
permission groups, or organization accounts.
|
|
34
34
|
- Sync App Functions or trusted JS_CODE: sync organization units, accounts, and
|
|
@@ -142,8 +142,13 @@ The platform keeps RBAC and data range separate:
|
|
|
142
142
|
|
|
143
143
|
- Roles decide who can enter pages, submit/view forms, use operations, and
|
|
144
144
|
maintain permissions.
|
|
145
|
+
- Form `actions` decide what a role can do: `view`, `create`, `edit`, `delete`,
|
|
146
|
+
`export`, `import`, `change_records`, and `workflow`.
|
|
147
|
+
- Data View `actions` decide query capabilities: `query`, `stats`, `export`,
|
|
148
|
+
and `refresh`.
|
|
145
149
|
- Field access remains `fieldAccessPolicy`.
|
|
146
|
-
- Business scope policies decide which rows
|
|
150
|
+
- Business scope policies decide which rows or submitted scope values a matched
|
|
151
|
+
role/user can access for the current action.
|
|
147
152
|
- App Functions must still check role/scope server-side before sensitive
|
|
148
153
|
writes.
|
|
149
154
|
|
|
@@ -166,12 +171,36 @@ Default semantics:
|
|
|
166
171
|
a separate authorization row.
|
|
167
172
|
- App Function form read helpers enforce view permission for real callers. Do
|
|
168
173
|
not grant broad form read permission and then filter in function/page code.
|
|
174
|
+
- `DataManagementList` can hide buttons from the backend action summary, but it
|
|
175
|
+
is not the authority. Sensitive actions must be represented by form actions or
|
|
176
|
+
App Function server checks.
|
|
169
177
|
|
|
170
178
|
Do not model a dynamic business object set by generating dozens or hundreds of
|
|
171
179
|
roles or permission groups. Use a business authorization form and `scope_policy`
|
|
172
180
|
when the object set is maintained by users, imported from another system, or
|
|
173
181
|
expected to grow.
|
|
174
182
|
|
|
183
|
+
Recommended action pattern:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"code": "campus_profile_manage",
|
|
188
|
+
"formCode": "student_profile",
|
|
189
|
+
"name": "校区管理员档案权限",
|
|
190
|
+
"type": "view",
|
|
191
|
+
"roles": ["campus_admin"],
|
|
192
|
+
"actions": ["view", "edit", "export"],
|
|
193
|
+
"dataPermission": {
|
|
194
|
+
"type": "scope_policy",
|
|
195
|
+
"policyCode": "campus_row_scope"
|
|
196
|
+
},
|
|
197
|
+
"fieldAccessPolicy": {
|
|
198
|
+
"defaultAccess": "readonly",
|
|
199
|
+
"fields": [{ "fieldId": "remark", "access": "edit" }]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
175
204
|
## Role Setting Delegation
|
|
176
205
|
|
|
177
206
|
Setting application roles is itself permission controlled. A user can be a
|
|
@@ -289,7 +289,43 @@ Reference the policy from a form permission group:
|
|
|
289
289
|
"name": "学院查看学生",
|
|
290
290
|
"type": "view",
|
|
291
291
|
"roles": ["college_admin"],
|
|
292
|
-
"
|
|
292
|
+
"actions": ["view", "edit", "export"],
|
|
293
|
+
"dataPermission": {
|
|
294
|
+
"type": "scope_policy",
|
|
295
|
+
"policyCode": "college_data"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Unified permission model:
|
|
301
|
+
|
|
302
|
+
- `scope_policy` means data range: which rows this user/current app role can
|
|
303
|
+
access.
|
|
304
|
+
- `actions` means operation ability: what the role can do to rows in that
|
|
305
|
+
range. Form actions are `view`, `create`, `edit`, `delete`, `export`,
|
|
306
|
+
`import`, `change_records`, and `workflow`.
|
|
307
|
+
- The platform still stores actions in the legacy `operations` field. Manifests
|
|
308
|
+
may use either `actions` or `operations`; prefer `actions` in new resources.
|
|
309
|
+
- `type: "submit"` is the legacy create group and maps to `actions:["create"]`.
|
|
310
|
+
- `export` is independent from `view` for new apps. Old apps may temporarily
|
|
311
|
+
rely on view-to-export compatibility, but `permission audit` should be used to
|
|
312
|
+
migrate to explicit `export`.
|
|
313
|
+
- `create` with `scope_policy` validates submitted scope fields; a campus admin
|
|
314
|
+
cannot create a row for another campus just because the create button is
|
|
315
|
+
visible.
|
|
316
|
+
- Frontend button hiding is UX only. The backend action check and action-specific
|
|
317
|
+
data range are authoritative.
|
|
318
|
+
|
|
319
|
+
Create permission with business scope:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"code": "college_student_create",
|
|
324
|
+
"formCode": "student",
|
|
325
|
+
"name": "学院管理员新增本学院学生",
|
|
326
|
+
"type": "submit",
|
|
327
|
+
"roles": ["college_admin"],
|
|
328
|
+
"actions": ["create"],
|
|
293
329
|
"dataPermission": {
|
|
294
330
|
"type": "scope_policy",
|
|
295
331
|
"policyCode": "college_data"
|
|
@@ -298,7 +334,8 @@ Reference the policy from a form permission group:
|
|
|
298
334
|
```
|
|
299
335
|
|
|
300
336
|
For Data View permission groups, use the same `dataPermission` shape inside the
|
|
301
|
-
view's `permissionGroups`.
|
|
337
|
+
view's `permissionGroups`. Data View actions are `query`, `stats`, `export`,
|
|
338
|
+
and `refresh`.
|
|
302
339
|
|
|
303
340
|
Default runtime semantics:
|
|
304
341
|
|
package/package.json
CHANGED
|
@@ -1021,6 +1021,12 @@ interface ProcessDefinition {
|
|
|
1021
1021
|
interface ViewPermissionSummary {
|
|
1022
1022
|
fieldPermissions: Record<string, 'FORM_FILED_HIDDEN' | 'FORM_FILED_VIEW' | 'FORM_FILED_EDIT'>;
|
|
1023
1023
|
operations: string[];
|
|
1024
|
+
actions?: string[];
|
|
1025
|
+
can?: Record<string, boolean>;
|
|
1026
|
+
fieldAccessPolicy?: any;
|
|
1027
|
+
hasFullAccess?: boolean;
|
|
1028
|
+
resourceType?: string;
|
|
1029
|
+
matchedGroupCodes?: string[];
|
|
1024
1030
|
}
|
|
1025
1031
|
/** 表单实例数据 */
|
|
1026
1032
|
interface FormInstanceData {
|
|
@@ -1021,6 +1021,12 @@ interface ProcessDefinition {
|
|
|
1021
1021
|
interface ViewPermissionSummary {
|
|
1022
1022
|
fieldPermissions: Record<string, 'FORM_FILED_HIDDEN' | 'FORM_FILED_VIEW' | 'FORM_FILED_EDIT'>;
|
|
1023
1023
|
operations: string[];
|
|
1024
|
+
actions?: string[];
|
|
1025
|
+
can?: Record<string, boolean>;
|
|
1026
|
+
fieldAccessPolicy?: any;
|
|
1027
|
+
hasFullAccess?: boolean;
|
|
1028
|
+
resourceType?: string;
|
|
1029
|
+
matchedGroupCodes?: string[];
|
|
1024
1030
|
}
|
|
1025
1031
|
/** 表单实例数据 */
|
|
1026
1032
|
interface FormInstanceData {
|