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.
Files changed (36) hide show
  1. package/dist/commands/create.d.ts.map +1 -1
  2. package/dist/commands/create.js +13 -5
  3. package/dist/commands/create.js.map +1 -1
  4. package/dist/create-workspace.d.ts +6 -1
  5. package/dist/create-workspace.d.ts.map +1 -1
  6. package/dist/create-workspace.js +12 -9
  7. package/dist/create-workspace.js.map +1 -1
  8. package/package.json +4 -4
  9. package/template/.dockerignore +11 -0
  10. package/template/README.md +3 -1
  11. package/template/apps/server/package.json +1 -1
  12. package/template/apps/web/e2e/instruments.spec.ts +912 -9
  13. package/template/apps/web/package.json +1 -1
  14. package/template/apps/web/scripts/check.mjs +2 -2
  15. package/template/apps/web/src/AuthoritativeSelector.tsx +371 -0
  16. package/template/apps/web/src/CollegePage.tsx +181 -0
  17. package/template/apps/web/src/InstrumentDetailPage.tsx +94 -20
  18. package/template/apps/web/src/InstrumentForm.tsx +450 -88
  19. package/template/apps/web/src/InstrumentFormPage.tsx +49 -9
  20. package/template/apps/web/src/InstrumentListPage.tsx +30 -35
  21. package/template/apps/web/src/Shell.tsx +229 -41
  22. package/template/apps/web/src/components/platform-fields/PlatformDirectoryPicker.tsx +698 -0
  23. package/template/apps/web/src/data-provider.ts +1 -1
  24. package/template/apps/web/src/fields.ts +17 -6
  25. package/template/apps/web/src/instrument.ts +1 -1
  26. package/template/apps/web/src/main.tsx +16 -1
  27. package/template/apps/web/src/platform-client.ts +213 -2
  28. package/template/apps/web/src/runtime.tsx +4 -2
  29. package/template/apps/web/src/styles.css +869 -29
  30. package/template/apps/web/test/contracts.test.ts +369 -10
  31. package/template/openxiangda.config.ts +24 -5
  32. package/template/package.json +3 -3
  33. package/template/packages/contracts/src/generated.ts +22 -0
  34. package/template/platform/data/colleges.ts +21 -0
  35. package/template/platform/data/instruments.ts +1 -1
  36. package/template/scripts/verify-template-budget.mjs +1 -1
@@ -1,60 +1,134 @@
1
- import { useOne } from '@refinedev/core';
2
- import { Button, Result, Space, Spin, Typography } from 'antd';
1
+ import {
2
+ ArrowLeftOutlined,
3
+ EditOutlined,
4
+ HistoryOutlined,
5
+ } from '@ant-design/icons';
6
+ import { Button, Card, Result, Space, Spin, Tag, Typography } from 'antd';
7
+ import { useEffect, useState } from 'react';
8
+ import type { DataAuditEntry } from 'openxiangda-contracts/browser';
3
9
  import { useNavigate, useParams } from 'react-router-dom';
4
10
  import { InstrumentForm } from './InstrumentForm';
5
11
  import { Shell } from './Shell';
6
12
  import { INSTRUMENT_CAPABILITIES, type InstrumentRecord } from './instrument';
13
+ import { instrumentDataApi } from './platform-client';
7
14
  import { useRuntime } from './runtime';
15
+ import { useOne } from '@refinedev/core';
8
16
 
