@a2simcode/ui 0.0.136 → 0.0.137

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.
@@ -67,6 +67,19 @@
67
67
  </template>
68
68
  </Demo>
69
69
 
70
+ ## 数据表模型页面示例
71
+
72
+ 参考业务项目中常见的“左侧树 / 右侧表格”的写法:点击左侧数据库树切换数据源;右侧使用 `j-table-panel` 展示表模型列表,并支持“新增 / 导入 / 编辑 / 删除 / 同步 / 更新”等常见操作;通过 `j-page.call('mainTable','refreshData')` 刷新表格。
73
+
74
+ <Demo :source-code="pageCodeTableModelCode">
75
+ <template #source>
76
+ <page-code-table-model />
77
+ </template>
78
+ <template #description>
79
+ 示例没有依赖额外的 page 配置文件,直接在 Demo 内部定义 schema、按钮与动作,适合用于演示 `j-page` 的 Schema 页面组织能力。
80
+ </template>
81
+ </Demo>
82
+
70
83
  ## API
71
84
 
72
85
  <ApiTable :data="pageApi" componentName="page" />
@@ -77,6 +90,7 @@ import PageInit from '../examples/page/init.vue'
77
90
  import PageLog from '../examples/page/log.vue'
78
91
  import PageUserManagement from '../examples/page/user-management.vue'
79
92
  import PageDeptUserManagement from '../examples/page/dept-user-management.vue'
93
+ import PageCodeTableModel from '../examples/page/code-table-model.vue'
80
94
  import pageApi from './meta/page'
81
95
 
82
96
  import pageBasicCode from '../examples/page/basic.vue?raw'
@@ -84,4 +98,5 @@ import pageInitCode from '../examples/page/init.vue?raw'
84
98
  import pageLogCode from '../examples/page/log.vue?raw'
85
99
  import pageUserManagementCode from '../examples/page/user-management.vue?raw'
86
100
  import pageDeptUserManagementCode from '../examples/page/dept-user-management.vue?raw'
101
+ import pageCodeTableModelCode from '../examples/page/code-table-model.vue?raw'
87
102
  </script>
