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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../src/create-workspace.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,sBAAsB,CAAC;CACxC;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB;;;;;;;;GAkDhE;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,uBAA4B
|
|
1
|
+
{"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../src/create-workspace.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,sBAAsB,CAAC;CACxC;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB;;;;;;;;GAkDhE;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,uBAA4B;;;;;eAoBtC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.61",
|
|
4
4
|
"description": "Thin application-level CLI for OpenXiangda 2.0.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@oclif/core": "4.13.3",
|
|
26
|
-
"openxiangda-contracts": "2.0.0-alpha.
|
|
27
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
28
|
-
"openxiangda-mcp": "2.0.0-alpha.
|
|
26
|
+
"openxiangda-contracts": "2.0.0-alpha.29",
|
|
27
|
+
"openxiangda-devkit-core": "2.0.0-alpha.37",
|
|
28
|
+
"openxiangda-mcp": "2.0.0-alpha.37"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"tsx": "4.23.12",
|
|
@@ -441,7 +441,7 @@ test('continues create, list, detail, update, and delete with declared query fie
|
|
|
441
441
|
.locator('.ant-table-column-sorter')
|
|
442
442
|
).toHaveCount(0);
|
|
443
443
|
const queriesBeforeSort = queryBodies.length;
|
|
444
|
-
await page.getByRole('columnheader', { name:
|
|
444
|
+
await page.getByRole('columnheader', { name: /仪器中文名/ }).click();
|
|
445
445
|
await expect.poll(() => queryBodies.length).toBeGreaterThan(queriesBeforeSort);
|
|
446
446
|
expect(
|
|
447
447
|
(queryBodies.at(-1) as { order: Array<{ field: string; direction: string }> })
|
|
@@ -19,8 +19,11 @@ const lines = files.reduce(
|
|
|
19
19
|
(total, file) => total + readFileSync(file, 'utf8').split(/\r?\n/).length,
|
|
20
20
|
0
|
|
21
21
|
);
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// The shared Surface renderer is deliberately counted as application source;
|
|
23
|
+
// keep its small budget explicit instead of hiding it in generated output.
|
|
24
|
+
if (files.length > 20) throw new Error(`WEB_SOURCE_FILE_BUDGET_EXCEEDED:${files.length}>20`);
|
|
25
|
+
// Mobile shells, Surface filters, and generated resource CRUD are shared application infrastructure.
|
|
26
|
+
if (lines > 5_600) throw new Error(`WEB_BUSINESS_LOC_BUDGET_EXCEEDED:${lines}>5600`);
|
|
24
27
|
const source = files.map(file => readFileSync(file, 'utf8')).join('\n');
|
|
25
28
|
for (const marker of ['@umijs/', 'openxiangda-' + 'admin', 'openxiangda-' + 'user', 'instrument_query', 'instrument_save', 'function.invoke', 'role' + '-session', 'college-' + 'am', 'user-' + 'wang', 'dept-' + 'am-test']) {
|
|
26
29
|
if (source.includes(marker)) throw new Error(`WEB_FORBIDDEN_MARKER:${marker}`);
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ArrowLeftOutlined,
|
|
3
|
-
EditOutlined,
|
|
4
|
-
HistoryOutlined,
|
|
5
|
-
} from '@ant-design/icons';
|
|
6
|
-
import { Button, Card, Result, Space, Spin, Tag, Typography } from 'antd';
|
|
1
|
+
import { Space, Tag, Typography } from 'antd';
|
|
7
2
|
import { useEffect, useState } from 'react';
|
|
8
3
|
import type { DataAuditEntry } from 'openxiangda-contracts/browser';
|
|
9
4
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
5
|
+
import { useOne } from '@refinedev/core';
|
|
6
|
+
import {
|
|
7
|
+
MobileResourceDetailPage,
|
|
8
|
+
ResourceDetailPage,
|
|
9
|
+
} from './components/resource/StandardResourcePages';
|
|
10
10
|
import { InstrumentForm } from './InstrumentForm';
|
|
11
|
-
import { Shell } from './Shell';
|
|
12
11
|
import { INSTRUMENT_CAPABILITIES, type InstrumentRecord } from './instrument';
|
|
12
|
+
import { instrumentSurface } from '../../../platform/data/instrument-surface.js';
|
|
13
13
|
import { instrumentDataApi } from './platform-client';
|
|
14
|
-
import { useRuntime } from './runtime';
|
|
15
|
-
import { useOne } from '@refinedev/core';
|
|
16
14
|
|
|
17
|
-
export function InstrumentDetailPage() {
|
|
15
|
+
export function InstrumentDetailPage({ variant = 'desktop' }: { variant?: 'desktop' | 'mobile' }) {
|
|
16
|
+
return variant === 'mobile' ? <MobileInstrumentDetailPage /> : <DesktopInstrumentDetailPage />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DesktopInstrumentDetailPage() {
|
|
18
20
|
const { id = '' } = useParams();
|
|
19
21
|
const navigate = useNavigate();
|
|
20
|
-
const { hasCapability } = useRuntime();
|
|
21
22
|
const [auditEntries, setAuditEntries] = useState<DataAuditEntry[]>([]);
|
|
22
23
|
const [auditError, setAuditError] = useState('');
|
|
23
24
|
const query = useOne<InstrumentRecord>({
|
|
@@ -43,61 +44,19 @@ export function InstrumentDetailPage() {
|
|
|
43
44
|
active = false;
|
|
44
45
|
};
|
|
45
46
|
}, [record?.id, record?.revision]);
|
|
46
|
-
if (query.query.isLoading) {
|
|
47
|
-
return (
|
|
48
|
-
<Shell>
|
|
49
|
-
<div className="oxa-page-loading">
|
|
50
|
-
<Spin />
|
|
51
|
-
</div>
|
|
52
|
-
</Shell>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (query.query.isError || !record) {
|
|
56
|
-
return (
|
|
57
|
-
<Shell>
|
|
58
|
-
<Result
|
|
59
|
-
status="403"
|
|
60
|
-
subTitle={query.query.error?.message || '记录不存在'}
|
|
61
|
-
title="无法访问"
|
|
62
|
-
/>
|
|
63
|
-
</Shell>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
47
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<Button
|
|
80
|
-
icon={<HistoryOutlined />}
|
|
81
|
-
onClick={() =>
|
|
82
|
-
document
|
|
83
|
-
.getElementById('instrument-audit')
|
|
84
|
-
?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
85
|
-
}
|
|
86
|
-
>
|
|
87
|
-
操作记录
|
|
88
|
-
</Button>
|
|
89
|
-
{hasCapability(INSTRUMENT_CAPABILITIES.update) && (
|
|
90
|
-
<Button
|
|
91
|
-
icon={<EditOutlined />}
|
|
92
|
-
onClick={() => navigate(`/instruments/${id}/edit`)}
|
|
93
|
-
type="primary"
|
|
94
|
-
>
|
|
95
|
-
编辑
|
|
96
|
-
</Button>
|
|
97
|
-
)}
|
|
98
|
-
</Space>
|
|
99
|
-
</div>
|
|
100
|
-
<div className="oxa-detail-hero-main">
|
|
48
|
+
<ResourceDetailPage
|
|
49
|
+
backLabel="返回仪器资源"
|
|
50
|
+
backPath="/instruments"
|
|
51
|
+
editCapability={INSTRUMENT_CAPABILITIES.update}
|
|
52
|
+
editPath={`/instruments/${id}/edit`}
|
|
53
|
+
error={
|
|
54
|
+
query.query.isError || (!query.query.isLoading && !record)
|
|
55
|
+
? query.query.error?.message || '记录不存在'
|
|
56
|
+
: undefined
|
|
57
|
+
}
|
|
58
|
+
hero={
|
|
59
|
+
record ? (
|
|
101
60
|
<div>
|
|
102
61
|
<Space align="center" size={10}>
|
|
103
62
|
<Typography.Title level={2}>仪器详情</Typography.Title>
|
|
@@ -112,20 +71,103 @@ export function InstrumentDetailPage() {
|
|
|
112
71
|
{record.instrumentCode} · {record.specModel}
|
|
113
72
|
</Typography.Text>
|
|
114
73
|
</div>
|
|
115
|
-
|
|
74
|
+
) : null
|
|
75
|
+
}
|
|
76
|
+
loading={query.query.isLoading}
|
|
77
|
+
meta={
|
|
78
|
+
record ? (
|
|
79
|
+
<>
|
|
116
80
|
<span>最近更新 {formatDateTime(record.updated_at)}</span>
|
|
117
81
|
<span>版本 {record.revision}</span>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
82
|
+
</>
|
|
83
|
+
) : null
|
|
84
|
+
}
|
|
85
|
+
readCapability={INSTRUMENT_CAPABILITIES.read}
|
|
86
|
+
resource="instruments"
|
|
87
|
+
surface={instrumentSurface}
|
|
88
|
+
title="仪器"
|
|
89
|
+
auditTargetId="instrument-audit"
|
|
90
|
+
>
|
|
91
|
+
{record && (
|
|
92
|
+
<InstrumentForm
|
|
93
|
+
auditEntries={auditEntries}
|
|
94
|
+
auditError={auditError}
|
|
95
|
+
mode="detail"
|
|
96
|
+
onCancel={() => navigate('/instruments')}
|
|
97
|
+
record={record}
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
100
|
+
</ResourceDetailPage>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function MobileInstrumentDetailPage() {
|
|
105
|
+
const { id = '' } = useParams();
|
|
106
|
+
const navigate = useNavigate();
|
|
107
|
+
const [auditEntries, setAuditEntries] = useState<DataAuditEntry[]>([]);
|
|
108
|
+
const [auditError, setAuditError] = useState('');
|
|
109
|
+
const query = useOne<InstrumentRecord>({
|
|
110
|
+
resource: 'instruments',
|
|
111
|
+
id,
|
|
112
|
+
queryOptions: { retry: false },
|
|
113
|
+
});
|
|
114
|
+
const record = query.result;
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!record?.id) return;
|
|
117
|
+
let active = true;
|
|
118
|
+
setAuditError('');
|
|
119
|
+
void instrumentDataApi.audit(record.id).then(
|
|
120
|
+
page => {
|
|
121
|
+
if (active) setAuditEntries(page.items || []);
|
|
122
|
+
},
|
|
123
|
+
error => {
|
|
124
|
+
if (active) setAuditError(error instanceof Error ? error.message : String(error));
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
return () => {
|
|
128
|
+
active = false;
|
|
129
|
+
};
|
|
130
|
+
}, [record?.id, record?.revision]);
|
|
131
|
+
return (
|
|
132
|
+
<MobileResourceDetailPage
|
|
133
|
+
backLabel="返回仪器资源"
|
|
134
|
+
backPath="/m/instruments"
|
|
135
|
+
editCapability={INSTRUMENT_CAPABILITIES.update}
|
|
136
|
+
editPath={`/m/instruments/${id}/edit`}
|
|
137
|
+
error={query.query.isError || (!query.query.isLoading && !record) ? query.query.error?.message || '记录不存在' : undefined}
|
|
138
|
+
hero={
|
|
139
|
+
record ? (
|
|
140
|
+
<div>
|
|
141
|
+
<Space align="center" size={8}>
|
|
142
|
+
<Typography.Title level={3}>仪器详情</Typography.Title>
|
|
143
|
+
<Tag color={record.instrumentStatus === 'normal' ? 'green' : 'default'}>
|
|
144
|
+
{record.instrumentStatus === 'normal' ? '启用' : '非正常'}
|
|
145
|
+
</Tag>
|
|
146
|
+
</Space>
|
|
147
|
+
<Typography.Title className="oxa-instrument-name" level={4}>{record.chineseName}</Typography.Title>
|
|
148
|
+
<Typography.Text type="secondary">{record.instrumentCode} · {record.specModel}</Typography.Text>
|
|
149
|
+
</div>
|
|
150
|
+
) : null
|
|
151
|
+
}
|
|
152
|
+
loading={query.query.isLoading}
|
|
153
|
+
meta={record ? <><span>最近更新 {formatDateTime(record.updated_at)}</span><span>版本 {record.revision}</span></> : null}
|
|
154
|
+
readCapability={INSTRUMENT_CAPABILITIES.read}
|
|
155
|
+
resource="instruments"
|
|
156
|
+
surface={instrumentSurface}
|
|
157
|
+
title="仪器"
|
|
158
|
+
auditTargetId="instrument-audit"
|
|
159
|
+
>
|
|
160
|
+
{record && (
|
|
161
|
+
<InstrumentForm
|
|
162
|
+
auditEntries={auditEntries}
|
|
163
|
+
auditError={auditError}
|
|
164
|
+
mode="detail"
|
|
165
|
+
onCancel={() => navigate('/m/instruments')}
|
|
166
|
+
record={record}
|
|
167
|
+
variant="mobile"
|
|
168
|
+
/>
|
|
169
|
+
)}
|
|
170
|
+
</MobileResourceDetailPage>
|
|
129
171
|
);
|
|
130
172
|
}
|
|
131
173
|
|