9
17
  export function InstrumentDetailPage() {
10
18
  const { id = '' } = useParams();
11
19
  const navigate = useNavigate();
12
20
  const { hasCapability } = useRuntime();
21
+ const [auditEntries, setAuditEntries] = useState<DataAuditEntry[]>([]);
22
+ const [auditError, setAuditError] = useState('');
13
23
  const query = useOne<InstrumentRecord>({
14
24
  resource: 'instruments',
15
25
  id,
16
26
  queryOptions: { retry: false },
17
27
  });
18
28
  const record = query.result;
19
- if (query.query.isLoading)
29
+ useEffect(() => {
30
+ if (!record?.id) return;
31
+ let active = true;
32
+ setAuditError('');
33
+ void instrumentDataApi.audit(record.id).then(
34
+ page => {
35
+ if (active) setAuditEntries(page.items || []);
36
+ },
37
+ error => {
38
+ if (active)
39
+ setAuditError(error instanceof Error ? error.message : String(error));
40
+ }
41
+ );
42
+ return () => {
43
+ active = false;
44
+ };
45
+ }, [record?.id, record?.revision]);
46
+ if (query.query.isLoading) {
20
47
  return (
21
48
  <Shell>
22
- <Spin />
49
+ <div className="oxa-page-loading">
50
+ <Spin />
51
+ </div>
23
52
  </Shell>
24
53
  );
25
- if (query.query.isError || !record)
54
+ }
55
+ if (query.query.isError || !record) {
26
56
  return (
27
57
  <Shell>
28
58
  <Result
29
59
  status="403"
30
- title="无法访问"
31
60
  subTitle={query.query.error?.message || '记录不存在'}
61
+ title="无法访问"
32
62
  />
33
63
  </Shell>
34
64
  );
65
+ }
35
66
  return (
36
67
  <Shell>
37
- <Space className="oxa-page-heading">
38
- <Typography.Title level={3} style={{ margin: 0 }}>
39
- 仪器详情
40
- </Typography.Title>
41
- <Space>
42
- <Button onClick={() => navigate('/instruments')}>返回列表</Button>
43
- {hasCapability(INSTRUMENT_CAPABILITIES.update) && (
68
+ <Card className="oxa-detail-hero">
69
+ <div className="oxa-detail-hero-top">
70
+ <Button
71
+ aria-label="返回列表"
72
+ icon={<ArrowLeftOutlined />}
73
+ onClick={() => navigate('/instruments')}
74
+ type="link"
75
+ >
76
+ 返回仪器资源
77
+ </Button>
78
+ <Space>
44
79
  <Button
45
- type="primary"
46
- onClick={() => navigate(`/instruments/${id}/edit`)}
80
+ icon={<HistoryOutlined />}
81
+ onClick={() =>
82
+ document
83
+ .getElementById('instrument-audit')
84
+ ?.scrollIntoView({ behavior: 'smooth', block: 'center' })
85
+ }
47
86
  >
48
- 编辑
87
+ 操作记录
49
88
  </Button>
50
- )}
51
- </Space>
52
- </Space>
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">
101
+ <div>
102
+ <Space align="center" size={10}>
103
+ <Typography.Title level={2}>仪器详情</Typography.Title>
104
+ <Tag color={record.instrumentStatus === 'normal' ? 'green' : 'default'}>
105
+ {record.instrumentStatus === 'normal' ? '启用' : '非正常'}
106
+ </Tag>
107
+ </Space>
108
+ <Typography.Title className="oxa-instrument-name" level={3}>
109
+ {record.chineseName}
110
+ </Typography.Title>
111
+ <Typography.Text type="secondary">
112
+ {record.instrumentCode} · {record.specModel}
113
+ </Typography.Text>
114
+ </div>
115
+ <Space className="oxa-record-meta" separator={<span>·</span>}>
116
+ <span>最近更新 {formatDateTime(record.updated_at)}</span>
117
+ <span>版本 {record.revision}</span>
118
+ </Space>
119
+ </div>
120
+ </Card>
53
121
  <InstrumentForm
122
+ auditEntries={auditEntries}
123
+ auditError={auditError}
54
124
  mode="detail"
55
- record={record}
56
125
  onCancel={() => navigate('/instruments')}
126
+ record={record}
57
127
  />
58
128
  </Shell>
59
129
  );
60
130
  }
131
+
132
+ function formatDateTime(value?: string) {
133
+ return value ? new Date(value).toLocaleString('zh-CN', { hour12: false }) : '-';
134
+ }