@@ -0,0 +1,428 @@
1
+ <template>
2
+ <div class="j-block" style="height: 600px">
3
+ <j-page ref="pageRef" :noPadding="true" :schema="schema" />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import type { ButtonClickScope, TableColumnCompConfig } from '@a2simcode/ui'
9
+
10
+ import { ref } from 'vue'
11
+ import { ElMessage, ElMessageBox } from 'element-plus'
12
+
13
+ interface DbNode {
14
+ id: string
15
+ label: string
16
+ }
17
+
18
+ interface TableRecord {
19
+ j_Id: number
20
+ j_TableName: string
21
+ j_Description: string
22
+ j_Des?: string
23
+ j_State: 0 | 1
24
+ }
25
+
26
+ interface ColumnRecord {
27
+ j_DbColumnName: string
28
+ j_CsType: string
29
+ j_Length: number
30
+ j_DecimalDigits: number
31
+ j_IsNullable: 0 | 1
32
+ j_IsPrimaryKey: 0 | 1
33
+ j_IsIdentity: 0 | 1
34
+ j_Des: string
35
+ }
36
+
37
+ const pageRef = ref()
38
+
39
+ const refreshTable = (reset = false) => {
40
+ pageRef.value?.call('mainTable', reset ? 'resetData' : 'refreshData')
41
+ }
42
+
43
+ const dbList = ref<DbNode[]>([
44
+ { id: 'sysDblink', label: '系统数据库' },
45
+ { id: 'bizDblink', label: '业务数据库' },
46
+ { id: 'reportDblink', label: '报表数据库' },
47
+ ])
48
+
49
+ const selectedDbCode = ref<string>('sysDblink')
50
+ const selectedDbName = ref<string>('系统数据库')
51
+
52
+ const tablesByDb = ref<Record<string, TableRecord[]>>({
53
+ sysDblink: [
54
+ { j_Id: 1, j_TableName: 'SYS_USER', j_Description: '用户表', j_State: 1 },
55
+ { j_Id: 2, j_TableName: 'SYS_ROLE', j_Description: '角色表', j_State: 1 },
56
+ { j_Id: 3, j_TableName: 'SYS_LOG', j_Description: '日志表', j_State: 0 },
57
+ ],
58
+ bizDblink: [
59
+ { j_Id: 11, j_TableName: 'BIZ_ORDER', j_Description: '订单表', j_State: 0 },
60
+ { j_Id: 12, j_TableName: 'BIZ_CUSTOMER', j_Description: '客户表', j_State: 1 },
61
+ ],
62
+ reportDblink: [{ j_Id: 21, j_TableName: 'RPT_DAILY', j_Description: '日报表', j_State: 0 }],
63
+ })
64
+
65
+ const columnsByTableId = ref<Record<number, ColumnRecord[]>>({
66
+ 1: [
67
+ {
68
+ j_DbColumnName: 'j_Id',
69
+ j_CsType: 'int',
70
+ j_Length: 0,
71
+ j_DecimalDigits: 0,
72
+ j_IsNullable: 0,
73
+ j_IsPrimaryKey: 1,
74
+ j_IsIdentity: 1,
75
+ j_Des: '主键',
76
+ },
77
+ {
78
+ j_DbColumnName: 'j_Account',
79
+ j_CsType: 'nvarchar',
80
+ j_Length: 50,
81
+ j_DecimalDigits: 0,
82
+ j_IsNullable: 0,
83
+ j_IsPrimaryKey: 0,
84
+ j_IsIdentity: 0,
85
+ j_Des: '账号',
86
+ },
87
+ {
88
+ j_DbColumnName: 'j_Name',
89
+ j_CsType: 'nvarchar',
90
+ j_Length: 50,
91
+ j_DecimalDigits: 0,
92
+ j_IsNullable: 1,
93
+ j_IsPrimaryKey: 0,
94
+ j_IsIdentity: 0,
95
+ j_Des: '姓名',
96
+ },
97
+ ],
98
+ 2: [
99
+ {
100
+ j_DbColumnName: 'j_Id',
101
+ j_CsType: 'int',
102
+ j_Length: 0,
103
+ j_DecimalDigits: 0,
104
+ j_IsNullable: 0,
105
+ j_IsPrimaryKey: 1,
106
+ j_IsIdentity: 1,
107
+ j_Des: '主键',
108
+ },
109
+ {
110
+ j_DbColumnName: 'j_Name',
111
+ j_CsType: 'nvarchar',
112
+ j_Length: 50,
113
+ j_DecimalDigits: 0,
114
+ j_IsNullable: 0,
115
+ j_IsPrimaryKey: 0,
116
+ j_IsIdentity: 0,
117
+ j_Des: '角色名称',
118
+ },
119
+ ],
120
+ })
121
+
122
+ const getCurrentTables = () => tablesByDb.value[selectedDbCode.value] || []
123
+
124
+ const api = {
125
+ getTablePage: async (dbCode: string, params: Record<string, any>) => {
126
+ const keyword = String(params?.keyword?.value || '').trim()
127
+ const { pagination } = params || {}
128
+ const { rows = 10, page = 1 } = pagination || {}
129
+
130
+ let list = [...(tablesByDb.value[dbCode] || [])]
131
+ if (keyword) {
132
+ list = list.filter(
133
+ (t) => t.j_TableName.includes(keyword) || t.j_Description.includes(keyword)
134
+ )
135
+ }
136
+
137
+ const start = (page - 1) * rows
138
+ const end = start + rows
139
+
140
+ return {
141
+ rows: list.slice(start, end).map((item) => ({
142
+ ...item,
143
+ children: (columnsByTableId.value[item.j_Id] || []).length > 0,
144
+ })),
145
+ records: list.length,
146
+ }
147
+ },
148
+ get: async (id: number) => {
149
+ return { codecolumnsList: columnsByTableId.value[id] || [] }
150
+ },
151
+ remove: async (id: number) => {
152
+ const list = getCurrentTables()
153
+ const idx = list.findIndex((t) => t.j_Id === id)
154
+ if (idx < 0) return false
155
+ list.splice(idx, 1)
156
+ return true
157
+ },
158
+ importTable: async (
159
+ dbCode: string,
160
+ postData: { tableList: Array<{ name: string; description?: string }> }
161
+ ) => {
162
+ const list = tablesByDb.value[dbCode] || (tablesByDb.value[dbCode] = [])
163
+ const nowId =
164
+ Math.max(
165
+ 0,
166
+ ...Object.values(tablesByDb.value)
167
+ .flat()
168
+ .map((t) => t.j_Id)
169
+ ) + 1
170
+ postData.tableList.forEach((t, i) => {
171
+ const exist = list.find((x) => x.j_TableName === t.name)
172
+ if (exist) {
173
+ exist.j_State = 1
174
+ exist.j_Description = t.description || exist.j_Description
175
+ return
176
+ }
177
+ list.unshift({
178
+ j_Id: nowId + i,
179
+ j_TableName: t.name,
180
+ j_Description: t.description || '导入表',
181
+ j_State: 1,
182
+ })
183
+ })
184
+ return true
185
+ },
186
+ createOrUpdateTable: async (id: number, dbCode: string, dbName: string) => {
187
+ const list = tablesByDb.value[dbCode] || []
188
+ const target = list.find((t) => t.j_Id === id)
189
+ if (!target) return false
190
+ ElMessage.success(`已将【${target.j_TableName}】的设置更新到【${dbName}】`)
191
+ return true
192
+ },
193
+ }
194
+
195
+ const columnsConfig: Record<string, TableColumnCompConfig> = {
196
+ j_TableName: { minWidth: 200, filter: { isSearchKeyword: true } },
197
+ j_Description: { minWidth: 200, filter: { isSearchKeyword: true } },
198
+ j_State: {
199
+ type: 'j-tag',
200
+ width: 72,
201
+ dataType: 'options',
202
+ options: [
203
+ { label: '同步', value: 1, color: '#fff7e8' },
204
+ { label: '未同步', value: 0, color: '#e6f7ff' },
205
+ ],
206
+ },
207
+ }
208
+
209
+ const columns = [
210
+ { id: 'j_TableName', label: '表名' },
211
+ { id: 'j_Description', label: '说明' },
212
+ { id: 'j_State', label: '状态' },
213
+ ].map((item) => {
214
+ const cfg = columnsConfig[item.id] || {}
215
+ return {
216
+ id: item.id,
217
+ type: cfg.type || '',
218
+ config: { ...cfg, label: item.label },
219
+ }
220
+ })
221
+
222
+ const subColumns = [
223
+ { id: 'j_DbColumnName', config: { label: '名称', minWidth: 120 } },
224
+ { id: 'j_CsType', config: { label: '类型', width: 120 } },
225
+ { id: 'j_Length', config: { label: '长度', width: 80 } },
226
+ { id: 'j_DecimalDigits', config: { label: '小数位', width: 64 } },
227
+ {
228
+ id: 'j_IsNullable',
229
+ type: 'j-switch',
230
+ config: { label: '可空', width: 64, align: 'center', activeValue: 1, inactiveValue: 0 },
231
+ },
232
+ {
233
+ id: 'j_IsPrimaryKey',
234
+ type: 'j-switch',
235
+ config: { label: '主键', width: 64, align: 'center', activeValue: 1, inactiveValue: 0 },
236
+ },
237
+ {
238
+ id: 'j_IsIdentity',
239
+ type: 'j-switch',
240
+ config: { label: '自增', width: 64, align: 'center', activeValue: 1, inactiveValue: 0 },
241
+ },
242
+ { id: 'j_Des', config: { label: '备注', minWidth: 120 } },
243
+ ]
244
+
245
+ const buttons = [
246
+ {
247
+ id: 'add',
248
+ label: '新增',
249
+ config: { type: 'primary', icon: 'mdi:add' },
250
+ click: async () => {
251
+ const res = await ElMessageBox.prompt('请输入表名', '新增', {
252
+ confirmButtonText: '确定',
253
+ cancelButtonText: '取消',
254
+ inputValue: '',
255
+ }).catch(() => null)
256
+ const name = String(res?.value || '').trim()
257
+ if (!name) return
258
+
259
+ const list = getCurrentTables()
260
+ const nextId =
261
+ Math.max(
262
+ 0,
263
+ ...Object.values(tablesByDb.value)
264
+ .flat()
265
+ .map((t) => t.j_Id)
266
+ ) + 1
267
+ list.unshift({ j_Id: nextId, j_TableName: name, j_Description: '新建表', j_State: 0 })
268
+ ElMessage.success('新增成功')
269
+ refreshTable(true)
270
+ },
271
+ },
272
+ {
273
+ id: 'import',
274
+ label: '导入表',
275
+ config: { icon: 'mdi:table-upload' },
276
+ click: async () => {
277
+ await api.importTable(selectedDbCode.value, {
278
+ tableList: [
279
+ { name: 'NEW_TABLE_1', description: '导入示例表-1' },
280
+ { name: 'NEW_TABLE_2', description: '导入示例表-2' },
281
+ ],
282
+ })
283
+ ElMessage.success('导入成功')
284
+ refreshTable(true)
285
+ },
286
+ },
287
+ ]
288
+
289
+ const actions = [
290
+ {
291
+ id: 'edit',
292
+ label: '编辑',
293
+ click: async ({ data }: ButtonClickScope) => {
294
+ const res = await ElMessageBox.prompt('请输入说明', `编辑-${data?.j_TableName}`, {
295
+ confirmButtonText: '确定',
296
+ cancelButtonText: '取消',
297
+ inputValue: data?.j_Description || '',
298
+ }).catch(() => null)
299
+ const desc = String(res?.value || '').trim()
300
+ if (!desc) return
301
+
302
+ const list = getCurrentTables()
303
+ const target = list.find((t) => t.j_Id === data?.j_Id)
304
+ if (!target) return
305
+ target.j_Description = desc
306
+ ElMessage.success('保存成功')
307
+ refreshTable()
308
+ },
309
+ },
310
+ {
311
+ id: 'delete',
312
+ label: '删除',
313
+ config: { danger: true },
314
+ click: async ({ data }: ButtonClickScope) => {
315
+ await ElMessageBox.confirm(`您确定删除【${data?.j_TableName}】吗?`, '提示', {
316
+ type: 'warning',
317
+ confirmButtonText: '确定',
318
+ cancelButtonText: '取消',
319
+ })
320
+ const res = await api.remove(data?.j_Id)
321
+ if (res) {
322
+ ElMessage.success('删除成功')
323
+ refreshTable(true)
324
+ }
325
+ },
326
+ },
327
+ {
328
+ id: 'update',
329
+ label: '同步',
330
+ click: async ({ data }: ButtonClickScope) => {
331
+ await ElMessageBox.confirm(`您确定同步表【${data?.j_TableName}】到系统中吗?`, '提示', {
332
+ type: 'warning',
333
+ confirmButtonText: '确定',
334
+ cancelButtonText: '取消',
335
+ })
336
+ await api.importTable(selectedDbCode.value, {
337
+ tableList: [{ name: data?.j_TableName, description: data?.j_Des || data?.j_Description }],
338
+ })
339
+ ElMessage.success('同步成功')
340
+ refreshTable()
341
+ },
342
+ },
343
+ {
344
+ id: 'updateTable',
345
+ label: '更新',
346
+ click: async ({ data }: ButtonClickScope) => {
347
+ await ElMessageBox.confirm(
348
+ `您确定更新设置数据【${data?.j_TableName}】到数据库中吗?`,
349
+ '提示',
350
+ {
351
+ type: 'warning',
352
+ confirmButtonText: '确定',
353
+ cancelButtonText: '取消',
354
+ }
355
+ )
356
+ const res = await api.createOrUpdateTable(
357
+ data?.j_Id,
358
+ selectedDbCode.value,
359
+ selectedDbName.value
360
+ )
361
+ if (res) {
362
+ refreshTable()
363
+ }
364
+ },
365
+ },
366
+ ]
367
+
368
+ const schema = ref([
369
+ {
370
+ type: 'j-layout',
371
+ children: [
372
+ {
373
+ type: 'j-panel',
374
+ slot: 'left',
375
+ config: { title: '数据库' },
376
+ children: [
377
+ {
378
+ id: 'tree',
379
+ type: 'j-tree',
380
+ config: {
381
+ valueKey: 'id',
382
+ labelKey: 'label',
383
+ pidKey: 'id',
384
+ loadData: async () => dbList.value,
385
+ afterLoadData: () => {
386
+ const first = dbList.value?.[0]
387
+ selectedDbCode.value = first?.id || ''
388
+ selectedDbName.value = first?.label || ''
389
+ pageRef.value?.call('tree', 'setValue', null, selectedDbCode.value)
390
+ refreshTable(true)
391
+ },
392
+ click: (value: any, node: any) => {
393
+ if (value === selectedDbCode.value) return
394
+ selectedDbCode.value = node?.id || value
395
+ selectedDbName.value = node?.label || ''
396
+ refreshTable(true)
397
+ },
398
+ },
399
+ },
400
+ ],
401
+ },
402
+ {
403
+ id: 'mainTable',
404
+ type: 'j-table-panel',
405
+ config: {
406
+ columns,
407
+ subColumns,
408
+ rowKey: 'j_Id',
409
+ isPage: true,
410
+ buttons,
411
+ actions,
412
+ isSubShowNumber: true,
413
+ sort: 'j_TableName DESC',
414
+ immediate: false,
415
+ loadData: async (params: Record<string, any>) => {
416
+ if (!selectedDbCode.value) return { rows: [], records: 0 }
417
+ return api.getTablePage(selectedDbCode.value, params)
418
+ },
419
+ loadChildren: async (row: any) => {
420
+ const detail = await api.get(row?.j_Id)
421
+ return detail?.codecolumnsList || []
422
+ },
423
+ },
424
+ },
425
+ ],
426
+ },
427
+ ])
428
+ </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2simcode/ui",
3
- "version": "0.0.136",
3
+ "version": "0.0.137",
4
4
  "description": "A Vue 3 UI Component Library",
5
5
  "type": "module",
6
6
  "main": "./dist/simcode-ui.umd.js",