jianghu-ui 1.0.6 → 1.0.7
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/jianghu-ui.css +181 -110
- package/dist/jianghu-ui.js +1 -1
- package/package.json +1 -1
- package/src/components/JhDrawer/JhDrawer.stories.js +6 -6
- package/src/components/JhDrawerForm/JhDrawerForm.vue +1 -1
- package/src/components/JhForm/JhForm.stories.js +114 -95
- package/src/components/JhForm/JhForm.vue +148 -21
- package/src/components/JhFormFields/JhFormFields.vue +33 -14
- package/src/components/JhModal/JhModal.stories.js +6 -6
- package/src/components/JhModal/JhModal.vue +1 -1
- package/src/components/JhModalForm/JhModalForm.vue +1 -1
- package/src/components/JhTable/JhTable.stories.js +134 -167
- package/src/components/JhTable/JhTable.vue +83 -23
- package/src/style/globalCSSVuetifyV4.css +1 -2
- package/src/components/JhAddressSelect/JhAddressSelect.md +0 -267
- package/src/components/JhCard/JhCard.md +0 -246
- package/src/components/JhCheckCard/JhCheckCard.md +0 -245
- package/src/components/JhConfirmDialog/JhConfirmDialog.md +0 -70
- package/src/components/JhDateRangePicker/JhDateRangePicker.md +0 -56
- package/src/components/JhDescriptions/JhDescriptions.md +0 -724
- package/src/components/JhDraggable/JhDraggable.md +0 -66
- package/src/components/JhDrawer/JhDrawer.md +0 -68
- package/src/components/JhDrawerForm/JhDrawerForm.md +0 -69
- package/src/components/JhEditableTable/JhEditableTable.md +0 -507
- package/src/components/JhFileInput/JhFileInput.md +0 -56
- package/src/components/JhForm/JhForm.md +0 -676
- package/src/components/JhFormFields/JhFormFields.md +0 -647
- package/src/components/JhFormList/JhFormList.md +0 -303
- package/src/components/JhJsonEditor/JhJsonEditor.md +0 -54
- package/src/components/JhLayout/JhLayout.md +0 -580
- package/src/components/JhList/JhList.md +0 -441
- package/src/components/JhMarkdownEditor/JhMarkdownEditor.md +0 -56
- package/src/components/JhMask/JhMask.md +0 -62
- package/src/components/JhMenu/JhMenu.md +0 -85
- package/src/components/JhModal/JhModal.md +0 -68
- package/src/components/JhModalForm/JhModalForm.md +0 -69
- package/src/components/JhPageContainer/JhPageContainer.md +0 -409
- package/src/components/JhQueryFilter/JhQueryFilter.md +0 -77
- package/src/components/JhScene/JhScene.md +0 -64
- package/src/components/JhStatisticCard/JhStatisticCard.md +0 -363
- package/src/components/JhStepsForm/JhStepsForm.md +0 -666
- package/src/components/JhTable/JhTable.md +0 -730
- package/src/components/JhTableAttachment/JhTableAttachment.md +0 -70
- package/src/components/JhToast/JhToast.md +0 -67
- package/src/components/JhTreeSelect/JhTreeSelect.md +0 -82
- package/src/components/JhWaterMark/JhWaterMark.md +0 -190
- package/src/components/README.md +0 -52
|
@@ -101,6 +101,14 @@ export default {
|
|
|
101
101
|
control: 'boolean',
|
|
102
102
|
description: '是否显示选择框',
|
|
103
103
|
},
|
|
104
|
+
rowKey: {
|
|
105
|
+
control: 'text',
|
|
106
|
+
description: '行唯一标识字段,默认为 "id"',
|
|
107
|
+
},
|
|
108
|
+
itemKey: {
|
|
109
|
+
control: 'text',
|
|
110
|
+
description: 'rowKey 的别名,与 v-data-table API 保持一致',
|
|
111
|
+
},
|
|
104
112
|
size: {
|
|
105
113
|
control: 'select',
|
|
106
114
|
options: ['default', 'medium', 'compact'],
|
|
@@ -144,97 +152,51 @@ export default {
|
|
|
144
152
|
description: {
|
|
145
153
|
component: `
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
##
|
|
150
|
-
|
|
151
|
-
### 1.
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
- **全屏切换**:全屏显示表格(可选)
|
|
157
|
-
|
|
158
|
-
### 2. 列增强功能
|
|
159
|
-
- **copyable**:显示复制按钮,一键复制单元格内容
|
|
160
|
-
- **ellipsis**:超长文本自动省略,鼠标悬停显示完整内容
|
|
161
|
-
- **tooltip**:鼠标悬停提示
|
|
162
|
-
- **自定义插槽**:完全自定义列渲染
|
|
163
|
-
|
|
164
|
-
### 3. 操作列配置化
|
|
165
|
-
\`\`\`javascript
|
|
166
|
-
actionColumn: {
|
|
167
|
-
title: '操作',
|
|
168
|
-
width: 180,
|
|
169
|
-
fixed: 'right',
|
|
170
|
-
buttons: [
|
|
171
|
-
{
|
|
172
|
-
text: '编辑',
|
|
173
|
-
type: 'link', // link / icon / button
|
|
174
|
-
icon: 'mdi-pencil',
|
|
175
|
-
color: 'primary',
|
|
176
|
-
tooltip: '编辑记录',
|
|
177
|
-
onClick: (row) => { console.log('编辑', row) },
|
|
178
|
-
visible: (row) => row.status !== '禁用',
|
|
179
|
-
confirm: '确认编辑?'
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
text: '删除',
|
|
183
|
-
type: 'link',
|
|
184
|
-
icon: 'mdi-delete',
|
|
185
|
-
color: 'error',
|
|
186
|
-
onClick: (row) => { console.log('删除', row) },
|
|
187
|
-
confirm: '确认删除?'
|
|
188
|
-
}
|
|
189
|
-
]
|
|
190
|
-
}
|
|
191
|
-
\`\`\`
|
|
192
|
-
|
|
193
|
-
### 4. 服务端分页
|
|
194
|
-
通过 \`request\` prop 支持服务端分页:
|
|
195
|
-
\`\`\`javascript
|
|
196
|
-
async fetchData(params) {
|
|
197
|
-
// params: { page, pageSize, search, sorter, filters }
|
|
198
|
-
const response = await fetch('/api/users', {
|
|
199
|
-
method: 'POST',
|
|
200
|
-
body: JSON.stringify(params),
|
|
201
|
-
});
|
|
202
|
-
return {
|
|
203
|
-
data: response.list,
|
|
204
|
-
total: response.total,
|
|
205
|
-
success: true,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
\`\`\`
|
|
155
|
+
增强版数据表格组件,深度继承 Vuetify 2 的 v-data-table,并提供 Pro 级功能。
|
|
156
|
+
|
|
157
|
+
## 核心特性
|
|
158
|
+
|
|
159
|
+
### 1. 深度继承
|
|
160
|
+
- **API 兼容**:支持 v-data-table 的所有 Props(如 \`show-expand\`、\`group-by\`、\`item-class\` 等)。
|
|
161
|
+
- **插槽透传**:支持所有原生插槽(如 \`top\`、\`footer\`、\`expanded-item\` 等)以及自定义列插槽 \`item.<value>\`。
|
|
162
|
+
- **事件透传**:支持所有原生事件(如 \`click:row\`、\`current-items\` 等)。
|
|
163
|
+
- **实例访问**:通过 \`getVDataTable()\` 获取底层 v-data-table 实例。
|
|
209
164
|
|
|
210
|
-
###
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
165
|
+
### 2. 工具栏与高级筛选
|
|
166
|
+
- **集成搜索**:工具栏内置全局搜索。
|
|
167
|
+
- **列设置**:支持运行时列显示/隐藏切换。
|
|
168
|
+
- **密度切换**:支持 default/medium/compact 三种视觉密度。
|
|
169
|
+
- **高级筛选**:可集成 JhQueryFilter 实现复杂的组合查询。
|
|
214
170
|
|
|
215
|
-
###
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
|
|
171
|
+
### 3. 列与单元格增强
|
|
172
|
+
- **Schema 渲染**:支持 \`valueType\` (status, progress, money, date, json 等) 自动化渲染。
|
|
173
|
+
- **交互功能**:支持 \`copyable\` (一键复制) 和 \`ellipsis\` (超长省略)。
|
|
174
|
+
- **操作列配置**:支持配置化的按钮列表,内置权限控制和二次确认。
|
|
175
|
+
|
|
176
|
+
### 4. 数据流控制
|
|
177
|
+
- **服务端分页**:通过 \`request\` 自动处理请求、分页、排序。
|
|
178
|
+
- **状态持久化**:支持列显示状态持久化。
|
|
220
179
|
|
|
221
180
|
## 方法
|
|
222
181
|
|
|
182
|
+
- \`getVDataTable()\`: 获取底层 v-data-table 实例
|
|
223
183
|
- \`reload()\`: 重新加载数据(服务端分页)
|
|
224
184
|
- \`reset()\`: 重置到第一页
|
|
225
185
|
- \`clearSelection()\`: 清空选择
|
|
226
186
|
- \`getSelectedRows()\`: 获取选中的行
|
|
227
187
|
|
|
228
|
-
## 事件
|
|
188
|
+
## 事件 (除 v-data-table 原生事件外)
|
|
229
189
|
|
|
230
190
|
- \`create-click\`: 点击新增按钮
|
|
231
|
-
- \`update-click\`:
|
|
232
|
-
- \`delete-click\`:
|
|
233
|
-
- \`row-click\`: 点击行(item, event)
|
|
191
|
+
- \`update-click\`: 点击详情按钮(针对 actionColumn 默认按钮)
|
|
192
|
+
- \`delete-click\`: 点击删除按钮(针对 actionColumn 默认按钮)
|
|
234
193
|
- \`selection-change\`: 选择改变 ({ selectedRowKeys, selectedRows })
|
|
235
|
-
- \`
|
|
236
|
-
- \`
|
|
237
|
-
- \`
|
|
194
|
+
- \`columns-state-change\`: 列状态改变 (snapshot)
|
|
195
|
+
- \`filter-search\`: 高级筛选查询
|
|
196
|
+
- \`filter-reset\`: 高级筛选重置
|
|
197
|
+
- \`sort-change\`: 排序改变
|
|
198
|
+
- \`copy-success\`: 复制成功 (text)
|
|
199
|
+
- \`request-error\`: 请求失败 (error)
|
|
238
200
|
`,
|
|
239
201
|
},
|
|
240
202
|
},
|
|
@@ -253,7 +215,7 @@ export const 基础示例 = {
|
|
|
253
215
|
showUpdateAction: true,
|
|
254
216
|
showDeleteAction: true,
|
|
255
217
|
showSelect: false,
|
|
256
|
-
size: '
|
|
218
|
+
size: 'medium',
|
|
257
219
|
},
|
|
258
220
|
render: (args) => ({
|
|
259
221
|
components: { JhTable },
|
|
@@ -857,16 +819,20 @@ export const 自定义列渲染 = {
|
|
|
857
819
|
<div>
|
|
858
820
|
<jh-table v-bind="args">
|
|
859
821
|
<!-- 自定义状态列 -->
|
|
860
|
-
<template v-slot:item.status="{ item }">
|
|
822
|
+
<template v-slot:item.status="{ item, index }">
|
|
861
823
|
<v-chip
|
|
862
824
|
:color="item.status === '启用' ? 'success' : 'error'"
|
|
863
825
|
small
|
|
864
826
|
label
|
|
865
827
|
>
|
|
866
|
-
<v-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
828
|
+
<v-badge
|
|
829
|
+
v-if="index === 0"
|
|
830
|
+
dot
|
|
831
|
+
inline
|
|
832
|
+
color="red"
|
|
833
|
+
class="mr-1"
|
|
834
|
+
></v-badge>
|
|
835
|
+
{{ item.status }} (索引: {{ index }})
|
|
870
836
|
</v-chip>
|
|
871
837
|
</template>
|
|
872
838
|
</jh-table>
|
|
@@ -1360,89 +1326,6 @@ export const 完整CRUD示例 = {
|
|
|
1360
1326
|
};
|
|
1361
1327
|
|
|
1362
1328
|
|
|
1363
|
-
export const SelectionAlertRender = {
|
|
1364
|
-
render: () => ({
|
|
1365
|
-
components: { JhTable },
|
|
1366
|
-
data() {
|
|
1367
|
-
return {
|
|
1368
|
-
headers: [
|
|
1369
|
-
{ text: '????', value: 'name' },
|
|
1370
|
-
{ text: '????', value: 'containers' },
|
|
1371
|
-
{ text: '????', value: 'calls' },
|
|
1372
|
-
{ text: '???', value: 'owner' },
|
|
1373
|
-
{ text: '????', value: 'createdAt' }
|
|
1374
|
-
],
|
|
1375
|
-
items: [
|
|
1376
|
-
{ id: 1, name: 'AppName-0', containers: 8, calls: 1276, owner: '???', createdAt: '2025-11-23' },
|
|
1377
|
-
{ id: 2, name: 'AppName-1', containers: 12, calls: 938, owner: '???', createdAt: '2025-11-23' },
|
|
1378
|
-
{ id: 3, name: 'AppName-2', containers: 1, calls: 820, owner: '???', createdAt: '2025-11-23' },
|
|
1379
|
-
{ id: 4, name: 'AppName-3', containers: 5, calls: 1291, owner: '???', createdAt: '2025-11-23' },
|
|
1380
|
-
{ id: 5, name: 'AppName-4', containers: 17, calls: 1796, owner: '???', createdAt: '2025-11-23' }
|
|
1381
|
-
]
|
|
1382
|
-
};
|
|
1383
|
-
},
|
|
1384
|
-
methods: {
|
|
1385
|
-
renderAlert(h, { selectedRows, onCleanSelected }) {
|
|
1386
|
-
const totalContainers = selectedRows.reduce((sum, row) => sum + row.containers, 0);
|
|
1387
|
-
const totalCalls = selectedRows.reduce((sum, row) => sum + row.calls, 0);
|
|
1388
|
-
return h(
|
|
1389
|
-
'div',
|
|
1390
|
-
{ class: 'd-flex align-center flex-wrap', style: { gap: '12px' } },
|
|
1391
|
-
[
|
|
1392
|
-
h('span', { class: 'font-weight-medium' }, `?? ${selectedRows.length} ?`),
|
|
1393
|
-
h(
|
|
1394
|
-
'v-btn',
|
|
1395
|
-
{
|
|
1396
|
-
props: { text: true, xSmall: true, color: 'primary' },
|
|
1397
|
-
on: { click: onCleanSelected }
|
|
1398
|
-
},
|
|
1399
|
-
'????'
|
|
1400
|
-
),
|
|
1401
|
-
h('span', { class: 'grey--text text--darken-1' }, `?????${totalContainers} ?`),
|
|
1402
|
-
h('span', { class: 'grey--text text--darken-1' }, `????${totalCalls} ?`)
|
|
1403
|
-
]
|
|
1404
|
-
);
|
|
1405
|
-
},
|
|
1406
|
-
renderAlertActions(h, { selectedRows }) {
|
|
1407
|
-
return h('div', { class: 'd-flex align-center', style: { gap: '8px' } }, [
|
|
1408
|
-
h(
|
|
1409
|
-
'v-btn',
|
|
1410
|
-
{
|
|
1411
|
-
props: { small: true, color: 'error' },
|
|
1412
|
-
on: { click: () => this.handleBatchDelete(selectedRows) }
|
|
1413
|
-
},
|
|
1414
|
-
'????'
|
|
1415
|
-
),
|
|
1416
|
-
h(
|
|
1417
|
-
'v-btn',
|
|
1418
|
-
{
|
|
1419
|
-
props: { small: true, outlined: true, color: 'primary' },
|
|
1420
|
-
on: { click: () => this.handleExport(selectedRows) }
|
|
1421
|
-
},
|
|
1422
|
-
'????'
|
|
1423
|
-
)
|
|
1424
|
-
]);
|
|
1425
|
-
},
|
|
1426
|
-
handleBatchDelete(rows) {
|
|
1427
|
-
alert(`???? ${rows.length} ?`);
|
|
1428
|
-
},
|
|
1429
|
-
handleExport(rows) {
|
|
1430
|
-
alert(`?? ${rows.length} ?`);
|
|
1431
|
-
}
|
|
1432
|
-
},
|
|
1433
|
-
template: `
|
|
1434
|
-
<jh-table
|
|
1435
|
-
:headers="headers"
|
|
1436
|
-
:items="items"
|
|
1437
|
-
show-select
|
|
1438
|
-
:toolbar="false"
|
|
1439
|
-
:table-alert-render="renderAlert"
|
|
1440
|
-
:table-alert-option-render="renderAlertActions"
|
|
1441
|
-
/>
|
|
1442
|
-
`
|
|
1443
|
-
})
|
|
1444
|
-
};
|
|
1445
|
-
|
|
1446
1329
|
// 受控服务端分页 (完全由父组件控制)
|
|
1447
1330
|
export const 受控服务端分页 = {
|
|
1448
1331
|
render: () => ({
|
|
@@ -1513,3 +1396,87 @@ export const 受控服务端分页 = {
|
|
|
1513
1396
|
`,
|
|
1514
1397
|
}),
|
|
1515
1398
|
};
|
|
1399
|
+
|
|
1400
|
+
// 原生插槽透传
|
|
1401
|
+
export const 原生插槽透传 = {
|
|
1402
|
+
render: () => ({
|
|
1403
|
+
components: { JhTable },
|
|
1404
|
+
data() {
|
|
1405
|
+
return {
|
|
1406
|
+
headers: sampleHeaders,
|
|
1407
|
+
items: sampleItems.slice(0, 5),
|
|
1408
|
+
};
|
|
1409
|
+
},
|
|
1410
|
+
template: `
|
|
1411
|
+
<jh-table :headers="headers" :items="items" header-title="原生插槽透传示例">
|
|
1412
|
+
<!-- 顶部插槽 -->
|
|
1413
|
+
<template v-slot:top>
|
|
1414
|
+
<v-toolbar flat color="blue lighten-4">
|
|
1415
|
+
<v-toolbar-title>这是 v-data-table 的 top 插槽</v-toolbar-title>
|
|
1416
|
+
<v-divider class="mx-4" inset vertical></v-divider>
|
|
1417
|
+
<v-spacer></v-spacer>
|
|
1418
|
+
<v-btn color="primary" dark class="mb-2">自定义按钮</v-btn>
|
|
1419
|
+
</v-toolbar>
|
|
1420
|
+
</template>
|
|
1421
|
+
|
|
1422
|
+
<!-- 底部插槽 -->
|
|
1423
|
+
<template v-slot:footer>
|
|
1424
|
+
<div class="pa-4 text-center grey lighten-3">
|
|
1425
|
+
这是 v-data-table 的 footer 插槽,可以放置汇总信息等
|
|
1426
|
+
</div>
|
|
1427
|
+
</template>
|
|
1428
|
+
|
|
1429
|
+
<!-- 自定义表头插槽 (原生方式) -->
|
|
1430
|
+
<template v-slot:header.username="{ header }">
|
|
1431
|
+
<span class="orange--text font-weight-bold italic">{{ header.text }} (自定义)</span>
|
|
1432
|
+
</template>
|
|
1433
|
+
</jh-table>
|
|
1434
|
+
`,
|
|
1435
|
+
}),
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
// 原生实例访问
|
|
1439
|
+
export const 原生实例访问 = {
|
|
1440
|
+
render: () => ({
|
|
1441
|
+
components: { JhTable },
|
|
1442
|
+
data() {
|
|
1443
|
+
return {
|
|
1444
|
+
headers: sampleHeaders,
|
|
1445
|
+
items: sampleItems,
|
|
1446
|
+
};
|
|
1447
|
+
},
|
|
1448
|
+
template: `
|
|
1449
|
+
<div>
|
|
1450
|
+
<div class="mb-4">
|
|
1451
|
+
<v-btn @click="callNativeSelectAll" color="primary">通过 Ref 调用原生全选 (selectAll)</v-btn>
|
|
1452
|
+
<v-btn @click="checkNativeInstance" color="secondary" class="ml-2">检查原生实例</v-btn>
|
|
1453
|
+
</div>
|
|
1454
|
+
<jh-table
|
|
1455
|
+
ref="jhTableRef"
|
|
1456
|
+
:headers="headers"
|
|
1457
|
+
:items="items"
|
|
1458
|
+
show-select
|
|
1459
|
+
header-title="原生实例访问示例"
|
|
1460
|
+
/>
|
|
1461
|
+
</div>
|
|
1462
|
+
`,
|
|
1463
|
+
methods: {
|
|
1464
|
+
callNativeSelectAll() {
|
|
1465
|
+
const vDataTable = this.$refs.jhTableRef.getVDataTable();
|
|
1466
|
+
if (vDataTable) {
|
|
1467
|
+
// 注意:Vuetify 2.x 的 selectAll 可能需要特定参数或通过内部 toggle 实现
|
|
1468
|
+
// 这里演示获取实例并操作
|
|
1469
|
+
vDataTable.toggleSelectAll(true);
|
|
1470
|
+
alert('已通过 getVDataTable().toggleSelectAll(true) 触发全选');
|
|
1471
|
+
}
|
|
1472
|
+
},
|
|
1473
|
+
checkNativeInstance() {
|
|
1474
|
+
const vDataTable = this.$refs.jhTableRef.getVDataTable();
|
|
1475
|
+
console.log('原生 v-data-table 实例:', vDataTable);
|
|
1476
|
+
alert('实例已打印到控制台,构造函数名:' + vDataTable.$options.name);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}),
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
|
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
/>
|
|
66
66
|
<template v-else>
|
|
67
67
|
<v-icon small class="mr-2" color="primary">mdi-checkbox-marked-circle</v-icon>
|
|
68
|
-
<
|
|
69
|
-
<v-
|
|
68
|
+
<div>已选择 <strong class="primary--text">{{ selectedItems.length }}</strong> 项</div>
|
|
69
|
+
<v-spacer></v-spacer>
|
|
70
|
+
<a text x-small class="ml-2" @click="clearSelection">清空</a>
|
|
70
71
|
</template>
|
|
71
72
|
</div>
|
|
72
73
|
<div v-if="hasAlertActionsContent" class="jh-pro-table-alert-actions">
|
|
@@ -254,7 +255,7 @@
|
|
|
254
255
|
:show-select="showSelectComputed"
|
|
255
256
|
:single-select="singleSelectComputed"
|
|
256
257
|
:value="selectedItems"
|
|
257
|
-
:item-key="
|
|
258
|
+
:item-key="computedRowKey"
|
|
258
259
|
:dense="tableDense"
|
|
259
260
|
:multi-sort="multiSort"
|
|
260
261
|
:must-sort="mustSort"
|
|
@@ -277,7 +278,7 @@
|
|
|
277
278
|
<slot :name="`header.${header.value}`" :header="h">{{ h.text || h.title }}</slot>
|
|
278
279
|
</template>
|
|
279
280
|
|
|
280
|
-
<!-- 自定义列插槽 -->
|
|
281
|
+
<!-- 自定义列插槽 (透传 item.* 并在没有时回退到默认渲染) -->
|
|
281
282
|
<template
|
|
282
283
|
v-for="header in visibleHeaders"
|
|
283
284
|
v-slot:[`item.${header.value}`]="{ item, value, index }"
|
|
@@ -383,10 +384,10 @@
|
|
|
383
384
|
|
|
384
385
|
<!-- 普通列 -->
|
|
385
386
|
<template v-else>
|
|
386
|
-
<!-- 自定义插槽 -->
|
|
387
|
-
<slot v-if="header.slot || $scopedSlots[`item.${header.value}`]" :name="`item.${header.value}`" :item="item" :value="value"></slot>
|
|
387
|
+
<!-- 自定义插槽 (透传 item.[value]) -->
|
|
388
|
+
<slot v-if="header.slot || $scopedSlots[`item.${header.value}`]" :name="`item.${header.value}`" :item="item" :value="value" :index="index"></slot>
|
|
388
389
|
|
|
389
|
-
<!-- 列配置渲染 -->
|
|
390
|
+
<!-- 列配置渲染 (没有自定义插槽时) -->
|
|
390
391
|
<div v-else class="jh-table-schema-cell d-flex align-center flex-wrap">
|
|
391
392
|
<!-- 状态标签 -->
|
|
392
393
|
<template v-if="header.valueType === 'status'">
|
|
@@ -486,8 +487,13 @@
|
|
|
486
487
|
</template>
|
|
487
488
|
</template>
|
|
488
489
|
|
|
490
|
+
<!-- 透传所有其他插槽 (v-data-table 的原生插槽,排除已处理的) -->
|
|
491
|
+
<template v-for="slotName in passthroughSlots" v-slot:[slotName]="slotProps">
|
|
492
|
+
<slot :name="slotName" v-bind="slotProps"></slot>
|
|
493
|
+
</template>
|
|
494
|
+
|
|
489
495
|
<!-- 加载中 -->
|
|
490
|
-
<template v-slot:loading>
|
|
496
|
+
<template v-if="!$scopedSlots.loading" v-slot:loading>
|
|
491
497
|
<div class="jh-no-data pa-6">
|
|
492
498
|
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
|
493
499
|
<div class="mt-2">数据加载中...</div>
|
|
@@ -495,7 +501,7 @@
|
|
|
495
501
|
</template>
|
|
496
502
|
|
|
497
503
|
<!-- 无数据 -->
|
|
498
|
-
<template v-slot:no-data>
|
|
504
|
+
<template v-if="!$scopedSlots['no-data']" v-slot:no-data>
|
|
499
505
|
<div class="jh-no-data pa-6">
|
|
500
506
|
<v-icon large color="grey lighten-1">mdi-inbox-outline</v-icon>
|
|
501
507
|
<div class="mt-2 text-body-2 grey--text">暂无数据</div>
|
|
@@ -503,7 +509,7 @@
|
|
|
503
509
|
</template>
|
|
504
510
|
|
|
505
511
|
<!-- 无结果 -->
|
|
506
|
-
<template v-slot:no-results>
|
|
512
|
+
<template v-if="!$scopedSlots['no-results']" v-slot:no-results>
|
|
507
513
|
<div class="jh-no-data pa-6">
|
|
508
514
|
<v-icon large color="grey lighten-1">mdi-magnify</v-icon>
|
|
509
515
|
<div class="mt-2 text-body-2 grey--text">未找到匹配的数据</div>
|
|
@@ -511,7 +517,7 @@
|
|
|
511
517
|
</template>
|
|
512
518
|
|
|
513
519
|
<!-- 分页文本 -->
|
|
514
|
-
<template v-slot:[`footer.page-text`]="pagination">
|
|
520
|
+
<template v-if="!$scopedSlots['footer.page-text']" v-slot:[`footer.page-text`]="pagination">
|
|
515
521
|
<span>{{ pagination.pageStart }}-{{ pagination.pageStop }}</span>
|
|
516
522
|
<span class="ml-1">共{{ pagination.itemsLength }}条</span>
|
|
517
523
|
</template>
|
|
@@ -808,11 +814,16 @@ export default {
|
|
|
808
814
|
},
|
|
809
815
|
rowKey: {
|
|
810
816
|
type: String,
|
|
811
|
-
default:
|
|
817
|
+
default: undefined
|
|
818
|
+
},
|
|
819
|
+
// 别名,兼容 v-data-table
|
|
820
|
+
itemKey: {
|
|
821
|
+
type: String,
|
|
822
|
+
default: undefined
|
|
812
823
|
},
|
|
813
824
|
size: {
|
|
814
825
|
type: String,
|
|
815
|
-
default: '
|
|
826
|
+
default: 'medium', // default / medium / compact
|
|
816
827
|
validator: (v) => ['default', 'medium', 'compact'].includes(v)
|
|
817
828
|
},
|
|
818
829
|
footerProps: {
|
|
@@ -882,13 +893,15 @@ export default {
|
|
|
882
893
|
internalSortBy: this.normalizeSortBy(this.sortBy),
|
|
883
894
|
internalSortDesc: this.normalizeSortDesc(this.sortDesc),
|
|
884
895
|
sortChangeTimer: null,
|
|
885
|
-
hasAppliedDefaultRowSelection: false
|
|
896
|
+
hasAppliedDefaultRowSelection: false,
|
|
897
|
+
// 最终使用的 item-key
|
|
898
|
+
computedRowKey: this.rowKey || this.itemKey || this.$attrs['item-key'] || 'id'
|
|
886
899
|
};
|
|
887
900
|
},
|
|
888
901
|
computed: {
|
|
889
902
|
// 选中的行 keys
|
|
890
903
|
selectedRowKeys() {
|
|
891
|
-
return this.selectedItems.map(item => item[this.
|
|
904
|
+
return this.selectedItems.map(item => item[this.computedRowKey]);
|
|
892
905
|
},
|
|
893
906
|
|
|
894
907
|
tableAlertScope() {
|
|
@@ -1013,6 +1026,24 @@ export default {
|
|
|
1013
1026
|
customHeaderSlots() {
|
|
1014
1027
|
return this.internalColumns.filter(h => this.$scopedSlots[`header.${h.value}`]);
|
|
1015
1028
|
},
|
|
1029
|
+
// 需要透传的原生插槽
|
|
1030
|
+
passthroughSlots() {
|
|
1031
|
+
const handledSlots = [
|
|
1032
|
+
'loading', 'no-data', 'no-results', 'footer.page-text',
|
|
1033
|
+
'toolbar-actions', 'toolbar-extra', 'header-title', 'table-extra', 'alert', 'alert-actions'
|
|
1034
|
+
];
|
|
1035
|
+
return Object.keys(this.$scopedSlots).filter(name => {
|
|
1036
|
+
// 排除已处理的 item.* 和 header.*
|
|
1037
|
+
if (name.startsWith('item.') || name.startsWith('header.')) {
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
// 排除筛选字段插槽
|
|
1041
|
+
if (name.startsWith('filter-field-')) {
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
return !handledSlots.includes(name);
|
|
1045
|
+
});
|
|
1046
|
+
},
|
|
1016
1047
|
// 表格样式类
|
|
1017
1048
|
tableClassComputed() {
|
|
1018
1049
|
return [
|
|
@@ -1098,7 +1129,7 @@ export default {
|
|
|
1098
1129
|
'filterSearchText', 'filterResetText', 'showCreateButton', 'showUpdateAction',
|
|
1099
1130
|
'showDeleteAction', 'actionColumn', 'columnsState', 'pagination',
|
|
1100
1131
|
'itemsPerPage', 'rowSelection', 'tableAlertRender', 'tableAlertOptionRender',
|
|
1101
|
-
'showSelect', 'singleSelect', 'rowKey', 'size', 'footerProps',
|
|
1132
|
+
'showSelect', 'singleSelect', 'rowKey', 'itemKey', 'size', 'footerProps',
|
|
1102
1133
|
'tableClass', 'polling', 'debounceTime', 'dataTableProps'
|
|
1103
1134
|
];
|
|
1104
1135
|
|
|
@@ -1215,6 +1246,12 @@ export default {
|
|
|
1215
1246
|
this.$nextTick(() => this.applySelectionState());
|
|
1216
1247
|
}
|
|
1217
1248
|
},
|
|
1249
|
+
rowKey(val) {
|
|
1250
|
+
this.computedRowKey = val || this.itemKey || this.$attrs['item-key'] || 'id';
|
|
1251
|
+
},
|
|
1252
|
+
itemKey(val) {
|
|
1253
|
+
this.computedRowKey = this.rowKey || val || this.$attrs['item-key'] || 'id';
|
|
1254
|
+
},
|
|
1218
1255
|
actionColumn: {
|
|
1219
1256
|
handler() {
|
|
1220
1257
|
this.initColumns(this.headers);
|
|
@@ -1618,7 +1655,7 @@ export default {
|
|
|
1618
1655
|
handleSelectionChange(selectedItems) {
|
|
1619
1656
|
this.selectedItems = selectedItems;
|
|
1620
1657
|
const payload = {
|
|
1621
|
-
selectedRowKeys: selectedItems.map(item => item[this.
|
|
1658
|
+
selectedRowKeys: selectedItems.map(item => item[this.computedRowKey]),
|
|
1622
1659
|
selectedRows: selectedItems
|
|
1623
1660
|
};
|
|
1624
1661
|
this.$emit('selection-change', payload);
|
|
@@ -1824,6 +1861,10 @@ export default {
|
|
|
1824
1861
|
if (!label) return '';
|
|
1825
1862
|
return type === 'select' ? `请选择${label}` : `请输入${label}`;
|
|
1826
1863
|
},
|
|
1864
|
+
// 获取原生 v-data-table 实例
|
|
1865
|
+
getVDataTable() {
|
|
1866
|
+
return this.$refs.dataTable;
|
|
1867
|
+
},
|
|
1827
1868
|
// 重新加载数据(服务端分页)
|
|
1828
1869
|
async reload() {
|
|
1829
1870
|
if (!this.request) return;
|
|
@@ -2042,7 +2083,7 @@ export default {
|
|
|
2042
2083
|
syncSelectionByKeys(keys) {
|
|
2043
2084
|
if (!Array.isArray(keys)) return;
|
|
2044
2085
|
const keySet = new Set(keys);
|
|
2045
|
-
const matched = this.currentItems.filter(item => keySet.has(item[this.
|
|
2086
|
+
const matched = this.currentItems.filter(item => keySet.has(item[this.computedRowKey]));
|
|
2046
2087
|
this.selectedItems = matched;
|
|
2047
2088
|
this.$emit('input', matched);
|
|
2048
2089
|
},
|
|
@@ -2050,7 +2091,7 @@ export default {
|
|
|
2050
2091
|
if (this.hasAppliedDefaultRowSelection) return;
|
|
2051
2092
|
if (!this.rowSelection || !Array.isArray(this.rowSelection.defaultSelectedRowKeys)) return;
|
|
2052
2093
|
const keySet = new Set(this.rowSelection.defaultSelectedRowKeys);
|
|
2053
|
-
const matched = this.currentItems.filter(item => keySet.has(item[this.
|
|
2094
|
+
const matched = this.currentItems.filter(item => keySet.has(item[this.computedRowKey]));
|
|
2054
2095
|
if (matched.length) {
|
|
2055
2096
|
this.selectedItems = matched;
|
|
2056
2097
|
this.$emit('input', matched);
|
|
@@ -2081,7 +2122,7 @@ export default {
|
|
|
2081
2122
|
display: flex;
|
|
2082
2123
|
align-items: center;
|
|
2083
2124
|
justify-content: space-between;
|
|
2084
|
-
padding: 16px
|
|
2125
|
+
padding: 16px 0;
|
|
2085
2126
|
border-bottom: 1px solid #f0f0f0;
|
|
2086
2127
|
min-height: 64px;
|
|
2087
2128
|
}
|
|
@@ -2099,7 +2140,10 @@ export default {
|
|
|
2099
2140
|
height: 40px;
|
|
2100
2141
|
}
|
|
2101
2142
|
.jh-pro-table ::v-deep .jh-table-medium.v-data-table > .v-data-table__wrapper > table > tbody > tr > td {
|
|
2102
|
-
|
|
2143
|
+
min-height: 40px;
|
|
2144
|
+
height: 40px;
|
|
2145
|
+
color: #333C44;
|
|
2146
|
+
font-size: .8125rem;
|
|
2103
2147
|
}
|
|
2104
2148
|
|
|
2105
2149
|
|
|
@@ -2133,14 +2177,14 @@ export default {
|
|
|
2133
2177
|
align-items: center;
|
|
2134
2178
|
justify-content: space-between;
|
|
2135
2179
|
padding: 12px 24px;
|
|
2136
|
-
background: #
|
|
2137
|
-
border: 1px solid #91d5ff;
|
|
2180
|
+
background: #fbfbfb;
|
|
2138
2181
|
border-radius: 4px;
|
|
2139
2182
|
margin: 16px 0 0;
|
|
2140
2183
|
}
|
|
2141
2184
|
|
|
2142
2185
|
.jh-pro-table-alert-info {
|
|
2143
2186
|
display: flex;
|
|
2187
|
+
flex: 1;
|
|
2144
2188
|
align-items: center;
|
|
2145
2189
|
font-size: 14px;
|
|
2146
2190
|
color: rgba(0, 0, 0, 0.65);
|
|
@@ -2155,6 +2199,7 @@ export default {
|
|
|
2155
2199
|
/* 工具栏 */
|
|
2156
2200
|
.jh-pro-table-toolbar {
|
|
2157
2201
|
padding: 16px 0 !important;
|
|
2202
|
+
flex: inherit;
|
|
2158
2203
|
}
|
|
2159
2204
|
|
|
2160
2205
|
/* 表格额外内容区 */
|
|
@@ -2225,6 +2270,7 @@ export default {
|
|
|
2225
2270
|
.jh-table-default >>> .v-data-table > .v-data-table__wrapper > table > thead > tr > th {
|
|
2226
2271
|
height: 48px !important;
|
|
2227
2272
|
padding: 0 16px !important;
|
|
2273
|
+
|
|
2228
2274
|
}
|
|
2229
2275
|
|
|
2230
2276
|
.jh-table-medium >>> .v-data-table > .v-data-table__wrapper > table > tbody > tr > td,
|
|
@@ -2338,6 +2384,20 @@ export default {
|
|
|
2338
2384
|
cursor: pointer;
|
|
2339
2385
|
}
|
|
2340
2386
|
|
|
2387
|
+
/* 表格行选中样式 */
|
|
2388
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr.v-data-table__selected {
|
|
2389
|
+
background-color: #E6F7FF !important;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr.v-data-table__selected:hover {
|
|
2393
|
+
background-color: #D1EDFF !important;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
/* 优化选中行的过渡效果 */
|
|
2397
|
+
.jh-pro-table ::v-deep .v-data-table > .v-data-table__wrapper > table > tbody > tr {
|
|
2398
|
+
transition: background-color 0.2s ease;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2341
2401
|
/* Flex 工具类(如果没有 Tailwind) */
|
|
2342
2402
|
.flex {
|
|
2343
2403
|
display: flex;
|
|
@@ -359,8 +359,7 @@
|
|
|
359
359
|
.v-data-table>.v-data-table__wrapper>table>tbody>tr>td,
|
|
360
360
|
.v-data-table>.v-data-table__wrapper>table>tfoot>tr>td,
|
|
361
361
|
.v-data-table>.v-data-table__wrapper>table>thead>tr>td {
|
|
362
|
-
|
|
363
|
-
height: 40px;
|
|
362
|
+
|
|
364
363
|
color: #333C44;
|
|
365
364
|
font-size: .8125rem;
|
|
366
365
|
}
|