openxiangda-cli 2.0.0-alpha.56 → 2.0.0-alpha.58
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/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +13 -5
- package/dist/commands/create.js.map +1 -1
- package/dist/create-workspace.d.ts +6 -1
- package/dist/create-workspace.d.ts.map +1 -1
- package/dist/create-workspace.js +12 -9
- package/dist/create-workspace.js.map +1 -1
- package/package.json +4 -4
- package/template/.dockerignore +11 -0
- package/template/README.md +3 -1
- package/template/apps/server/package.json +1 -1
- package/template/apps/web/e2e/instruments.spec.ts +912 -9
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/scripts/check.mjs +2 -2
- package/template/apps/web/src/AuthoritativeSelector.tsx +371 -0
- package/template/apps/web/src/CollegePage.tsx +181 -0
- package/template/apps/web/src/InstrumentDetailPage.tsx +94 -20
- package/template/apps/web/src/InstrumentForm.tsx +450 -88
- package/template/apps/web/src/InstrumentFormPage.tsx +49 -9
- package/template/apps/web/src/InstrumentListPage.tsx +30 -35
- package/template/apps/web/src/Shell.tsx +229 -41
- package/template/apps/web/src/components/platform-fields/PlatformDirectoryPicker.tsx +698 -0
- package/template/apps/web/src/data-provider.ts +1 -1
- package/template/apps/web/src/fields.ts +17 -6
- package/template/apps/web/src/instrument.ts +1 -1
- package/template/apps/web/src/main.tsx +16 -1
- package/template/apps/web/src/platform-client.ts +213 -2
- package/template/apps/web/src/runtime.tsx +4 -2
- package/template/apps/web/src/styles.css +869 -29
- package/template/apps/web/test/contracts.test.ts +369 -10
- package/template/openxiangda.config.ts +24 -5
- package/template/package.json +3 -3
- package/template/packages/contracts/src/generated.ts +22 -0
- package/template/platform/data/colleges.ts +21 -0
- package/template/platform/data/instruments.ts +1 -1
- package/template/scripts/verify-template-budget.mjs +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ArrowLeftOutlined, EyeOutlined } from '@ant-design/icons';
|
|
1
2
|
import { useCreate, useOne, useUpdate } from '@refinedev/core';
|
|
2
|
-
import { App, Result, Spin, Typography } from 'antd';
|
|
3
|
+
import { Alert, App, Button, Card, Result, Space, Spin, Typography } from 'antd';
|
|
3
4
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
4
5
|
import { InstrumentForm } from './InstrumentForm';
|
|
5
6
|
import { Shell } from './Shell';
|
|
@@ -23,32 +24,70 @@ export function InstrumentFormPage({ mode }: { mode: 'create' | 'edit' }) {
|
|
|
23
24
|
mode === 'create'
|
|
24
25
|
? hasCapability(INSTRUMENT_CAPABILITIES.create)
|
|
25
26
|
: hasCapability(INSTRUMENT_CAPABILITIES.update);
|
|
26
|
-
if (!allowed)
|
|
27
|
+
if (!allowed) {
|
|
27
28
|
return (
|
|
28
29
|
<Shell>
|
|
29
30
|
<Result status="403" title="当前平台用户无此操作权限" />
|
|
30
31
|
</Shell>
|
|
31
32
|
);
|
|
32
|
-
|
|
33
|
+
}
|
|
34
|
+
if (mode === 'edit' && query.query.isLoading) {
|
|
33
35
|
return (
|
|
34
36
|
<Shell>
|
|
35
|
-
<
|
|
37
|
+
<div className="oxa-page-loading">
|
|
38
|
+
<Spin />
|
|
39
|
+
</div>
|
|
36
40
|
</Shell>
|
|
37
41
|
);
|
|
38
|
-
|
|
42
|
+
}
|
|
43
|
+
if (mode === 'edit' && !record) {
|
|
39
44
|
return (
|
|
40
45
|
<Shell>
|
|
41
46
|
<Result status="404" title="仪器不存在或不在数据范围内" />
|
|
42
47
|
</Shell>
|
|
43
48
|
);
|
|
49
|
+
}
|
|
44
50
|
return (
|
|
45
51
|
<Shell>
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
<Card className="oxa-edit-hero">
|
|
53
|
+
<div className="oxa-edit-hero-title">
|
|
54
|
+
<div>
|
|
55
|
+
<Button
|
|
56
|
+
icon={<ArrowLeftOutlined />}
|
|
57
|
+
onClick={() =>
|
|
58
|
+
navigate(mode === 'edit' ? `/instruments/${id}` : '/instruments')
|
|
59
|
+
}
|
|
60
|
+
type="link"
|
|
61
|
+
>
|
|
62
|
+
{mode === 'edit' ? '返回仪器详情' : '返回仪器资源'}
|
|
63
|
+
</Button>
|
|
64
|
+
<Typography.Title level={2}>
|
|
65
|
+
{mode === 'create' ? '新增仪器' : '编辑仪器'}
|
|
66
|
+
</Typography.Title>
|
|
67
|
+
{record && (
|
|
68
|
+
<Typography.Text type="secondary">
|
|
69
|
+
{record.chineseName} · {record.instrumentCode} · 当前版本{' '}
|
|
70
|
+
{record.revision}
|
|
71
|
+
</Typography.Text>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
{record && (
|
|
75
|
+
<Button
|
|
76
|
+
icon={<EyeOutlined />}
|
|
77
|
+
onClick={() => navigate(`/instruments/${id}`)}
|
|
78
|
+
>
|
|
79
|
+
查看详情
|
|
80
|
+
</Button>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
<Alert
|
|
84
|
+
title="仅显示当前角色可编辑的字段;保存时将校验数据版本与权限范围。"
|
|
85
|
+
showIcon
|
|
86
|
+
type="info"
|
|
87
|
+
/>
|
|
88
|
+
</Card>
|
|
49
89
|
<InstrumentForm
|
|
50
90
|
mode={mode}
|
|
51
|
-
record={record}
|
|
52
91
|
onCancel={() =>
|
|
53
92
|
navigate(mode === 'edit' ? `/instruments/${id}` : '/instruments')
|
|
54
93
|
}
|
|
@@ -71,6 +110,7 @@ export function InstrumentFormPage({ mode }: { mode: 'create' | 'edit' }) {
|
|
|
71
110
|
navigate(`/instruments/${result.data.id}`);
|
|
72
111
|
}
|
|
73
112
|
}}
|
|
113
|
+
record={record}
|
|
74
114
|
/>
|
|
75
115
|
</Shell>
|
|
76
116
|
);
|
|
@@ -26,6 +26,11 @@ import {
|
|
|
26
26
|
} from 'antd';
|
|
27
27
|
import { useMemo, useState } from 'react';
|
|
28
28
|
import { useNavigate } from 'react-router-dom';
|
|
29
|
+
import {
|
|
30
|
+
AuthoritativeSelector,
|
|
31
|
+
ResolvedValueText,
|
|
32
|
+
} from './AuthoritativeSelector';
|
|
33
|
+
import { PlatformDirectoryPicker } from './components/platform-fields/PlatformDirectoryPicker';
|
|
29
34
|
import { Shell } from './Shell';
|
|
30
35
|
import {
|
|
31
36
|
INSTRUMENT_CAPABILITIES,
|
|
@@ -34,16 +39,6 @@ import {
|
|
|
34
39
|
} from './instrument';
|
|
35
40
|
import { useRuntime } from './runtime';
|
|
36
41
|
|
|
37
|
-
const colleges = [
|
|
38
|
-
{ label: '先进材料学院', value: 'college-am' },
|
|
39
|
-
{ label: '生命健康学院', value: 'college-lh' },
|
|
40
|
-
{ label: '公共测试中心', value: 'college-pt' },
|
|
41
|
-
];
|
|
42
|
-
const admins = [
|
|
43
|
-
{ label: '王老师', value: 'user-wang' },
|
|
44
|
-
{ label: '李老师', value: 'user-li' },
|
|
45
|
-
{ label: '陈老师', value: 'user-chen' },
|
|
46
|
-
];
|
|
47
42
|
const statuses = [
|
|
48
43
|
{ label: '正常', value: 'normal' },
|
|
49
44
|
{ label: '维修', value: 'maintenance' },
|
|
@@ -62,7 +57,7 @@ export function InstrumentListPage() {
|
|
|
62
57
|
const [sort, setSort] = useState<{
|
|
63
58
|
field: string;
|
|
64
59
|
order: 'asc' | 'desc';
|
|
65
|
-
}>({ field: '
|
|
60
|
+
}>({ field: 'instrumentCode', order: 'asc' });
|
|
66
61
|
const list = useList<InstrumentRecord>({
|
|
67
62
|
resource: 'instruments',
|
|
68
63
|
pagination: { currentPage: page, pageSize },
|
|
@@ -100,20 +95,15 @@ export function InstrumentListPage() {
|
|
|
100
95
|
title: '所属学院',
|
|
101
96
|
dataIndex: 'collegeId',
|
|
102
97
|
width: 140,
|
|
103
|
-
render: value =>
|
|
104
|
-
colleges.find(item => item.value === value)?.label || value,
|
|
98
|
+
render: value => <ResolvedValueText source="college" value={value} />,
|
|
105
99
|
},
|
|
106
100
|
{
|
|
107
101
|
title: '仪器管理员',
|
|
108
102
|
dataIndex: 'instrumentAdminIds',
|
|
109
103
|
width: 130,
|
|
110
|
-
render: values =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
value =>
|
|
114
|
-
admins.find(item => item.value === value)?.label || value
|
|
115
|
-
)
|
|
116
|
-
.join('、'),
|
|
104
|
+
render: values => (
|
|
105
|
+
<ResolvedValueText source="user" value={values as string[]} />
|
|
106
|
+
),
|
|
117
107
|
},
|
|
118
108
|
{
|
|
119
109
|
title: '仪器状态',
|
|
@@ -143,9 +133,8 @@ export function InstrumentListPage() {
|
|
|
143
133
|
},
|
|
144
134
|
{
|
|
145
135
|
title: '更新时间',
|
|
146
|
-
dataIndex: '
|
|
136
|
+
dataIndex: 'updated_at',
|
|
147
137
|
width: 160,
|
|
148
|
-
sorter: true,
|
|
149
138
|
render: value => (value ? new Date(value).toLocaleString() : '-'),
|
|
150
139
|
},
|
|
151
140
|
{
|
|
@@ -211,15 +200,15 @@ export function InstrumentListPage() {
|
|
|
211
200
|
}
|
|
212
201
|
return (
|
|
213
202
|
<Shell>
|
|
214
|
-
<Card>
|
|
215
|
-
<Space
|
|
203
|
+
<Card className="oxa-list-card">
|
|
204
|
+
<Space orientation="vertical" size={16} style={{ width: '100%' }}>
|
|
216
205
|
<div className="oxa-page-heading">
|
|
217
206
|
<div>
|
|
218
|
-
<
|
|
207
|
+
<h2 className="oxa-title">
|
|
219
208
|
仪器资源
|
|
220
|
-
</
|
|
209
|
+
</h2>
|
|
221
210
|
<div className="oxa-muted">
|
|
222
|
-
|
|
211
|
+
统一管理仪器档案、归属、管理员与开放状态
|
|
223
212
|
</div>
|
|
224
213
|
</div>
|
|
225
214
|
<Space>
|
|
@@ -255,22 +244,28 @@ export function InstrumentListPage() {
|
|
|
255
244
|
setDraft(value => ({ ...value, keyword: event.target.value }))
|
|
256
245
|
}
|
|
257
246
|
/>
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
options={colleges}
|
|
247
|
+
<AuthoritativeSelector
|
|
248
|
+
operation="update"
|
|
261
249
|
placeholder="所属学院"
|
|
250
|
+
source="college"
|
|
262
251
|
value={draft.collegeId}
|
|
263
252
|
onChange={value =>
|
|
264
|
-
setDraft(item => ({
|
|
253
|
+
setDraft(item => ({
|
|
254
|
+
...item,
|
|
255
|
+
collegeId: typeof value === 'string' ? value : undefined,
|
|
256
|
+
}))
|
|
265
257
|
}
|
|
266
258
|
/>
|
|
267
|
-
<
|
|
268
|
-
|
|
269
|
-
options={admins}
|
|
259
|
+
<PlatformDirectoryPicker
|
|
260
|
+
kind="user"
|
|
270
261
|
placeholder="仪器管理员"
|
|
271
262
|
value={draft.instrumentAdminId}
|
|
272
263
|
onChange={value =>
|
|
273
|
-
setDraft(item => ({
|
|
264
|
+
setDraft(item => ({
|
|
265
|
+
...item,
|
|
266
|
+
instrumentAdminId:
|
|
267
|
+
typeof value === 'string' ? value : undefined,
|
|
268
|
+
}))
|
|
274
269
|
}
|
|
275
270
|
/>
|
|
276
271
|
<Select
|
|
@@ -1,52 +1,240 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ApartmentOutlined,
|
|
3
|
+
AppstoreOutlined,
|
|
4
|
+
BookOutlined,
|
|
5
|
+
DownOutlined,
|
|
6
|
+
ExperimentOutlined,
|
|
7
|
+
HomeOutlined,
|
|
8
|
+
LeftOutlined,
|
|
9
|
+
MenuUnfoldOutlined,
|
|
10
|
+
SettingOutlined,
|
|
11
|
+
TeamOutlined,
|
|
12
|
+
} from '@ant-design/icons';
|
|
13
|
+
import {
|
|
14
|
+
Avatar,
|
|
15
|
+
Breadcrumb,
|
|
16
|
+
Button,
|
|
17
|
+
Layout,
|
|
18
|
+
Menu,
|
|
19
|
+
Space,
|
|
20
|
+
Typography,
|
|
21
|
+
type MenuProps,
|
|
22
|
+
} from 'antd';
|
|
3
23
|
import type { ReactNode } from 'react';
|
|
24
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
25
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
26
|
+
import { resolveDirectory } from './platform-client';
|
|
4
27
|
import { useRuntime } from './runtime';
|
|
5
|
-
import { applicationApiPath } from './runtime-meta';
|
|
6
28
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
const { Content, Header, Sider } = Layout;
|
|
30
|
+
|
|
31
|
+
type PageMeta = { title: string; breadcrumbs: string[]; selectedKey: string };
|
|
32
|
+
|
|
33
|
+
function pageMeta(pathname: string): PageMeta {
|
|
34
|
+
if (pathname === '/colleges') {
|
|
35
|
+
return {
|
|
36
|
+
title: '学院字典',
|
|
37
|
+
breadcrumbs: ['首页', '基础数据', '学院字典'],
|
|
38
|
+
selectedKey: '/colleges',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (pathname.endsWith('/new')) {
|
|
42
|
+
return {
|
|
43
|
+
title: '新增仪器',
|
|
44
|
+
breadcrumbs: ['首页', '仪器管理', '仪器资源', '新增仪器'],
|
|
45
|
+
selectedKey: '/instruments',
|
|
19
46
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
}
|
|
48
|
+
if (pathname.endsWith('/edit')) {
|
|
49
|
+
return {
|
|
50
|
+
title: '编辑仪器',
|
|
51
|
+
breadcrumbs: ['首页', '仪器管理', '仪器资源', '编辑仪器'],
|
|
52
|
+
selectedKey: '/instruments',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (/^\/instruments\/[^/]+$/.test(pathname)) {
|
|
56
|
+
return {
|
|
57
|
+
title: '仪器详情',
|
|
58
|
+
breadcrumbs: ['首页', '仪器管理', '仪器资源', '仪器详情'],
|
|
59
|
+
selectedKey: '/instruments',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
title: '仪器资源',
|
|
64
|
+
breadcrumbs: ['首页', '仪器管理', '仪器资源'],
|
|
65
|
+
selectedKey: '/instruments',
|
|
23
66
|
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function Shell({ children }: { children: ReactNode }) {
|
|
70
|
+
const { identity, hasCapability } = useRuntime();
|
|
71
|
+
const location = useLocation();
|
|
72
|
+
const navigate = useNavigate();
|
|
73
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
74
|
+
const [user, setUser] = useState({ label: '当前用户', description: '平台用户' });
|
|
75
|
+
const meta = pageMeta(location.pathname);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
let active = true;
|
|
79
|
+
void resolveDirectory('user', [identity.userId])
|
|
80
|
+
.then(page => {
|
|
81
|
+
const entry = page.items[0];
|
|
82
|
+
if (active && entry) {
|
|
83
|
+
setUser({
|
|
84
|
+
label: entry.label || '当前用户',
|
|
85
|
+
description: entry.description || '平台用户',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
.catch(() => {
|
|
90
|
+
if (active) setUser({ label: '当前用户', description: '平台用户' });
|
|
91
|
+
});
|
|
92
|
+
return () => {
|
|
93
|
+
active = false;
|
|
94
|
+
};
|
|
95
|
+
}, [identity.userId]);
|
|
96
|
+
|
|
97
|
+
const menuItems = useMemo<MenuProps['items']>(
|
|
98
|
+
() => [
|
|
99
|
+
{
|
|
100
|
+
key: 'overview',
|
|
101
|
+
icon: <AppstoreOutlined />,
|
|
102
|
+
label: '总览',
|
|
103
|
+
children: [
|
|
104
|
+
{ key: 'workbench', icon: <HomeOutlined />, label: '工作台' },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: 'instrument-management',
|
|
109
|
+
icon: <ExperimentOutlined />,
|
|
110
|
+
label: '仪器管理',
|
|
111
|
+
children: [
|
|
112
|
+
{
|
|
113
|
+
key: '/instruments',
|
|
114
|
+
icon: <ExperimentOutlined />,
|
|
115
|
+
label: '仪器资源',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
key: 'open-records',
|
|
119
|
+
icon: <BookOutlined />,
|
|
120
|
+
label: '开放记录',
|
|
121
|
+
disabled: true,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: 'base-data',
|
|
127
|
+
icon: <ApartmentOutlined />,
|
|
128
|
+
label: '基础数据',
|
|
129
|
+
children: hasCapability('app:instrument-center:data:colleges:read')
|
|
130
|
+
? [
|
|
131
|
+
{
|
|
132
|
+
key: '/colleges',
|
|
133
|
+
icon: <ApartmentOutlined />,
|
|
134
|
+
label: '学院字典',
|
|
135
|
+
},
|
|
136
|
+
]
|
|
137
|
+
: [],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
key: 'system-settings',
|
|
141
|
+
icon: <SettingOutlined />,
|
|
142
|
+
label: '系统设置',
|
|
143
|
+
children: [
|
|
144
|
+
{
|
|
145
|
+
key: 'application-admins',
|
|
146
|
+
icon: <TeamOutlined />,
|
|
147
|
+
label: '应用管理员',
|
|
148
|
+
disabled: true,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
[hasCapability]
|
|
154
|
+
);
|
|
155
|
+
|
|
24
156
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
157
|
+
<Layout className="oxa-app-layout">
|
|
158
|
+
<Sider
|
|
159
|
+
className="oxa-sider"
|
|
160
|
+
breakpoint="lg"
|
|
161
|
+
collapsed={collapsed}
|
|
162
|
+
collapsedWidth={76}
|
|
163
|
+
collapsible
|
|
164
|
+
onBreakpoint={setCollapsed}
|
|
165
|
+
theme="light"
|
|
166
|
+
trigger={null}
|
|
167
|
+
width={240}
|
|
168
|
+
>
|
|
169
|
+
<div className="oxa-brand">
|
|
170
|
+
<div aria-hidden className="oxa-brand-mark">
|
|
171
|
+
<span />
|
|
172
|
+
</div>
|
|
173
|
+
{!collapsed && (
|
|
174
|
+
<div className="oxa-brand-copy">
|
|
175
|
+
<strong>仪器资源管理</strong>
|
|
176
|
+
<span>仪器、学院与开放管理</span>
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
<Menu
|
|
181
|
+
className="oxa-menu"
|
|
182
|
+
defaultOpenKeys={[
|
|
183
|
+
'overview',
|
|
184
|
+
'instrument-management',
|
|
185
|
+
'base-data',
|
|
186
|
+
'system-settings',
|
|
187
|
+
]}
|
|
188
|
+
inlineCollapsed={collapsed}
|
|
189
|
+
items={menuItems}
|
|
190
|
+
mode="inline"
|
|
191
|
+
onClick={({ key }) => {
|
|
192
|
+
if (key === 'workbench' || key === '/instruments') {
|
|
193
|
+
navigate('/instruments');
|
|
194
|
+
} else if (key === '/colleges') {
|
|
195
|
+
navigate('/colleges');
|
|
43
196
|
}
|
|
197
|
+
}}
|
|
198
|
+
selectedKeys={[meta.selectedKey]}
|
|
199
|
+
/>
|
|
200
|
+
<div className="oxa-sider-footer">
|
|
201
|
+
<Button
|
|
202
|
+
aria-label={collapsed ? '展开侧边栏' : '收起侧边栏'}
|
|
203
|
+
block
|
|
204
|
+
icon={collapsed ? <MenuUnfoldOutlined /> : <LeftOutlined />}
|
|
205
|
+
onClick={() => setCollapsed(value => !value)}
|
|
206
|
+
type="text"
|
|
44
207
|
>
|
|
45
|
-
|
|
208
|
+
{!collapsed && '收起侧边栏'}
|
|
46
209
|
</Button>
|
|
47
|
-
</
|
|
48
|
-
</
|
|
49
|
-
<
|
|
50
|
-
|
|
210
|
+
</div>
|
|
211
|
+
</Sider>
|
|
212
|
+
<Layout className="oxa-workspace">
|
|
213
|
+
<Header className="oxa-topbar">
|
|
214
|
+
<div className="oxa-topbar-page">
|
|
215
|
+
<Breadcrumb items={meta.breadcrumbs.map(title => ({ title }))} />
|
|
216
|
+
<Typography.Title
|
|
217
|
+
data-testid={meta.title === '仪器资源' ? 'instrument-title' : undefined}
|
|
218
|
+
level={3}
|
|
219
|
+
>
|
|
220
|
+
{meta.title}
|
|
221
|
+
</Typography.Title>
|
|
222
|
+
</div>
|
|
223
|
+
<Space className="oxa-current-user" size={12}>
|
|
224
|
+
<Avatar className="oxa-current-user-avatar" size={42}>
|
|
225
|
+
{user.label.slice(0, 1)}
|
|
226
|
+
</Avatar>
|
|
227
|
+
<div className="oxa-current-user-copy">
|
|
228
|
+
<strong>{user.label}</strong>
|
|
229
|
+
<span>{user.description}</span>
|
|
230
|
+
</div>
|
|
231
|
+
<DownOutlined className="oxa-current-user-chevron" />
|
|
232
|
+
</Space>
|
|
233
|
+
</Header>
|
|
234
|
+
<Content className="oxa-content">
|
|
235
|
+
<main className="oxa-main">{children}</main>
|
|
236
|
+
</Content>
|
|
237
|
+
</Layout>
|
|
238
|
+
</Layout>
|
|
51
239
|
);
|
|
52
240
|
}
|