@yuku123/z-frontend-common 0.2.9 → 1.1.0

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 (75) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +55 -42
  3. package/dist/PagedTable-DyjNokXN.js +1211 -0
  4. package/dist/index.js +310 -0
  5. package/dist/ui.js +27 -0
  6. package/package.json +52 -44
  7. package/src/components/ErrorBoundary/index.jsx +24 -77
  8. package/src/components/Layout/index.jsx +141 -38
  9. package/src/components/LoginPage/index.jsx +47 -21
  10. package/src/components/StatusTag/index.jsx +14 -12
  11. package/src/components/ui/CategoryDot.jsx +38 -0
  12. package/src/components/ui/DarkBanner.jsx +142 -0
  13. package/src/components/ui/EChartsChart.jsx +82 -0
  14. package/src/components/ui/EmptyState.jsx +94 -0
  15. package/src/components/ui/ErrorState.jsx +114 -0
  16. package/src/components/ui/FeatureCard.jsx +205 -0
  17. package/src/components/ui/GradientStatCard.jsx +196 -0
  18. package/src/components/ui/LoadingState.jsx +116 -0
  19. package/src/components/ui/PageHeader.jsx +143 -0
  20. package/src/components/ui/PagedTable.jsx +83 -0
  21. package/src/components/ui/SearchInput.jsx +111 -0
  22. package/src/components/ui/SectionHeader.jsx +76 -0
  23. package/src/components/ui/StatusBadge.jsx +66 -0
  24. package/src/components/ui/TableToolbar.jsx +58 -0
  25. package/src/components/ui/index.js +36 -0
  26. package/src/components/ui/tokens.js +224 -0
  27. package/src/index.js +33 -37
  28. package/src/services/request.js +74 -4
  29. package/dist/z-frontend-common.css +0 -1
  30. package/dist/z-frontend-common.es.js +0 -51275
  31. package/dist/z-frontend-common.umd.js +0 -459
  32. package/src/components/Ctc/Layout.jsx +0 -330
  33. package/src/components/Ctc/Layout.module.css +0 -145
  34. package/src/components/Ctc/agentTeam/index.tsx +0 -308
  35. package/src/components/Ctc/login/AuthPage.module.css +0 -26
  36. package/src/components/Ctc/login/AuthPage.tsx +0 -235
  37. package/src/components/Ctc/login/index.less +0 -49
  38. package/src/components/Ctc/login/index.tsx +0 -142
  39. package/src/components/Ctc/uc/UCLogin.tsx +0 -31
  40. package/src/components/Ctc/uc/UCPhoneLogin.tsx +0 -62
  41. package/src/components/Ctc/uc/UCRegister.tsx +0 -37
  42. package/src/components/Ctc/uc/UCResetPassword.tsx +0 -69
  43. package/src/components/Ctc/uc/index.tsx +0 -15
  44. package/src/components/Ctc/userPanel/index.tsx +0 -999
  45. package/src/components/Ctc/webide/index.tsx +0 -272
  46. package/src/components/LowCode/LowCodeModel.jsx +0 -962
  47. package/src/components/LowCode/LowCodePage.jsx +0 -31
  48. package/src/components/LowCode/LowCodeRuntime.jsx +0 -335
  49. package/src/components/LowCode/MaterializePage.jsx +0 -235
  50. package/src/components/LowCode/index.js +0 -1
  51. package/src/components/MockPlatform/CurlImportModal.jsx +0 -362
  52. package/src/components/MockPlatform/EndpointsTab.jsx +0 -509
  53. package/src/components/MockPlatform/EnvironmentsTab.jsx +0 -212
  54. package/src/components/MockPlatform/MockTemplateHelper.jsx +0 -200
  55. package/src/components/MockPlatform/OpenApiImportModal.jsx +0 -305
  56. package/src/components/MockPlatform/RecordingsTab.jsx +0 -397
  57. package/src/components/MockPlatform/RequestLogsTab.jsx +0 -239
  58. package/src/components/MockPlatform/ScenariosTab.jsx +0 -236
  59. package/src/components/MockPlatform/TestCasesTab.jsx +0 -462
  60. package/src/components/MockPlatform/index.jsx +0 -127
  61. package/src/components/Overview.jsx +0 -74
  62. package/src/dev.jsx +0 -28
  63. package/src/services/agentTeam.js +0 -7
  64. package/src/services/api.js +0 -84
  65. package/src/services/ctcAc.js +0 -7
  66. package/src/services/ctcAcDomain.js +0 -7
  67. package/src/services/ctcAuthorization.js +0 -7
  68. package/src/services/ctcSurl.js +0 -7
  69. package/src/services/ctcUser.js +0 -7
  70. package/src/services/job.js +0 -7
  71. package/src/services/metaApp.js +0 -7
  72. package/src/services/privateConfig.js +0 -7
  73. package/src/services/webide.js +0 -6
  74. package/src/services/workspace.js +0 -21
  75. package/src/utils/request.ts +0 -102
