openxiangda 1.0.148 → 1.0.149
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/openxiangda-skills/references/pages/page-sdk.md +19 -0
- package/package.json +1 -1
- package/packages/sdk/dist/{ProcessPreview-BoblxCUt.d.mts → ProcessPreview-BOCARAvP.d.mts} +9 -1
- package/packages/sdk/dist/{ProcessPreview-BoblxCUt.d.ts → ProcessPreview-BOCARAvP.d.ts} +9 -1
- package/packages/sdk/dist/components/index.cjs +33 -21
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +4 -4
- package/packages/sdk/dist/components/index.d.ts +4 -4
- package/packages/sdk/dist/components/index.mjs +33 -21
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/{dataManagementApi-BCzfV88G.d.mts → dataManagementApi-CLMqf79O.d.mts} +1 -1
- package/packages/sdk/dist/{dataManagementApi-_FFNPv2e.d.ts → dataManagementApi-DhpRKmlp.d.ts} +1 -1
- package/packages/sdk/dist/runtime/index.cjs +375 -30
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +3 -3
- package/packages/sdk/dist/runtime/index.d.ts +3 -3
- package/packages/sdk/dist/runtime/index.mjs +377 -32
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +12 -0
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +24 -2
- package/packages/sdk/dist/runtime/react.d.ts +24 -2
- package/packages/sdk/dist/runtime/react.mjs +12 -0
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/AGENTS.md +7 -0
- package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +18 -33
|
@@ -16,6 +16,7 @@ Guidelines:
|
|
|
16
16
|
- Use `sdk.function.invoke(code, { input })` for reusable backend business logic declared under `src/resources/functions/` and `src/functions/`. Do not implement multi-form orchestration, connector fan-out, notification orchestration, or permission-sensitive backend rules directly in a page component.
|
|
17
17
|
- Use `sdk.connector.invoke`, `sdk.connector.call("connector.api")`, or `sdk.connector.download` for external services. The SDK calls the platform runtime connector endpoint; it must not call third-party domains directly.
|
|
18
18
|
- Use `sdk.notification.sendByType` and `batchSendByType` for reusable business messages. Custom notification types must be declared in `src/resources/notifications/` and published with `openxiangda resource publish`.
|
|
19
|
+
- Use `sdk.createFileAccessTicket(bucketName, objectName, fileName, "preview")` when a custom React SPA page needs a user-openable attachment preview link. Open `response.result.previewPageUrl`; do not use `previewUrl` or `/service/file/preview-by-ticket/:ticket` as the page entry.
|
|
19
20
|
- Use `sdk.organization.departments.*` and `sdk.organization.accounts.*` only for admin-style app pages that intentionally manage platform departments/accounts. The current user needs `app:organization:manage` on the app. Do not call legacy `/user` or `/department` write endpoints from pages.
|
|
20
21
|
- Use `sdk.workCenter.listItems({ boxType })` and `sdk.workCenter.getStats()` for current-user work center lists and counts. `boxType` is one of `todo`, `done`, `cc`, or `initiated`. This is the end-user task/handled/cc/initiated surface; do not build todo pages by reading workflow operation logs, automation logs, or raw process-task tables.
|
|
21
22
|
- `sdk.form.create` returns identifiers and generated serial number values only. Treat `formInstId` / `formInstanceId` as the required success contract; `serialNumber` / `serialNumbers` are present only when the form has `SerialNumberField`. Do not expect the save response to contain the full row, formula results, or other server-generated field values. Call `sdk.form.getDetail` explicitly after create when those values are needed.
|
|
@@ -100,6 +101,24 @@ const response = await sdk.dataSource.run("tickets", {
|
|
|
100
101
|
|
|
101
102
|
Data view filters, having, and fields use output aliases such as `customerName` or `ticketCount`, not source references such as `customer.name`. For option-like fields, prefer scalar aliases such as `statusValue` in the view definition and page filters. If the page only needs one form, prefer `sdk.form.advancedSearch`. If the page only needs a simple one-form dropdown, prefer linkedForm options.
|
|
102
103
|
|
|
104
|
+
File preview ticket:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const ticketResponse = await sdk.createFileAccessTicket(
|
|
108
|
+
"lowcode",
|
|
109
|
+
"attachments/contracts/demo.docx",
|
|
110
|
+
"demo.docx",
|
|
111
|
+
"preview",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const previewPageUrl = ticketResponse.result?.previewPageUrl
|
|
115
|
+
if (previewPageUrl) {
|
|
116
|
+
window.open(previewPageUrl, "_blank")
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`previewPageUrl` is the final browser page URL, normally `/view/:appType/file-preview?ticket=...`. `previewUrl` is the file content stream for the preview renderer and should not be used as the user-facing page entry.
|
|
121
|
+
|
|
103
122
|
App Function invocation:
|
|
104
123
|
|
|
105
124
|
```ts
|
package/package.json
CHANGED
|
@@ -269,7 +269,15 @@ interface FormRuntimeApi {
|
|
|
269
269
|
success: boolean;
|
|
270
270
|
}>;
|
|
271
271
|
createDownloadTicket: (bucketName: string, objectName: string, fileName?: string) => Promise<any>;
|
|
272
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Creates a protected file access ticket.
|
|
274
|
+
*
|
|
275
|
+
* previewPageUrl is the page URL that can be opened directly.
|
|
276
|
+
* previewUrl is the raw file content stream URL for iframe/img/PDF viewers.
|
|
277
|
+
*/
|
|
278
|
+
createFileAccessTicket: (bucketName: string, objectName: string, fileName?: string, purpose?: 'download' | 'preview' | 'onlyoffice', options?: {
|
|
279
|
+
appType?: string;
|
|
280
|
+
}) => Promise<any>;
|
|
273
281
|
getUserById: (id: string) => Promise<any>;
|
|
274
282
|
getUserList: (params?: Record<string, any>) => Promise<any[]>;
|
|
275
283
|
getDepartmentRoots: () => Promise<any[]>;
|
|
@@ -269,7 +269,15 @@ interface FormRuntimeApi {
|
|
|
269
269
|
success: boolean;
|
|
270
270
|
}>;
|
|
271
271
|
createDownloadTicket: (bucketName: string, objectName: string, fileName?: string) => Promise<any>;
|
|
272
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Creates a protected file access ticket.
|
|
274
|
+
*
|
|
275
|
+
* previewPageUrl is the page URL that can be opened directly.
|
|
276
|
+
* previewUrl is the raw file content stream URL for iframe/img/PDF viewers.
|
|
277
|
+
*/
|
|
278
|
+
createFileAccessTicket: (bucketName: string, objectName: string, fileName?: string, purpose?: 'download' | 'preview' | 'onlyoffice', options?: {
|
|
279
|
+
appType?: string;
|
|
280
|
+
}) => Promise<any>;
|
|
273
281
|
getUserById: (id: string) => Promise<any>;
|
|
274
282
|
getUserList: (params?: Record<string, any>) => Promise<any[]>;
|
|
275
283
|
getDepartmentRoots: () => Promise<any[]>;
|
|
@@ -3317,6 +3317,11 @@ function canPreview(item) {
|
|
|
3317
3317
|
const ext = getFileExtension(item.name);
|
|
3318
3318
|
return isImageFile(item.name, item.contentType) || ["mp4", "webm", "ogg", "mov", "m4v"].includes(ext) || PREVIEW_PAGE_EXTENSIONS.includes(ext);
|
|
3319
3319
|
}
|
|
3320
|
+
function appendTicketIfMissing(url, ticket) {
|
|
3321
|
+
if (!url || !ticket || /[?&]ticket=/.test(url)) return url;
|
|
3322
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
3323
|
+
return `${url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
3324
|
+
}
|
|
3320
3325
|
function AttachmentFieldPC({
|
|
3321
3326
|
fieldId,
|
|
3322
3327
|
behavior,
|
|
@@ -3432,16 +3437,15 @@ function AttachmentFieldPC({
|
|
|
3432
3437
|
item.bucketName || bucketName,
|
|
3433
3438
|
item.objectName,
|
|
3434
3439
|
item.name,
|
|
3435
|
-
"preview"
|
|
3440
|
+
"preview",
|
|
3441
|
+
{ appType: item.appType || config.appType }
|
|
3436
3442
|
);
|
|
3437
3443
|
const ticketValue = typeof ticket === "string" ? { previewUrl: ticket } : ticket;
|
|
3438
|
-
if (ticketValue?.previewPageUrl
|
|
3439
|
-
|
|
3440
|
-
return `${ticketValue.previewPageUrl}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
3444
|
+
if (ticketValue?.previewPageUrl) {
|
|
3445
|
+
return appendTicketIfMissing(ticketValue.previewPageUrl, ticketValue.ticket);
|
|
3441
3446
|
}
|
|
3442
3447
|
if (ticketValue?.ticket && PREVIEW_PAGE_EXTENSIONS.includes(getFileExtension(item.name))) {
|
|
3443
|
-
|
|
3444
|
-
return `${previewPagePath}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
3448
|
+
return appendTicketIfMissing(previewPagePath, ticketValue.ticket);
|
|
3445
3449
|
}
|
|
3446
3450
|
return ticketValue?.previewUrl || ticketValue?.url || "";
|
|
3447
3451
|
}
|
|
@@ -3683,6 +3687,11 @@ function canPreview2(item) {
|
|
|
3683
3687
|
const ext = getFileExtension(item.name);
|
|
3684
3688
|
return isImageFile(item.name, item.contentType) || ["mp4", "webm", "ogg", "mov", "m4v"].includes(ext) || PREVIEW_PAGE_EXTENSIONS2.includes(ext);
|
|
3685
3689
|
}
|
|
3690
|
+
function appendTicketIfMissing2(url, ticket) {
|
|
3691
|
+
if (!url || !ticket || /[?&]ticket=/.test(url)) return url;
|
|
3692
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
3693
|
+
return `${url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
3694
|
+
}
|
|
3686
3695
|
function AttachmentFieldMobile({
|
|
3687
3696
|
fieldId,
|
|
3688
3697
|
behavior,
|
|
@@ -3831,16 +3840,15 @@ function AttachmentFieldMobile({
|
|
|
3831
3840
|
item.bucketName || bucketName,
|
|
3832
3841
|
item.objectName,
|
|
3833
3842
|
item.name,
|
|
3834
|
-
"preview"
|
|
3843
|
+
"preview",
|
|
3844
|
+
{ appType: item.appType || config.appType }
|
|
3835
3845
|
);
|
|
3836
3846
|
const ticketValue = typeof ticket === "string" ? { previewUrl: ticket } : ticket;
|
|
3837
|
-
if (ticketValue?.previewPageUrl
|
|
3838
|
-
|
|
3839
|
-
return `${ticketValue.previewPageUrl}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
3847
|
+
if (ticketValue?.previewPageUrl) {
|
|
3848
|
+
return appendTicketIfMissing2(ticketValue.previewPageUrl, ticketValue.ticket);
|
|
3840
3849
|
}
|
|
3841
3850
|
if (ticketValue?.ticket && PREVIEW_PAGE_EXTENSIONS2.includes(getFileExtension(item.name))) {
|
|
3842
|
-
|
|
3843
|
-
return `${previewPagePath}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
3851
|
+
return appendTicketIfMissing2(previewPagePath, ticketValue.ticket);
|
|
3844
3852
|
}
|
|
3845
3853
|
return ticketValue?.previewUrl || ticketValue?.url || "";
|
|
3846
3854
|
}
|
|
@@ -3971,6 +3979,11 @@ function canPreview3(item) {
|
|
|
3971
3979
|
const ext = getFileExtension(item.name);
|
|
3972
3980
|
return isImageFile(item.name, item.contentType) || ["mp4", "webm", "ogg", "mov", "m4v"].includes(ext) || PREVIEW_PAGE_EXTENSIONS3.includes(ext);
|
|
3973
3981
|
}
|
|
3982
|
+
function appendTicketIfMissing3(url, ticket) {
|
|
3983
|
+
if (!url || !ticket || /[?&]ticket=/.test(url)) return url;
|
|
3984
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
3985
|
+
return `${url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
3986
|
+
}
|
|
3974
3987
|
function AttachmentFieldReadonly({
|
|
3975
3988
|
fieldId,
|
|
3976
3989
|
readonlyClassName,
|
|
@@ -3981,7 +3994,7 @@ function AttachmentFieldReadonly({
|
|
|
3981
3994
|
showFileTypeBadge = false,
|
|
3982
3995
|
previewPagePath = "/file-preview"
|
|
3983
3996
|
}) {
|
|
3984
|
-
const { formData, api } = useFormContext();
|
|
3997
|
+
const { formData, api, config } = useFormContext();
|
|
3985
3998
|
const { isMobile } = useDeviceDetect();
|
|
3986
3999
|
const value = formData[fieldId] ?? [];
|
|
3987
4000
|
if (value.length === 0) {
|
|
@@ -4006,15 +4019,14 @@ function AttachmentFieldReadonly({
|
|
|
4006
4019
|
item.bucketName || bucketName,
|
|
4007
4020
|
item.objectName,
|
|
4008
4021
|
item.name,
|
|
4009
|
-
"preview"
|
|
4022
|
+
"preview",
|
|
4023
|
+
{ appType: item.appType || config.appType }
|
|
4010
4024
|
);
|
|
4011
4025
|
const ticketValue = typeof ticket === "string" ? { previewUrl: ticket } : ticket;
|
|
4012
|
-
if (ticketValue?.previewPageUrl
|
|
4013
|
-
|
|
4014
|
-
url = `${ticketValue.previewPageUrl}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
4026
|
+
if (ticketValue?.previewPageUrl) {
|
|
4027
|
+
url = appendTicketIfMissing3(ticketValue.previewPageUrl, ticketValue.ticket);
|
|
4015
4028
|
} else if (ticketValue?.ticket && PREVIEW_PAGE_EXTENSIONS3.includes(getFileExtension(item.name))) {
|
|
4016
|
-
|
|
4017
|
-
url = `${previewPagePath}${separator}ticket=${encodeURIComponent(ticketValue.ticket)}`;
|
|
4029
|
+
url = appendTicketIfMissing3(previewPagePath, ticketValue.ticket);
|
|
4018
4030
|
} else {
|
|
4019
4031
|
url = getTicketUrl(ticketValue, url);
|
|
4020
4032
|
}
|
|
@@ -10817,11 +10829,11 @@ function createFormRuntimeApi(config) {
|
|
|
10817
10829
|
});
|
|
10818
10830
|
return normalizeFileTicketResult(baseUrl, response.data || response.result);
|
|
10819
10831
|
},
|
|
10820
|
-
createFileAccessTicket: async (bucketName, objectName, fileName, purpose = "preview") => {
|
|
10832
|
+
createFileAccessTicket: async (bucketName, objectName, fileName, purpose = "preview", options = {}) => {
|
|
10821
10833
|
const response = await request({
|
|
10822
10834
|
url: "/file/access-ticket",
|
|
10823
10835
|
method: "post",
|
|
10824
|
-
data: { bucketName, objectName, fileName, purpose }
|
|
10836
|
+
data: { bucketName, objectName, fileName, purpose, appType: options.appType }
|
|
10825
10837
|
});
|
|
10826
10838
|
return normalizeFileTicketResult(baseUrl, response.data || response.result);
|
|
10827
10839
|
},
|