openxiangda-cli 2.0.0-alpha.58 → 2.0.0-alpha.59
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/package.json +4 -4
- package/template/apps/server/package.json +1 -1
- package/template/apps/web/e2e/instruments.spec.ts +285 -0
- package/template/apps/web/package.json +4 -2
- package/template/apps/web/scripts/check.mjs +1 -1
- package/template/apps/web/scripts/verify-build.mjs +14 -2
- package/template/apps/web/src/FilePreviewPage.tsx +303 -0
- package/template/apps/web/src/InstrumentForm.tsx +21 -130
- package/template/apps/web/src/components/platform-fields/AttachmentFileList.tsx +226 -0
- package/template/apps/web/src/main.tsx +5 -0
- package/template/apps/web/src/platform-client.ts +38 -3
- package/template/apps/web/src/runtime-meta.ts +15 -0
- package/template/apps/web/src/styles.css +163 -24
- package/template/apps/web/test/contracts.test.ts +104 -0
- package/template/package.json +3 -3
- package/template/scripts/verify-template-budget.mjs +1 -1
|
@@ -692,6 +692,25 @@ body {
|
|
|
692
692
|
gap: 2px;
|
|
693
693
|
}
|
|
694
694
|
|
|
695
|
+
.oxa-file-name-button {
|
|
696
|
+
border: 0;
|
|
697
|
+
background: transparent;
|
|
698
|
+
padding: 0;
|
|
699
|
+
color: inherit;
|
|
700
|
+
font: inherit;
|
|
701
|
+
text-align: left;
|
|
702
|
+
cursor: pointer;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.oxa-file-name-button:hover strong {
|
|
706
|
+
color: #1677ff;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.oxa-file-name-button:disabled {
|
|
710
|
+
cursor: wait;
|
|
711
|
+
opacity: 0.65;
|
|
712
|
+
}
|
|
713
|
+
|
|
695
714
|
.oxa-file-meta strong {
|
|
696
715
|
overflow: hidden;
|
|
697
716
|
text-overflow: ellipsis;
|
|
@@ -725,30 +744,6 @@ body {
|
|
|
725
744
|
padding: 22px;
|
|
726
745
|
}
|
|
727
746
|
|
|
728
|
-
.oxa-attachment-readonly {
|
|
729
|
-
display: grid;
|
|
730
|
-
width: 100%;
|
|
731
|
-
gap: 8px;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
.oxa-attachment-row {
|
|
735
|
-
display: grid;
|
|
736
|
-
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
737
|
-
align-items: center;
|
|
738
|
-
gap: 10px;
|
|
739
|
-
min-height: 42px;
|
|
740
|
-
border: 1px solid #e2e8f0;
|
|
741
|
-
border-radius: 7px;
|
|
742
|
-
padding: 6px 10px;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
.oxa-attachment-name {
|
|
746
|
-
overflow: hidden;
|
|
747
|
-
color: #27364d;
|
|
748
|
-
text-overflow: ellipsis;
|
|
749
|
-
white-space: nowrap;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
747
|
.oxa-detail-hero,
|
|
753
748
|
.oxa-edit-hero {
|
|
754
749
|
margin-bottom: 16px;
|
|
@@ -768,6 +763,150 @@ body {
|
|
|
768
763
|
gap: 16px;
|
|
769
764
|
}
|
|
770
765
|
|
|
766
|
+
.oxa-file-preview-page {
|
|
767
|
+
min-height: 100vh;
|
|
768
|
+
background: #eef2f7;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.oxa-file-preview-toolbar {
|
|
772
|
+
position: sticky;
|
|
773
|
+
z-index: 10;
|
|
774
|
+
top: 0;
|
|
775
|
+
display: flex;
|
|
776
|
+
min-height: 66px;
|
|
777
|
+
align-items: center;
|
|
778
|
+
justify-content: space-between;
|
|
779
|
+
gap: 20px;
|
|
780
|
+
border-bottom: 1px solid #dfe5ee;
|
|
781
|
+
background: rgb(255 255 255 / 96%);
|
|
782
|
+
padding: 10px 20px;
|
|
783
|
+
box-shadow: 0 2px 12px rgb(26 45 75 / 6%);
|
|
784
|
+
backdrop-filter: blur(12px);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.oxa-file-preview-toolbar > div:last-child {
|
|
788
|
+
display: flex;
|
|
789
|
+
gap: 8px;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.oxa-file-preview-title {
|
|
793
|
+
display: flex;
|
|
794
|
+
min-width: 0;
|
|
795
|
+
align-items: center;
|
|
796
|
+
gap: 12px;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.oxa-file-preview-title > .anticon {
|
|
800
|
+
color: #1677ff;
|
|
801
|
+
font-size: 26px;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.oxa-file-preview-title > div {
|
|
805
|
+
display: grid;
|
|
806
|
+
min-width: 0;
|
|
807
|
+
gap: 2px;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.oxa-file-preview-title strong,
|
|
811
|
+
.oxa-file-preview-title span {
|
|
812
|
+
overflow: hidden;
|
|
813
|
+
text-overflow: ellipsis;
|
|
814
|
+
white-space: nowrap;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.oxa-file-preview-title span {
|
|
818
|
+
color: #7b879b;
|
|
819
|
+
font-size: 12px;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.oxa-file-preview-body {
|
|
823
|
+
display: grid;
|
|
824
|
+
min-height: calc(100vh - 66px);
|
|
825
|
+
place-items: center;
|
|
826
|
+
padding: 20px;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.oxa-preview-frame {
|
|
830
|
+
width: 100%;
|
|
831
|
+
height: calc(100vh - 106px);
|
|
832
|
+
border: 0;
|
|
833
|
+
border-radius: 8px;
|
|
834
|
+
background: #fff;
|
|
835
|
+
box-shadow: 0 8px 32px rgb(24 42 72 / 10%);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
.oxa-preview-image {
|
|
839
|
+
max-width: 100%;
|
|
840
|
+
max-height: calc(100vh - 106px);
|
|
841
|
+
border-radius: 8px;
|
|
842
|
+
object-fit: contain;
|
|
843
|
+
box-shadow: 0 8px 32px rgb(24 42 72 / 12%);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.oxa-docx-preview,
|
|
847
|
+
.oxa-spreadsheet-preview {
|
|
848
|
+
width: 100%;
|
|
849
|
+
height: calc(100vh - 106px);
|
|
850
|
+
overflow: auto;
|
|
851
|
+
border-radius: 8px;
|
|
852
|
+
background: #fff;
|
|
853
|
+
box-shadow: 0 8px 32px rgb(24 42 72 / 10%);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.oxa-docx-preview .docx-wrapper {
|
|
857
|
+
min-height: 100%;
|
|
858
|
+
background: #dfe4eb;
|
|
859
|
+
padding: 24px;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.oxa-spreadsheet-preview {
|
|
863
|
+
display: flex;
|
|
864
|
+
flex-direction: column;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.oxa-spreadsheet-preview > .ant-segmented {
|
|
868
|
+
flex: 0 0 auto;
|
|
869
|
+
margin: 12px;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.oxa-spreadsheet-scroll {
|
|
873
|
+
min-height: 0;
|
|
874
|
+
flex: 1;
|
|
875
|
+
overflow: auto;
|
|
876
|
+
border-top: 1px solid #e5eaf1;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.oxa-spreadsheet-scroll table {
|
|
880
|
+
min-width: 100%;
|
|
881
|
+
border-spacing: 0;
|
|
882
|
+
border-collapse: separate;
|
|
883
|
+
font-size: 13px;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.oxa-spreadsheet-scroll th,
|
|
887
|
+
.oxa-spreadsheet-scroll td {
|
|
888
|
+
min-width: 96px;
|
|
889
|
+
max-width: 360px;
|
|
890
|
+
border-right: 1px solid #e5eaf1;
|
|
891
|
+
border-bottom: 1px solid #e5eaf1;
|
|
892
|
+
padding: 7px 9px;
|
|
893
|
+
background: #fff;
|
|
894
|
+
text-align: left;
|
|
895
|
+
vertical-align: top;
|
|
896
|
+
white-space: pre-wrap;
|
|
897
|
+
word-break: break-word;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.oxa-spreadsheet-scroll th {
|
|
901
|
+
position: sticky;
|
|
902
|
+
z-index: 1;
|
|
903
|
+
left: 0;
|
|
904
|
+
min-width: 52px;
|
|
905
|
+
background: #f5f7fa;
|
|
906
|
+
color: #7b879b;
|
|
907
|
+
text-align: center;
|
|
908
|
+
}
|
|
909
|
+
|
|
771
910
|
.oxa-detail-hero-top {
|
|
772
911
|
align-items: flex-start;
|
|
773
912
|
}
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
browseDepartmentTree,
|
|
11
11
|
browseDepartmentUsers,
|
|
12
12
|
instrumentDataApi,
|
|
13
|
+
fetchDataFileBlob,
|
|
14
|
+
loadDataFilePreview,
|
|
13
15
|
loadCurrentIdentity,
|
|
14
16
|
resolveCollegeScope,
|
|
15
17
|
resolveDirectory,
|
|
@@ -19,6 +21,7 @@ import {
|
|
|
19
21
|
import {
|
|
20
22
|
applicationApiPath,
|
|
21
23
|
readRuntimeMount,
|
|
24
|
+
resolveAttachmentPreviewPath,
|
|
22
25
|
resolveApplicationApiPath,
|
|
23
26
|
resolveApplicationBasename,
|
|
24
27
|
} from '../src/runtime-meta';
|
|
@@ -65,6 +68,14 @@ test('application shell and instrument surfaces match the approved navigation mo
|
|
|
65
68
|
new URL('../src/components/platform-fields/PlatformDirectoryPicker.tsx', import.meta.url),
|
|
66
69
|
'utf8'
|
|
67
70
|
);
|
|
71
|
+
const attachment = readFileSync(
|
|
72
|
+
new URL('../src/components/platform-fields/AttachmentFileList.tsx', import.meta.url),
|
|
73
|
+
'utf8'
|
|
74
|
+
);
|
|
75
|
+
const preview = readFileSync(
|
|
76
|
+
new URL('../src/FilePreviewPage.tsx', import.meta.url),
|
|
77
|
+
'utf8'
|
|
78
|
+
);
|
|
68
79
|
for (const marker of [
|
|
69
80
|
'仪器资源管理',
|
|
70
81
|
'工作台',
|
|
@@ -92,6 +103,13 @@ test('application shell and instrument surfaces match the approved navigation mo
|
|
|
92
103
|
assert.match(form, /PlatformDirectoryPicker/);
|
|
93
104
|
assert.match(form, /oxa-file-dropzone/);
|
|
94
105
|
assert.match(form, /oxa-detail-layout/);
|
|
106
|
+
assert.match(routes, /path="\/files\/:resourceCode\/:fileId\/preview"/);
|
|
107
|
+
assert.match(attachment, /Image\.PreviewGroup/);
|
|
108
|
+
assert.match(attachment, /attachmentPreviewPath/);
|
|
109
|
+
assert.match(preview, /import\('docx-preview'\)/);
|
|
110
|
+
assert.match(preview, /import\('xlsx'\)/);
|
|
111
|
+
assert.match(preview, /只读预览/);
|
|
112
|
+
assert.doesNotMatch(form, /instrumentFileContentUrl|window\.open/);
|
|
95
113
|
assert.doesNotMatch(
|
|
96
114
|
`${form}\n${directory}`,
|
|
97
115
|
/FormContext|tools\/openxiangda|RoleSession/
|
|
@@ -253,6 +271,92 @@ test('deployed app/environment come only from platform-injected runtime meta', (
|
|
|
253
271
|
resolveApplicationApiPath(mount, 'api/instruments/context'),
|
|
254
272
|
'/service/openxiangda-app-api/v2/instrument-center/production/api/instruments/context'
|
|
255
273
|
);
|
|
274
|
+
assert.equal(
|
|
275
|
+
resolveAttachmentPreviewPath(
|
|
276
|
+
mount,
|
|
277
|
+
'instruments',
|
|
278
|
+
'77777777-7777-4777-8777-777777777777'
|
|
279
|
+
),
|
|
280
|
+
'/view/instrument-center/files/instruments/77777777-7777-4777-8777-777777777777/preview'
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('managed file preview reads metadata and bytes without a RoleSession', async () => {
|
|
285
|
+
const calls: Array<{ path: string; init?: RequestInit }> = [];
|
|
286
|
+
const originalFetch = globalThis.fetch;
|
|
287
|
+
globalThis.fetch = async (input, init) => {
|
|
288
|
+
const path = String(input);
|
|
289
|
+
calls.push({ path, init });
|
|
290
|
+
if (path.endsWith('/dev-sessions/current')) {
|
|
291
|
+
return Response.json({
|
|
292
|
+
code: 200,
|
|
293
|
+
data: {
|
|
294
|
+
environment: { id: 'test-id', key: 'preproduction' },
|
|
295
|
+
principal: {
|
|
296
|
+
userId,
|
|
297
|
+
roleCodes: ['school_admin'],
|
|
298
|
+
capabilityCodes: [],
|
|
299
|
+
isAppSuperAdmin: false,
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (path.includes('/files/file-1/preview?')) {
|
|
305
|
+
return Response.json({
|
|
306
|
+
code: 200,
|
|
307
|
+
data: {
|
|
308
|
+
schemaVersion: 'openxiangda.data-file-preview/v2',
|
|
309
|
+
resourceCode: 'instruments',
|
|
310
|
+
file: {
|
|
311
|
+
schemaVersion: 'openxiangda.data-file-ref/v2',
|
|
312
|
+
id: 'file-1',
|
|
313
|
+
name: 'manual.docx',
|
|
314
|
+
size: 12,
|
|
315
|
+
contentType:
|
|
316
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
317
|
+
},
|
|
318
|
+
extension: 'docx',
|
|
319
|
+
previewType: 'office',
|
|
320
|
+
renderMode: 'docx-html',
|
|
321
|
+
previewSurface: 'document',
|
|
322
|
+
previewProvider: 'browser',
|
|
323
|
+
canPreview: true,
|
|
324
|
+
canDownload: true,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (path.includes('/files/file-1/content?')) {
|
|
329
|
+
return new Response(new Uint8Array([80, 75, 3, 4]), {
|
|
330
|
+
status: 200,
|
|
331
|
+
headers: {
|
|
332
|
+
'content-type':
|
|
333
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return Response.json({ code: 404, message: 'not mocked' }, { status: 404 });
|
|
338
|
+
};
|
|
339
|
+
try {
|
|
340
|
+
await loadCurrentIdentity();
|
|
341
|
+
const preview = await loadDataFilePreview('instruments', 'file-1');
|
|
342
|
+
const blob = await fetchDataFileBlob('instruments', 'file-1');
|
|
343
|
+
assert.equal(preview.renderMode, 'docx-html');
|
|
344
|
+
assert.equal(blob.size, 4);
|
|
345
|
+
} finally {
|
|
346
|
+
globalThis.fetch = originalFetch;
|
|
347
|
+
}
|
|
348
|
+
const previewCall = calls.find(call => call.path.includes('/files/file-1/preview?'))!;
|
|
349
|
+
const contentCall = calls.find(call => call.path.includes('/files/file-1/content?'))!;
|
|
350
|
+
assert.match(previewCall.path, /environmentKey=preproduction/);
|
|
351
|
+
assert.match(contentCall.path, /disposition=inline/);
|
|
352
|
+
assert.equal(
|
|
353
|
+
calls.some(call =>
|
|
354
|
+
Object.keys(call.init?.headers || {}).some(header =>
|
|
355
|
+
header.toLowerCase().includes('role-session')
|
|
356
|
+
)
|
|
357
|
+
),
|
|
358
|
+
false
|
|
359
|
+
);
|
|
256
360
|
});
|
|
257
361
|
|
|
258
362
|
test('list uses the native Data API with server paging, filters, and sorting', async () => {
|
package/template/package.json
CHANGED
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"build": "pnpm --recursive build"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"openxiangda-cli": "2.0.0-alpha.
|
|
25
|
-
"openxiangda-contracts": "2.0.0-alpha.
|
|
26
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
24
|
+
"openxiangda-cli": "2.0.0-alpha.59",
|
|
25
|
+
"openxiangda-contracts": "2.0.0-alpha.28",
|
|
26
|
+
"openxiangda-devkit-core": "2.0.0-alpha.35",
|
|
27
27
|
"typescript": "5.9.3"
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -16,7 +16,7 @@ function visit(directory) {
|
|
|
16
16
|
}
|
|
17
17
|
visit(root);
|
|
18
18
|
const bytes = files.reduce((total, file) => total + statSync(file).size, 0);
|
|
19
|
-
if (files.length >
|
|
19
|
+
if (files.length > 52) throw new Error(`TEMPLATE_SOURCE_FILE_BUDGET_EXCEEDED:${files.length}>52`);
|
|
20
20
|
if (bytes > 500_000) throw new Error(`TEMPLATE_SOURCE_BYTE_BUDGET_EXCEEDED:${bytes}>500000`);
|
|
21
21
|
const productionFiles = files.filter(file => {
|
|
22
22
|
const name = relative(root, file).replaceAll('\\', '/');
|