@@ -1,31 +0,0 @@
1
- import React from 'react'
2
- import {useParams} from 'react-router-dom'
3
- import LowCodeModel from './LowCodeModel'
4
- import LowCodeRuntime from './LowCodeRuntime'
5
-
6
- /**
7
- * LowCodePage 派发器 (z-lc Phase 1)
8
- * <p>
9
- * 单一入口 /form/:entity 拆分为两个真实页面:
10
- * <ul>
11
- * <li>/form/_model?appCode=xxx → LowCodeModel (实体/字段编辑器)</li>
12
- * <li>/form/{entityCode}?appCode=xxx → LowCodeRuntime (通用 CRUD)</li>
13
- * </ul>
14
- */
15
- const LowCodePage = () => {
16
- const {entity: entityCode} = useParams()
17
- if (!entityCode) {
18
- return (
19
- <div style={{padding: 24}}>
20
- 缺少实体编码. 请访问 <code>/form/_model</code> 编辑模型,
21
- 或 <code>/form/&#123;entityCode&#125;</code> 运行时 CRUD.
22
- </div>
23
- )
24
- }
25
- if (entityCode === '_model') {
26
- return <LowCodeModel/>
27
- }
28
- return <LowCodeRuntime/>
29
- }
30
-
31
- export default LowCodePage
@@ -1,335 +0,0 @@
1
- import React, {useCallback, useEffect, useState} from 'react'
2
- import {useParams, useSearchParams} from 'react-router-dom'
3
- import {
4
- Alert,
5
- Button,
6
- Card,
7
- DatePicker,
8
- Empty,
9
- Form,
10
- Input,
11
- InputNumber,
12
- message,
13
- Modal,
14
- Select,
15
- Space,
16
- Spin,
17
- Switch,
18
- Table,
19
- Tag
20
- } from 'antd'
21
- import {DatabaseOutlined, DeleteOutlined, EditOutlined, PlusOutlined, ReloadOutlined} from '@ant-design/icons'
22
- import request from '../../utils/request'
23
-
24
- const {Option} = Select
25
-
26
- const FIELD_TYPE_INPUT = {
27
- STRING: {component: Input, props: {}},
28
- TEXT: {component: Input.TextArea, props: {rows: 3}},
29
- INT: {component: InputNumber, props: {}},
30
- LONG: {component: InputNumber, props: {}},
31
- DECIMAL: {component: InputNumber, props: {step: 0.01}},
32
- BOOLEAN: {component: Switch, props: {}},
33
- DATE: {component: DatePicker, props: {format: 'YYYY-MM-DD'}},
34
- DATETIME: {component: DatePicker, props: {format: 'YYYY-MM-DD HH:mm:ss', showTime: true}},
35
- JSON: {component: Input.TextArea, props: {rows: 3, placeholder: 'JSON 字符串'}},
36
- REF: {component: InputNumber, props: {}}
37
- }
38
-
39
- /**
40
- * LowCodeRuntime: 通用 CRUD 页面
41
- * <p>
42
- * 通过 GET /api/lc/app/{appCode}/schema 取 entity 定义
43
- * 通过 POST /api/lc/runtime/{entityCode}/{list|get|create|update|delete} 走 CRUD
44
- */
45
- export default function LowCodeRuntime() {
46
- const {entity: entityCodeParam} = useParams()
47
- const [searchParams] = useSearchParams()
48
- const appCode = searchParams.get('appCode') || 'demo'
49
- const tenantCode = searchParams.get('tenant') || localStorage.getItem('z_tenant') || 'default'
50
-
51
- const [schema, setSchema] = useState([])
52
- const [entity, setEntity] = useState(null)
53
- const [rows, setRows] = useState([])
54
- const [total, setTotal] = useState(0)
55
- const [page, setPage] = useState(1)
56
- const [size, setSize] = useState(10)
57
- const [loading, setLoading] = useState(false)
58
- const [modalOpen, setModalOpen] = useState(false)
59
- const [editing, setEditing] = useState(null) // null = create, { id } = update
60
- const [form] = Form.useForm()
61
- const [dictCache, setDictCache] = useState({}) // { dictCode: [{ value, label }] }
62
-
63
- const fetchSchema = useCallback(async () => {
64
- try {
65
- const list = await request.get(`/lc/app/${appCode}/schema`, {params: {tenantCode}})
66
- setSchema(Array.isArray(list) ? list : [])
67
- const e = (list || []).find(x => x.entityCode === entityCodeParam)
68
- setEntity(e || null)
69
- } catch (err) {
70
- message.error('加载 schema 失败: ' + (err?.message || '未知错误'))
71
- setSchema([])
72
- setEntity(null)
73
- }
74
- }, [appCode, tenantCode, entityCodeParam])
75
-
76
- const fetchDict = useCallback(async (dictCode) => {
77
- if (!dictCode || dictCache[dictCode]) return dictCache[dictCode] || []
78
- try {
79
- // 走 z-meta 字典接口: /dict/items/get?dictCode=xxx
80
- const arr = await request.get(`/dict/items/get`, {params: {dictCode}})
81
- const list = Array.isArray(arr) ? arr : []
82
- const normalized = list.map(it => ({
83
- value: it.itemValue ?? it.itemCode,
84
- label: it.itemName ?? it.itemLabel ?? it.itemCode
85
- }))
86
- setDictCache(prev => ({...prev, [dictCode]: normalized}))
87
- return normalized
88
- } catch (e) {
89
- // 字典不存在也不报错 — 业务表允许 dict 字段没值
90
- return []
91
- }
92
- }, [dictCache])
93
-
94
- const fetchList = useCallback(async () => {
95
- if (!entity) return
96
- setLoading(true)
97
- try {
98
- const res = await request.post(`/lc/runtime/${entity.entityCode}/list`, {
99
- tenantCode,
100
- appCode,
101
- entityCode: entity.entityCode,
102
- page, size
103
- })
104
- setRows(res?.records || [])
105
- setTotal(res?.total || 0)
106
- } catch (err) {
107
- message.error('查询失败: ' + (err?.message || '未知错误'))
108
- setRows([])
109
- setTotal(0)
110
- } finally {
111
- setLoading(false)
112
- }
113
- }, [entity, tenantCode, appCode, page, size])
114
-
115
- useEffect(() => {
116
- fetchSchema()
117
- }, [fetchSchema])
118
- useEffect(() => {
119
- fetchList()
120
- }, [fetchList])
121
-
122
- // 预取字典
123
- useEffect(() => {
124
- if (!entity || !entity.fields) return
125
- entity.fields.forEach(f => {
126
- if (f.dictCode) fetchDict(f.dictCode)
127
- })
128
- }, [entity, fetchDict])
129
-
130
- if (!entityCodeParam) {
131
- return <Empty description="缺少实体编码 (路径变量 :entity)"/>
132
- }
133
-
134
- if (!entity) {
135
- return (
136
- <Card title={<><DatabaseOutlined/> z-lc 运行时</>}>
137
- <Alert
138
- type="warning"
139
- showIcon
140
- title={`实体 ${entityCodeParam} 未定义`}
141
- description={`请先在 模型编辑器 (路径 /form/_model?appCode=${appCode}) 中定义实体, 然后刷新此页.`}
142
- />
143
- </Card>
144
- )
145
- }
146
-
147
- const fields = entity.fields || []
148
-
149
- const openCreate = () => {
150
- setEditing(null)
151
- form.resetFields()
152
- setModalOpen(true)
153
- }
154
-
155
- const openEdit = async (row) => {
156
- setEditing(row)
157
- try {
158
- const full = await request.post(`/lc/runtime/${entity.entityCode}/get`, {
159
- tenantCode, appCode, entityCode: entity.entityCode, id: row.id
160
- })
161
- form.resetFields()
162
- form.setFieldsValue(full || row)
163
- } catch (e) {
164
- // 回退到当前行
165
- form.resetFields()
166
- form.setFieldsValue(row)
167
- }
168
- setModalOpen(true)
169
- }
170
-
171
- const handleDelete = (row) => {
172
- Modal.confirm({
173
- title: '确定删除?',
174
- content: `id=${row.id}`,
175
- okType: 'danger',
176
- onOk: async () => {
177
- try {
178
- await request.post(`/lc/runtime/${entity.entityCode}/delete`, {
179
- tenantCode, appCode, entityCode: entity.entityCode, id: row.id
180
- })
181
- message.success('删除成功')
182
- fetchList()
183
- } catch (e) {
184
- message.error('删除失败: ' + (e?.message || '未知错误'))
185
- }
186
- }
187
- })
188
- }
189
-
190
- const handleSubmit = async () => {
191
- try {
192
- const values = await form.validateFields()
193
- const body = {
194
- tenantCode, appCode, entityCode: entity.entityCode, fieldValues: {...values}
195
- }
196
- if (editing) {
197
- body.fieldValues.id = editing.id
198
- await request.post(`/lc/runtime/${entity.entityCode}/update`, body)
199
- message.success('更新成功')
200
- } else {
201
- await request.post(`/lc/runtime/${entity.entityCode}/create`, body)
202
- message.success('创建成功')
203
- }
204
- setModalOpen(false)
205
- fetchList()
206
- } catch (e) {
207
- if (e?.errorFields) {
208
- message.error('请补全必填字段')
209
- } else {
210
- message.error('提交失败: ' + (e?.message || '未知错误'))
211
- }
212
- }
213
- }
214
-
215
- const renderCell = (f, row) => {
216
- const v = row[f.fieldCode]
217
- const labelKey = f.fieldCode + '_label'
218
- if (row[labelKey] != null) {
219
- return <Tag>{row[labelKey]}</Tag>
220
- }
221
- if (v == null) return '-'
222
- if (f.fieldType === 'BOOLEAN') return v ? '是' : '否'
223
- if (f.fieldType === 'DATETIME' || f.fieldType === 'DATE') {
224
- // JdbcTemplate 返回 java.sql.Timestamp / Date
225
- try {
226
- return new Date(v).toLocaleString()
227
- } catch {
228
- return String(v)
229
- }
230
- }
231
- return String(v)
232
- }
233
-
234
- const columns = [
235
- {title: 'ID', dataIndex: 'id', key: 'id', width: 80},
236
- ...fields.map(f => ({
237
- title: f.fieldName || f.fieldCode,
238
- dataIndex: f.fieldCode,
239
- key: f.fieldCode,
240
- render: (_, row) => renderCell(f, row)
241
- })),
242
- {
243
- title: '操作',
244
- key: 'action',
245
- width: 180,
246
- fixed: 'right',
247
- render: (_, row) => (
248
- <Space>
249
- <Button type="link" icon={<EditOutlined/>} onClick={() => openEdit(row)}>编辑</Button>
250
- <Button type="link" danger icon={<DeleteOutlined/>} onClick={() => handleDelete(row)}>删除</Button>
251
- </Space>
252
- )
253
- }
254
- ]
255
-
256
- const renderFormItem = (f) => {
257
- const type = (f.fieldType || 'STRING').toUpperCase()
258
- if (f.dictCode) {
259
- const opts = dictCache[f.dictCode] || []
260
- return (
261
- <Select placeholder={`选择 ${f.fieldName || f.fieldCode}`} allowClear showSearch
262
- optionFilterProp="label">
263
- {opts.map(o => <Option key={o.value} value={o.value} label={o.label}>{o.label}</Option>)}
264
- </Select>
265
- )
266
- }
267
- const conf = FIELD_TYPE_INPUT[type] || FIELD_TYPE_INPUT.STRING
268
- const Cmp = conf.component
269
- return <Cmp {...conf.props} placeholder={f.fieldName || f.fieldCode}/>
270
- }
271
-
272
- return (
273
- <Card
274
- title={<><DatabaseOutlined/> {entity.entityName || entity.entityCode} (运行时 CRUD)</>}
275
- extra={
276
- <Space>
277
- <Button icon={<ReloadOutlined/>} onClick={fetchList}>刷新</Button>
278
- <Button type="primary" icon={<PlusOutlined/>} onClick={openCreate}>新增</Button>
279
- </Space>
280
- }
281
- >
282
- <Alert
283
- type="info"
284
- showIcon
285
- title={`App=${appCode} · Tenant=${tenantCode} · 字段数=${fields.length}`}
286
- style={{marginBottom: 16}}
287
- />
288
- <Spin spinning={loading}>
289
- <Table
290
- rowKey="id"
291
- columns={columns}
292
- dataSource={rows}
293
- pagination={{
294
- current: page, pageSize: size, total,
295
- showSizeChanger: true, showTotal: t => `共 ${t} 条`,
296
- onChange: (p, s) => {
297
- setPage(p);
298
- setSize(s)
299
- }
300
- }}
301
- scroll={{x: 'max-content'}}
302
- size="small"
303
- />
304
- </Spin>
305
-
306
- <Modal
307
- title={editing ? `编辑 #${editing.id}` : '新增记录'}
308
- open={modalOpen}
309
- onCancel={() => setModalOpen(false)}
310
- onOk={handleSubmit}
311
- width={680}
312
- destroyOnHidden
313
- >
314
- <Form form={form} layout="vertical">
315
- {fields.map(f => (
316
- <Form.Item
317
- key={f.fieldCode}
318
- name={f.fieldCode}
319
- label={
320
- <span>
321
- {f.fieldName || f.fieldCode}
322
- {f.required && <span style={{color: 'red'}}> *</span>}
323
- <Tag style={{marginLeft: 8}}>{f.fieldType}</Tag>
324
- </span>
325
- }
326
- rules={f.required ? [{required: true, message: '必填'}] : []}
327
- >
328
- {renderFormItem(f)}
329
- </Form.Item>
330
- ))}
331
- </Form>
332
- </Modal>
333
- </Card>
334
- )
335
- }
@@ -1,235 +0,0 @@
1
- import {useEffect, useState} from 'react'
2
- import {Alert, Badge, Button, Card, Col, Form, Input, message, Modal, Popconfirm, Row, Select, Space, Table, Tabs, Tag, Tooltip, Typography} from 'antd'
3
- import {
4
- CheckCircleOutlined,
5
- CodeOutlined,
6
- CopyOutlined,
7
- DeleteOutlined,
8
- DownloadOutlined,
9
- ExportOutlined,
10
- FileTextOutlined,
11
- HistoryOutlined,
12
- ReloadOutlined
13
- } from '@ant-design/icons'
14
- import request from '../../utils/request'
15
-
16
- const {Text, Paragraph} = Typography
17
- const {TextArea} = Input
18
-
19
- const STATUS_COLORS = {
20
- PENDING: 'default', GENERATING: 'processing', READY: 'success', FAILED: 'error',
21
- }
22
- const TYPE_COLORS = {
23
- Entity: 'blue', Mapper: 'cyan', Service: 'green', ServiceImpl: 'lime',
24
- Controller: 'orange', ReactPage: 'purple', File: 'default',
25
- }
26
-
27
- /**
28
- * z-lc 代码物化页面 (FEATURE006 T1).
29
- * <p>
30
- * 路径: /form/_materialize?appCode=xxx
31
- * <p>
32
- * 功能:
33
- * 1. 选择导出路径 + entity 范围 → 触发物化 (异步)
34
- * 2. 列出该 app 的历史物化批次 + 状态
35
- * 3. 选中某个 READY 批次 → 查看 diff (生成的文件清单)
36
- * 4. 单个文件 → 代码预览 + 复制到剪贴板
37
- */
38
- export default function MaterializePage() {
39
- const params = new URLSearchParams(window.location.search)
40
- const initialAppCode = params.get('appCode') || 'demo'
41
-
42
- const [appCode] = useState(initialAppCode)
43
- const [batches, setBatches] = useState([])
44
- const [loading, setLoading] = useState(false)
45
- const [triggerOpen, setTriggerOpen] = useState(false)
46
- const [diffOpen, setDiffOpen] = useState(false)
47
- const [diffFiles, setDiffFiles] = useState([])
48
- const [selectedFile, setSelectedFile] = useState(null)
49
- const [entities, setEntities] = useState([])
50
- const [form] = Form.useForm()
51
-
52
- useEffect(() => {
53
- loadBatches()
54
- loadEntities()
55
- }, [appCode])
56
-
57
- const loadBatches = async () => {
58
- setLoading(true)
59
- try {
60
- const r = await request.get(`/lc/app/${appCode}/materialize/list`, {params: {limit: 50}})
61
- setBatches(r?.data || [])
62
- } catch (e) {
63
- message.warning('加载批次列表失败')
64
- } finally {
65
- setLoading(false)
66
- }
67
- }
68
-
69
- const loadEntities = async () => {
70
- try {
71
- const r = await request.get(`/lc/app/${appCode}/schema`)
72
- const list = r?.data?.entities || r?.data || []
73
- setEntities(list)
74
- } catch (e) {
75
- // ignore
76
- }
77
- }
78
-
79
- const handleTrigger = async () => {
80
- try {
81
- const values = await form.validateFields()
82
- const payload = {
83
- appCode,
84
- materializationPath: values.materializationPath,
85
- entityCodes: values.entityCodes?.length ? values.entityCodes : null,
86
- description: values.description,
87
- triggerSource: 'USER',
88
- }
89
- await request.post(`/lc/app/${appCode}/materialize`, payload)
90
- message.success('物化已触发,请稍后查看状态')
91
- setTriggerOpen(false)
92
- form.resetFields()
93
- loadBatches()
94
- } catch (e) {
95
- if (!e?.errorFields) message.error('触发失败:' + (e?.message || '未知错误'))
96
- }
97
- }
98
-
99
- const handleShowDiff = async (batch) => {
100
- try {
101
- const r = await request.get(`/lc/app/${appCode}/materialize/diff`, {params: {id: batch.id}})
102
- const files = r?.data || []
103
- setDiffFiles(files)
104
- setDiffOpen(true)
105
- setSelectedFile(null)
106
- } catch (e) {
107
- message.error('加载文件清单失败')
108
- }
109
- }
110
-
111
- const handleCopy = (text) => {
112
- try {
113
- navigator.clipboard?.writeText(text)
114
- message.success('已复制到剪贴板')
115
- } catch {
116
- message.warning('复制失败')
117
- }
118
- }
119
-
120
- const handleDownload = (file) => {
121
- const blob = new Blob([file.content], {type: 'text/plain;charset=utf-8'})
122
- const url = URL.createObjectURL(blob)
123
- const a = document.createElement('a')
124
- a.href = url
125
- a.download = file.relativePath.split('/').pop()
126
- a.click()
127
- URL.revokeObjectURL(url)
128
- }
129
-
130
- const batchColumns = [
131
- {title: '批次号', dataIndex: 'exportVersion', width: 180, render: v => <Text code>{v}</Text>},
132
- {
133
- title: '状态', dataIndex: 'status', width: 100,
134
- render: v => <Badge status={STATUS_COLORS[v] === 'success' ? 'success' : STATUS_COLORS[v] === 'error' ? 'error' : 'processing'} text={v}/>
135
- },
136
- {title: '文件数', dataIndex: 'fileCount', width: 80, align: 'center', render: v => v || 0},
137
- {title: '来源', dataIndex: 'triggerSource', width: 80, render: v => <Tag>{v}</Tag>},
138
- {title: '路径', dataIndex: 'materializationPath', ellipsis: true, render: v => <Text type="secondary" style={{fontSize: 12}}>{v}</Text>},
139
- {title: '创建时间', dataIndex: 'createTime', width: 170},
140
- {
141
- title: '操作', key: 'action', width: 200, fixed: 'right',
142
- render: (_, r) => (
143
- <Space size="small">
144
- <Button size="small" type="link" icon={<FileTextOutlined/>}
145
- onClick={() => handleShowDiff(r)}
146
- disabled={r.status !== 'READY' && r.status !== 'GENERATING'}>
147
- 查看文件
148
- </Button>
149
- </Space>
150
- )
151
- },
152
- ]
153
-
154
- const fileColumns = [
155
- {title: '类型', dataIndex: 'type', width: 100, render: v => <Tag color={TYPE_COLORS[v] || 'default'}>{v}</Tag>},
156
- {title: 'Entity', dataIndex: 'entityCode', width: 120, render: v => v ? <Text code>{v}</Text> : '-'},
157
- {title: '相对路径', dataIndex: 'relativePath', ellipsis: true, render: v => <Text code style={{fontSize: 12}}>{v}</Text>},
158
- {title: '大小', dataIndex: 'sizeBytes', width: 90, render: v => `${(v/1024).toFixed(1)}KB`},
159
- {
160
- title: '操作', key: 'action', width: 200, fixed: 'right',
161
- render: (_, r) => (
162
- <Space size="small">
163
- <Button size="small" type="link" onClick={() => setSelectedFile(r)}>预览</Button>
164
- <Button size="small" type="link" icon={<CopyOutlined/>} onClick={() => handleCopy(r.content)}>复制</Button>
165
- <Button size="small" type="link" icon={<DownloadOutlined/>} onClick={() => handleDownload(r)}>下载</Button>
166
- </Space>
167
- )
168
- },
169
- ]
170
-
171
- return (
172
- <div style={{padding: 16}}>
173
- <Card>
174
- <Alert type="info" showIcon icon={<CodeOutlined/>} style={{marginBottom: 16}}
175
- message={`z-lc 代码物化 (FEATURE006 T1) — App: ${appCode}`}
176
- description="将低代码定义的 entity/field 渲染为可编译的 Java (Entity/Mapper/Service/Controller) + React 页面代码。导出是异步的, 完成后可在此查看文件清单。"/>
177
-
178
- <Space style={{marginBottom: 16}}>
179
- <Button icon={<ReloadOutlined/>} onClick={loadBatches}>刷新</Button>
180
- <Button type="primary" icon={<ExportOutlined/>} onClick={() => setTriggerOpen(true)}>导出代码</Button>
181
- <Button icon={<HistoryOutlined/>} onClick={() => message.info(`共 ${batches.length} 个历史批次`)}>历史</Button>
182
- </Space>
183
-
184
- <Table dataSource={batches} columns={batchColumns} rowKey="id" loading={loading}
185
- pagination={{pageSize: 10}} size="small" scroll={{x: 1000}}/>
186
- </Card>
187
-
188
- <Modal title="触发代码物化" open={triggerOpen} onCancel={() => setTriggerOpen(false)} onOk={handleTrigger}
189
- okText="开始导出" width={600}>
190
- <Form form={form} layout="vertical" style={{marginTop: 16}}
191
- initialValues={{materializationPath: `app-${appCode}/v1`}}>
192
- <Form.Item name="materializationPath" label="导出根路径 (相对沙箱默认根)"
193
- rules={[{required: true, message: '请输入路径'}]}
194
- extra="默认根: ${user.home}/z-lc-materialized. 如需写入绝对路径, 服务端需配置 z-lc.materialize.allow-absolute-path=true">
195
- <Input placeholder={`app-${appCode}/v1`}/>
196
- </Form.Item>
197
- <Form.Item name="entityCodes" label="导出的 entity (留空 = 全量)">
198
- <Select mode="multiple" placeholder="选择 entity" allowClear
199
- options={entities.map(e => ({value: e.entityCode, label: `${e.entityCode} (${e.tableName})`}))}/>
200
- </Form.Item>
201
- <Form.Item name="description" label="备注">
202
- <TextArea rows={2} placeholder="本次导出的业务说明"/>
203
- </Form.Item>
204
- </Form>
205
- </Modal>
206
-
207
- <Modal title={`物化文件清单 (${diffFiles.length} 个)`} open={diffOpen}
208
- onCancel={() => { setDiffOpen(false); setSelectedFile(null) }}
209
- footer={<Button onClick={() => { setDiffOpen(false); setSelectedFile(null) }}>关闭</Button>}
210
- width={selectedFile ? 1100 : 800}>
211
- {selectedFile ? (
212
- <div>
213
- <Space style={{marginBottom: 8}}>
214
- <Button onClick={() => setSelectedFile(null)}>← 返回清单</Button>
215
- <Text strong>{selectedFile.relativePath}</Text>
216
- <Tag color={TYPE_COLORS[selectedFile.type] || 'default'}>{selectedFile.type}</Tag>
217
- <Button size="small" icon={<CopyOutlined/>} onClick={() => handleCopy(selectedFile.content)}>复制</Button>
218
- <Button size="small" icon={<DownloadOutlined/>} onClick={() => handleDownload(selectedFile)}>下载</Button>
219
- </Space>
220
- <pre style={{
221
- background: '#1e1e1e', color: '#d4d4d4', padding: 16,
222
- borderRadius: 8, maxHeight: 500, overflow: 'auto',
223
- fontSize: 12, fontFamily: 'Menlo, Monaco, monospace',
224
- }}>
225
- {selectedFile.content}
226
- </pre>
227
- </div>
228
- ) : (
229
- <Table dataSource={diffFiles} columns={fileColumns} rowKey="relativePath"
230
- pagination={{pageSize: 20}} size="small" scroll={{x: 700}}/>
231
- )}
232
- </Modal>
233
- </div>
234
- )
235
- }
@@ -1 +0,0 @@
1
- export {default} from './LowCodePage'