openxiangda-cli 2.0.0-alpha.59 → 2.0.0-alpha.61
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/dist/create-workspace.d.ts +1 -0
- package/dist/create-workspace.d.ts.map +1 -1
- package/package.json +4 -4
- package/template/apps/server/package.json +1 -1
- package/template/apps/web/e2e/instruments.spec.ts +1 -1
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/scripts/check.mjs +5 -2
- package/template/apps/web/src/InstrumentDetailPage.tsx +119 -77
- package/template/apps/web/src/InstrumentForm.tsx +119 -314
- package/template/apps/web/src/InstrumentFormPage.tsx +40 -75
- package/template/apps/web/src/InstrumentListPage.tsx +264 -192
- package/template/apps/web/src/components/resource/GeneratedResourceCrud.tsx +285 -0
- package/template/apps/web/src/components/resource/StandardResourcePages.tsx +431 -0
- package/template/apps/web/src/components/resource/SurfaceFields.tsx +469 -0
- package/template/apps/web/src/data-provider.ts +85 -1
- package/template/apps/web/src/fields.ts +9 -74
- package/template/apps/web/src/instrument.ts +1 -0
- package/template/apps/web/src/main.tsx +32 -2
- package/template/apps/web/src/platform-client.ts +95 -2
- package/template/apps/web/src/styles.css +97 -0
- package/template/apps/web/test/contracts.test.ts +80 -4
- package/template/package.json +3 -3
- package/template/packages/contracts/src/generated.ts +674 -0
- package/template/platform/data/instrument-surface.ts +125 -0
- package/template/platform/data/instruments.ts +2 -0
|
@@ -4,8 +4,9 @@ import zhCN from 'antd/locale/zh_CN';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import ReactDOM from 'react-dom/client';
|
|
6
6
|
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
|
7
|
-
import {
|
|
7
|
+
import { applicationProvider } from './data-provider';
|
|
8
8
|
import { CollegePage } from './CollegePage';
|
|
9
|
+
import { GeneratedResourcePage } from './components/resource/GeneratedResourceCrud';
|
|
9
10
|
import { FilePreviewPage } from './FilePreviewPage';
|
|
10
11
|
import { InstrumentDetailPage } from './InstrumentDetailPage';
|
|
11
12
|
import { InstrumentFormPage } from './InstrumentFormPage';
|
|
@@ -13,6 +14,7 @@ import { InstrumentListPage } from './InstrumentListPage';
|
|
|
13
14
|
import { RuntimeBoundary } from './runtime';
|
|
14
15
|
import { applicationBasename } from './runtime-meta';
|
|
15
16
|
import './styles.css';
|
|
17
|
+
import { resourceDefinitions } from '../../../packages/contracts/src/generated.js';
|
|
16
18
|
|
|
17
19
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
18
20
|
<React.StrictMode>
|
|
@@ -37,7 +39,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
37
39
|
<RuntimeBoundary>
|
|
38
40
|
<BrowserRouter basename={applicationBasename()}>
|
|
39
41
|
<Refine
|
|
40
|
-
dataProvider={
|
|
42
|
+
dataProvider={applicationProvider}
|
|
41
43
|
resources={[
|
|
42
44
|
{
|
|
43
45
|
name: 'instruments',
|
|
@@ -51,6 +53,10 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
51
53
|
<Routes>
|
|
52
54
|
<Route path="/" element={<Navigate replace to="/instruments" />} />
|
|
53
55
|
<Route path="/instruments" element={<InstrumentListPage />} />
|
|
56
|
+
<Route
|
|
57
|
+
path="/m/instruments"
|
|
58
|
+
element={<InstrumentListPage variant="mobile" />}
|
|
59
|
+
/>
|
|
54
60
|
<Route path="/colleges" element={<CollegePage />} />
|
|
55
61
|
<Route
|
|
56
62
|
path="/files/:resourceCode/:fileId/preview"
|
|
@@ -68,6 +74,30 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
68
74
|
path="/instruments/:id"
|
|
69
75
|
element={<InstrumentDetailPage />}
|
|
70
76
|
/>
|
|
77
|
+
<Route
|
|
78
|
+
path="/m/instruments/:id"
|
|
79
|
+
element={<InstrumentDetailPage variant="mobile" />}
|
|
80
|
+
/>
|
|
81
|
+
<Route
|
|
82
|
+
path="/m/instruments/new"
|
|
83
|
+
element={<InstrumentFormPage mode="create" variant="mobile" />}
|
|
84
|
+
/>
|
|
85
|
+
<Route
|
|
86
|
+
path="/m/instruments/:id/edit"
|
|
87
|
+
element={<InstrumentFormPage mode="edit" variant="mobile" />}
|
|
88
|
+
/>
|
|
89
|
+
{Object.entries(resourceDefinitions)
|
|
90
|
+
.filter(([, definition]) => definition.surface.generated !== false)
|
|
91
|
+
.flatMap(([resourceCode]) => [
|
|
92
|
+
<Route key={`${resourceCode}-list`} path={`/${resourceCode}`} element={<GeneratedResourcePage resourceCode={resourceCode} />} />,
|
|
93
|
+
<Route key={`${resourceCode}-new`} path={`/${resourceCode}/new`} element={<GeneratedResourcePage mode="create" resourceCode={resourceCode} />} />,
|
|
94
|
+
<Route key={`${resourceCode}-detail`} path={`/${resourceCode}/:id`} element={<GeneratedResourcePage mode="detail" resourceCode={resourceCode} />} />,
|
|
95
|
+
<Route key={`${resourceCode}-edit`} path={`/${resourceCode}/:id/edit`} element={<GeneratedResourcePage mode="edit" resourceCode={resourceCode} />} />,
|
|
96
|
+
<Route key={`${resourceCode}-mobile-list`} path={`/m/${resourceCode}`} element={<GeneratedResourcePage resourceCode={resourceCode} variant="mobile" />} />,
|
|
97
|
+
<Route key={`${resourceCode}-mobile-new`} path={`/m/${resourceCode}/new`} element={<GeneratedResourcePage mode="create" resourceCode={resourceCode} variant="mobile" />} />,
|
|
98
|
+
<Route key={`${resourceCode}-mobile-detail`} path={`/m/${resourceCode}/:id`} element={<GeneratedResourcePage mode="detail" resourceCode={resourceCode} variant="mobile" />} />,
|
|
99
|
+
<Route key={`${resourceCode}-mobile-edit`} path={`/m/${resourceCode}/:id/edit`} element={<GeneratedResourcePage mode="edit" resourceCode={resourceCode} variant="mobile" />} />,
|
|
100
|
+
])}
|
|
71
101
|
</Routes>
|
|
72
102
|
</Refine>
|
|
73
103
|
</BrowserRouter>
|
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
type DirectoryResolveRequest,
|
|
12
12
|
type NativeScopeValuePage,
|
|
13
13
|
type NativeScopeValueResolveRequest,
|
|
14
|
+
type DataResourceSurface,
|
|
14
15
|
} from 'openxiangda-contracts/browser';
|
|
15
16
|
import type { InstrumentListQuery, InstrumentRecord } from './instrument';
|
|
16
|
-
import { isInstrumentResourceField } from './fields';
|
|
17
|
+
import { instrumentSurface, isInstrumentResourceField } from './fields';
|
|
17
18
|
import { applicationCode, runtimeMount } from './runtime-meta';
|
|
18
19
|
|
|
19
20
|
const resourceCode = 'instruments';
|
|
@@ -273,7 +274,11 @@ export const instrumentDataApi: InstrumentDataApiAdapter = {
|
|
|
273
274
|
// The current DataQuery contract has AND filters but no grouped OR. Keep
|
|
274
275
|
// this honest single-field search until the platform adds a native OR AST.
|
|
275
276
|
if (query.keyword?.trim()) {
|
|
276
|
-
|
|
277
|
+
const searchableFields = instrumentSurface.list?.searchableFields || [];
|
|
278
|
+
const field = searchableFields.includes(query.searchField || '')
|
|
279
|
+
? query.searchField!
|
|
280
|
+
: searchableFields[0] || 'chineseName';
|
|
281
|
+
filters.push({ field, operator: 'ilike', value: `%${query.keyword.trim()}%` });
|
|
277
282
|
}
|
|
278
283
|
if (query.collegeId) filters.push({ field: 'collegeId', operator: 'eq', value: query.collegeId });
|
|
279
284
|
if (query.instrumentAdminId) filters.push({ field: 'instrumentAdminIds', operator: 'cs', value: [query.instrumentAdminId] });
|
|
@@ -378,6 +383,94 @@ export const instrumentDataApi: InstrumentDataApiAdapter = {
|
|
|
378
383
|
},
|
|
379
384
|
};
|
|
380
385
|
|
|
386
|
+
export interface GenericResourceQuery {
|
|
387
|
+
page: number;
|
|
388
|
+
pageSize: number;
|
|
389
|
+
keyword?: string;
|
|
390
|
+
searchField?: string;
|
|
391
|
+
filters?: Record<string, unknown>;
|
|
392
|
+
sort?: { field: string; order: 'asc' | 'desc' };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Native CRUD adapter used by resources that opt into generated pages. */
|
|
396
|
+
export function createNativeResourceClient(
|
|
397
|
+
code: string,
|
|
398
|
+
surface: DataResourceSurface
|
|
399
|
+
) {
|
|
400
|
+
const base = dataBase(code);
|
|
401
|
+
const declaredFields = new Set(Object.keys(surface.fields));
|
|
402
|
+
const assertField = (field: string) => {
|
|
403
|
+
if (!declaredFields.has(field)) {
|
|
404
|
+
throw new Error(`OPENXIANGDA_RESOURCE_QUERY_FIELD_NOT_DECLARED:${code}:${field}`);
|
|
405
|
+
}
|
|
406
|
+
return field;
|
|
407
|
+
};
|
|
408
|
+
const filtersFor = (query: GenericResourceQuery) => {
|
|
409
|
+
const filters: NonNullable<DataQuery['filters']> = [];
|
|
410
|
+
if (query.keyword?.trim()) {
|
|
411
|
+
const searchable = surface.list?.searchableFields || [];
|
|
412
|
+
const field = searchable.includes(query.searchField || '') ? query.searchField! : searchable[0];
|
|
413
|
+
if (field) filters.push({ field: assertField(field), operator: 'ilike', value: `%${query.keyword.trim()}%` });
|
|
414
|
+
}
|
|
415
|
+
for (const [fieldCode, value] of Object.entries(query.filters || {})) {
|
|
416
|
+
if (value === undefined || value === null || value === '') continue;
|
|
417
|
+
const field = surface.fields[assertField(fieldCode)];
|
|
418
|
+
if (Array.isArray(value) && value.length === 2 && ['date', 'datetime', 'number', 'money', 'percent'].includes(field?.widget || '')) {
|
|
419
|
+
if (value[0] !== undefined && value[0] !== null && value[0] !== '') filters.push({ field: fieldCode, operator: 'gte', value: value[0] });
|
|
420
|
+
if (value[1] !== undefined && value[1] !== null && value[1] !== '') filters.push({ field: fieldCode, operator: 'lte', value: value[1] });
|
|
421
|
+
} else if (Array.isArray(value) && ['multi', 'directory-user', 'directory-department'].includes(field?.widget || '')) {
|
|
422
|
+
filters.push({ field: fieldCode, operator: 'cs', value });
|
|
423
|
+
} else {
|
|
424
|
+
filters.push({ field: fieldCode, operator: 'eq', value });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return filters;
|
|
428
|
+
};
|
|
429
|
+
return {
|
|
430
|
+
async list(query: GenericResourceQuery) {
|
|
431
|
+
const sort = query.sort || surface.list?.defaultSort || { field: Object.keys(surface.fields)[0] || 'id', order: 'asc' as const };
|
|
432
|
+
const body: DataQuery = {
|
|
433
|
+
schemaVersion: SCHEMA_VERSIONS.dataQuery,
|
|
434
|
+
filters: filtersFor(query),
|
|
435
|
+
order: [{ field: assertField(sort.field), direction: sort.order || 'asc' }],
|
|
436
|
+
limit: query.pageSize,
|
|
437
|
+
offset: (query.page - 1) * query.pageSize,
|
|
438
|
+
};
|
|
439
|
+
const page = await request<DataPage<Record<string, unknown>>>(`${base}/query`, {
|
|
440
|
+
method: 'POST',
|
|
441
|
+
body: JSON.stringify({ ...body, environmentKey: currentEnvironmentKey() }),
|
|
442
|
+
});
|
|
443
|
+
return { rows: page.items, total: page.total };
|
|
444
|
+
},
|
|
445
|
+
async get(id: string) {
|
|
446
|
+
const record = await request<DataRecord<Record<string, unknown>>>(`${base}/records/${encodeURIComponent(id)}?environmentKey=${encodeURIComponent(currentEnvironmentKey())}`);
|
|
447
|
+
return record.data;
|
|
448
|
+
},
|
|
449
|
+
async audit(id: string) {
|
|
450
|
+
return await request<DataAuditPage>(`${base}/records/${encodeURIComponent(id)}/audit?limit=10&offset=0&environmentKey=${encodeURIComponent(currentEnvironmentKey())}`);
|
|
451
|
+
},
|
|
452
|
+
async create(data: Record<string, unknown>) {
|
|
453
|
+
const record = await request<DataRecord<Record<string, unknown>>>(`${base}/records`, { method: 'POST', body: JSON.stringify({ environmentKey: currentEnvironmentKey(), data }) });
|
|
454
|
+
return record.data;
|
|
455
|
+
},
|
|
456
|
+
async update(id: string, expectedRevision: number, data: Record<string, unknown>) {
|
|
457
|
+
const record = await request<DataRecord<Record<string, unknown>>>(`${base}/records/${encodeURIComponent(id)}/update`, { method: 'POST', body: JSON.stringify({ environmentKey: currentEnvironmentKey(), expectedRevision, data }) });
|
|
458
|
+
return record.data;
|
|
459
|
+
},
|
|
460
|
+
async remove(id: string, expectedRevision: number) {
|
|
461
|
+
const record = await request<DataRecord<Record<string, unknown>>>(`${base}/records/${encodeURIComponent(id)}/delete`, { method: 'POST', body: JSON.stringify({ environmentKey: currentEnvironmentKey(), expectedRevision }) });
|
|
462
|
+
return record.data;
|
|
463
|
+
},
|
|
464
|
+
async upload(fieldCode: string, file: File, recordId?: string) {
|
|
465
|
+
assertField(fieldCode);
|
|
466
|
+
const plan = await request<DataFileUploadPlan>(`${base}/files/uploads/initiate`, { method: 'POST', body: JSON.stringify({ environmentKey: currentEnvironmentKey(), fieldCode, fileName: file.name, fileSize: file.size, contentType: file.type || 'application/octet-stream', ...(recordId ? { recordId } : {}) }) });
|
|
467
|
+
const uploaded = await fetch(plan.uploadUrl, { method: plan.uploadMethod, headers: plan.headers, body: file });
|
|
468
|
+
if (!uploaded.ok) throw new Error(`FILE_UPLOAD_FAILED: HTTP_${uploaded.status}`);
|
|
469
|
+
return await request<DataFileRef>(`${base}/files/${encodeURIComponent(plan.file.id)}/complete`, { method: 'POST', body: JSON.stringify({ environmentKey: currentEnvironmentKey() }) });
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
381
474
|
export function dataFileContentUrl(
|
|
382
475
|
resource: string,
|
|
383
476
|
fileId: string,
|
|
@@ -218,6 +218,103 @@ body {
|
|
|
218
218
|
font-size: 12px;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
.oxa-mobile-page {
|
|
222
|
+
min-height: 100vh;
|
|
223
|
+
box-sizing: border-box;
|
|
224
|
+
background: #f3f7fd;
|
|
225
|
+
padding: 16px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.oxa-mobile-header {
|
|
229
|
+
display: grid;
|
|
230
|
+
gap: 6px;
|
|
231
|
+
margin-bottom: 14px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.oxa-mobile-header .ant-typography {
|
|
235
|
+
margin: 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.oxa-mobile-form {
|
|
239
|
+
margin-top: 14px;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.oxa-mobile-field .ant-form-item-control-input-content {
|
|
243
|
+
min-height: 44px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.oxa-mobile-input {
|
|
247
|
+
width: 100%;
|
|
248
|
+
min-height: 42px;
|
|
249
|
+
box-sizing: border-box;
|
|
250
|
+
border: 1px solid #d9e2f0;
|
|
251
|
+
border-radius: 8px;
|
|
252
|
+
background: #fff;
|
|
253
|
+
padding: 9px 11px;
|
|
254
|
+
color: #172033;
|
|
255
|
+
font: inherit;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.oxa-mobile-input:focus {
|
|
259
|
+
border-color: #1677ff;
|
|
260
|
+
outline: 2px solid rgb(22 119 255 / 15%);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.oxa-mobile-filter,
|
|
264
|
+
.oxa-mobile-pagination,
|
|
265
|
+
.oxa-mobile-record-heading,
|
|
266
|
+
.oxa-mobile-record-fields {
|
|
267
|
+
display: grid;
|
|
268
|
+
gap: 10px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.oxa-mobile-filter {
|
|
272
|
+
margin: 14px 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.oxa-mobile-record-list {
|
|
276
|
+
display: grid;
|
|
277
|
+
gap: 12px;
|
|
278
|
+
margin-top: 14px;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.oxa-mobile-record-card {
|
|
282
|
+
border-color: #dfe6ef;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.oxa-mobile-record-heading {
|
|
286
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
287
|
+
align-items: center;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.oxa-mobile-record-heading a {
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
color: #1677ff;
|
|
293
|
+
font-size: 16px;
|
|
294
|
+
font-weight: 650;
|
|
295
|
+
text-overflow: ellipsis;
|
|
296
|
+
white-space: nowrap;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.oxa-mobile-record-fields {
|
|
300
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
301
|
+
margin: 12px 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.oxa-mobile-record-fields > div {
|
|
305
|
+
min-width: 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.oxa-mobile-pagination {
|
|
309
|
+
grid-template-columns: 1fr auto 1fr;
|
|
310
|
+
align-items: center;
|
|
311
|
+
margin-top: 14px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.oxa-mobile-pagination .ant-btn:last-child {
|
|
315
|
+
justify-self: end;
|
|
316
|
+
}
|
|
317
|
+
|
|
221
318
|
.oxa-current-user-chevron {
|
|
222
319
|
color: #65748a;
|
|
223
320
|
font-size: 12px;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
instrumentFieldAllowed,
|
|
6
6
|
sanitizeInstrumentValues,
|
|
7
7
|
} from '../src/InstrumentForm';
|
|
8
|
-
import { instrumentFields } from '../src/fields';
|
|
8
|
+
import { instrumentFields, instrumentSurface } from '../src/fields';
|
|
9
9
|
import {
|
|
10
10
|
browseDepartmentTree,
|
|
11
11
|
browseDepartmentUsers,
|
|
@@ -49,6 +49,43 @@ test('the golden module exposes exactly 30 fields and five restricted fields', (
|
|
|
49
49
|
);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
test('the instrument list, search, and filters are declared by the resource surface', () => {
|
|
53
|
+
assert.deepEqual(
|
|
54
|
+
Object.entries(instrumentSurface.fields)
|
|
55
|
+
.filter(([, field]) => field.list)
|
|
56
|
+
.map(([key]) => key)
|
|
57
|
+
.sort(),
|
|
58
|
+
[
|
|
59
|
+
'chineseName',
|
|
60
|
+
'collegeId',
|
|
61
|
+
'image',
|
|
62
|
+
'instrumentAdminIds',
|
|
63
|
+
'instrumentCode',
|
|
64
|
+
'instrumentStatus',
|
|
65
|
+
'openToOutside',
|
|
66
|
+
'specModel',
|
|
67
|
+
'assetCode',
|
|
68
|
+
].sort()
|
|
69
|
+
);
|
|
70
|
+
assert.deepEqual(instrumentSurface.list?.searchableFields, [
|
|
71
|
+
'chineseName',
|
|
72
|
+
'instrumentCode',
|
|
73
|
+
'assetCode',
|
|
74
|
+
]);
|
|
75
|
+
assert.ok(instrumentSurface.list?.filterFields?.includes('assetValue'));
|
|
76
|
+
const list = readFileSync(
|
|
77
|
+
new URL('../src/InstrumentListPage.tsx', import.meta.url),
|
|
78
|
+
'utf8'
|
|
79
|
+
);
|
|
80
|
+
const client = readFileSync(
|
|
81
|
+
new URL('../src/platform-client.ts', import.meta.url),
|
|
82
|
+
'utf8'
|
|
83
|
+
);
|
|
84
|
+
assert.match(list, /listSurfaceFields/);
|
|
85
|
+
assert.match(list, /searchableFields/);
|
|
86
|
+
assert.match(client, /query\.searchField/);
|
|
87
|
+
});
|
|
88
|
+
|
|
52
89
|
test('application shell and instrument surfaces match the approved navigation model', () => {
|
|
53
90
|
const shell = readFileSync(new URL('../src/Shell.tsx', import.meta.url), 'utf8');
|
|
54
91
|
const list = readFileSync(
|
|
@@ -60,6 +97,10 @@ test('application shell and instrument surfaces match the approved navigation mo
|
|
|
60
97
|
new URL('../src/InstrumentForm.tsx', import.meta.url),
|
|
61
98
|
'utf8'
|
|
62
99
|
);
|
|
100
|
+
const formPage = readFileSync(
|
|
101
|
+
new URL('../src/InstrumentFormPage.tsx', import.meta.url),
|
|
102
|
+
'utf8'
|
|
103
|
+
);
|
|
63
104
|
const colleges = readFileSync(
|
|
64
105
|
new URL('../src/CollegePage.tsx', import.meta.url),
|
|
65
106
|
'utf8'
|
|
@@ -72,10 +113,22 @@ test('application shell and instrument surfaces match the approved navigation mo
|
|
|
72
113
|
new URL('../src/components/platform-fields/AttachmentFileList.tsx', import.meta.url),
|
|
73
114
|
'utf8'
|
|
74
115
|
);
|
|
116
|
+
const surfaceFields = readFileSync(
|
|
117
|
+
new URL('../src/components/resource/SurfaceFields.tsx', import.meta.url),
|
|
118
|
+
'utf8'
|
|
119
|
+
);
|
|
75
120
|
const preview = readFileSync(
|
|
76
121
|
new URL('../src/FilePreviewPage.tsx', import.meta.url),
|
|
77
122
|
'utf8'
|
|
78
123
|
);
|
|
124
|
+
const standardPages = readFileSync(
|
|
125
|
+
new URL('../src/components/resource/StandardResourcePages.tsx', import.meta.url),
|
|
126
|
+
'utf8'
|
|
127
|
+
);
|
|
128
|
+
const generatedCrud = readFileSync(
|
|
129
|
+
new URL('../src/components/resource/GeneratedResourceCrud.tsx', import.meta.url),
|
|
130
|
+
'utf8'
|
|
131
|
+
);
|
|
79
132
|
for (const marker of [
|
|
80
133
|
'仪器资源管理',
|
|
81
134
|
'工作台',
|
|
@@ -94,15 +147,35 @@ test('application shell and instrument surfaces match the approved navigation mo
|
|
|
94
147
|
assert.doesNotMatch(shell, /验证本地 Nest|roleCodes\.join|environment\.key/);
|
|
95
148
|
assert.doesNotMatch(list, /<Drawer|surface="drawer"/);
|
|
96
149
|
assert.match(routes, /path="\/instruments\/new"[\s\S]*<InstrumentFormPage mode="create"/);
|
|
150
|
+
assert.match(routes, /path="\/m\/instruments\/new"[\s\S]*variant="mobile"/);
|
|
151
|
+
assert.match(routes, /path="\/m\/instruments"[\s\S]*variant="mobile"/);
|
|
152
|
+
assert.match(routes, /path="\/m\/instruments\/:id"[\s\S]*variant="mobile"/);
|
|
153
|
+
assert.match(routes, /path="\/m\/instruments\/:id\/edit"[\s\S]*variant="mobile"/);
|
|
97
154
|
assert.match(colleges, /<Drawer/);
|
|
98
155
|
assert.match(colleges, /size=\{520\}/);
|
|
99
156
|
assert.match(directory, /width=\{kind === 'user' \? 980 : 760\}/);
|
|
100
157
|
assert.match(directory, /browseDepartmentTree|browseDepartmentUsers/);
|
|
101
158
|
assert.match(directory, /选择成员|选择部门|组织架构|已选/);
|
|
102
159
|
assert.match(form, /oxa-form-full/);
|
|
103
|
-
assert.match(form, /
|
|
104
|
-
assert.match(
|
|
160
|
+
assert.match(form, /SurfaceFieldControl/);
|
|
161
|
+
assert.match(surfaceFields, /PlatformDirectoryPicker/);
|
|
162
|
+
assert.match(surfaceFields, /oxa-file-dropzone/);
|
|
105
163
|
assert.match(form, /oxa-detail-layout/);
|
|
164
|
+
assert.match(list, /<ResourceListPage/);
|
|
165
|
+
assert.match(formPage, /ResourceFormPage/);
|
|
166
|
+
assert.match(formPage, /MobileResourceFormPage/);
|
|
167
|
+
assert.match(list, /MobileResourceListPage/);
|
|
168
|
+
assert.match(surfaceFields, /SurfaceFilterControl/);
|
|
169
|
+
assert.match(form, /MobileSurfaceFieldControl/);
|
|
170
|
+
assert.match(
|
|
171
|
+
readFileSync(new URL('../src/InstrumentDetailPage.tsx', import.meta.url), 'utf8'),
|
|
172
|
+
/<ResourceDetailPage/
|
|
173
|
+
);
|
|
174
|
+
assert.match(standardPages, /批量事务合同落地后启用/);
|
|
175
|
+
assert.match(standardPages, /DataResourceSurface/);
|
|
176
|
+
assert.match(generatedCrud, /resourceDefinitions/);
|
|
177
|
+
assert.match(generatedCrud, /createNativeResourceClient/);
|
|
178
|
+
assert.match(generatedCrud, /MobileResourceDetailPage/);
|
|
106
179
|
assert.match(routes, /path="\/files\/:resourceCode\/:fileId\/preview"/);
|
|
107
180
|
assert.match(attachment, /Image\.PreviewGroup/);
|
|
108
181
|
assert.match(attachment, /attachmentPreviewPath/);
|
|
@@ -125,7 +198,10 @@ test('college, user, and department selectors have authoritative owners only', (
|
|
|
125
198
|
new URL('../../../platform/data/colleges.ts', import.meta.url),
|
|
126
199
|
'utf8'
|
|
127
200
|
);
|
|
128
|
-
const fields = readFileSync(
|
|
201
|
+
const fields = readFileSync(
|
|
202
|
+
new URL('../../../platform/data/instrument-surface.ts', import.meta.url),
|
|
203
|
+
'utf8'
|
|
204
|
+
);
|
|
129
205
|
assert.match(appConfig, /valueType:\s*'uuid'/);
|
|
130
206
|
assert.match(appConfig, /kind:\s*'native_resource'/);
|
|
131
207
|
assert.match(appConfig, /resourceCode:\s*'colleges'/);
|
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.61",
|
|
25
|
+
"openxiangda-contracts": "2.0.0-alpha.29",
|
|
26
|
+
"openxiangda-devkit-core": "2.0.0-alpha.37",
|
|
27
27
|
"typescript": "5.9.3"
|
|
28
28
|
}
|
|
29
29
|
}
|