@netang/quasar 0.1.24 → 0.1.26
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.
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
v-if="! readonly && ! disable"
|
|
100
100
|
>
|
|
101
101
|
<!-- 快捷表格 -->
|
|
102
|
-
<
|
|
102
|
+
<n-table
|
|
103
103
|
class="n-table n-field-table__popup-table"
|
|
104
104
|
v-model:pagination="tablePagination"
|
|
105
105
|
:selected="selected"
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
dense
|
|
153
153
|
/>
|
|
154
154
|
</template>
|
|
155
|
-
</
|
|
155
|
+
</n-table>
|
|
156
156
|
</q-popup-proxy>
|
|
157
157
|
</q-field>
|
|
158
158
|
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
v-bind="dialogProps"
|
|
169
169
|
>
|
|
170
170
|
<q-page>
|
|
171
|
-
<n-table />
|
|
171
|
+
<n-mixed-table />
|
|
172
172
|
</q-page>
|
|
173
173
|
</n-dialog>
|
|
174
174
|
</template>
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-layout
|
|
3
|
+
class="absolute-full"
|
|
4
|
+
:class="{
|
|
5
|
+
'n-table--grid': tableGrid,
|
|
6
|
+
}"
|
|
7
|
+
view="lHr LpR lff"
|
|
8
|
+
container
|
|
9
|
+
>
|
|
10
|
+
<!-- 头部 -->
|
|
11
|
+
<n-toolbar
|
|
12
|
+
:dense="dense"
|
|
13
|
+
header
|
|
14
|
+
>
|
|
15
|
+
<!-- 插槽 -->
|
|
16
|
+
<template
|
|
17
|
+
v-for="slotName in slotNames.toolbar"
|
|
18
|
+
v-slot:[slotName]
|
|
19
|
+
>
|
|
20
|
+
<slot :name="`toolbar-${slotName}`"/>
|
|
21
|
+
</template>
|
|
22
|
+
</n-toolbar>
|
|
23
|
+
|
|
24
|
+
<!-- 左侧分类 -->
|
|
25
|
+
<slot name="left-drawer" v-if="slotNames.leftDrawer" />
|
|
26
|
+
<n-drawer
|
|
27
|
+
:model-value="! hideLeftDrawer"
|
|
28
|
+
:side="treeSide"
|
|
29
|
+
:width="200"
|
|
30
|
+
:min-width="150"
|
|
31
|
+
bordered
|
|
32
|
+
drag
|
|
33
|
+
cache
|
|
34
|
+
v-bind="leftDrawerProps"
|
|
35
|
+
v-else-if="treeNodes.length"
|
|
36
|
+
>
|
|
37
|
+
<q-scroll-area class="absolute-full">
|
|
38
|
+
|
|
39
|
+
<!-- 树筛选 -->
|
|
40
|
+
<div class="q-pa-sm q-gutter-sm" v-if="treeFilter">
|
|
41
|
+
<q-input
|
|
42
|
+
v-model="treeFilterValue"
|
|
43
|
+
placeholder="搜索"
|
|
44
|
+
dense
|
|
45
|
+
outlined
|
|
46
|
+
clearable
|
|
47
|
+
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- 分类树 -->
|
|
52
|
+
<!--selected-color="primary"-->
|
|
53
|
+
<n-tree
|
|
54
|
+
color="grey-5"
|
|
55
|
+
ref="treeRef"
|
|
56
|
+
:nodes="treeNodes"
|
|
57
|
+
:filter="treeFilterValue"
|
|
58
|
+
:node-key="treeNodeKey"
|
|
59
|
+
:label-key="treeLabelKey"
|
|
60
|
+
v-model:selected="treeSelected"
|
|
61
|
+
no-selection-unset
|
|
62
|
+
default-expand-all
|
|
63
|
+
v-bind="treeProps"
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
</q-scroll-area>
|
|
67
|
+
</n-drawer>
|
|
68
|
+
|
|
69
|
+
<!-- 列表 -->
|
|
70
|
+
<q-page-container>
|
|
71
|
+
<q-page>
|
|
72
|
+
<n-table
|
|
73
|
+
class="n-table absolute-full"
|
|
74
|
+
:class="{
|
|
75
|
+
'n-table--last-fixed': showTableFixed,
|
|
76
|
+
}"
|
|
77
|
+
v-model:pagination="tablePagination"
|
|
78
|
+
v-model:selected="tableSelected"
|
|
79
|
+
:row-key="tableRowKey"
|
|
80
|
+
:rows="tableRows"
|
|
81
|
+
:columns="tableColumns"
|
|
82
|
+
:visible-columns="tableVisibleColumns"
|
|
83
|
+
:selection="tableSelection"
|
|
84
|
+
:separator="tableSeparator"
|
|
85
|
+
:loading="tableLoading"
|
|
86
|
+
:rows-per-page-options="tableRowsPerPageOptions"
|
|
87
|
+
:grid="tableGrid"
|
|
88
|
+
@row-click="tableRowClick"
|
|
89
|
+
@row-dblclick="currentTableRowDblclick"
|
|
90
|
+
@request="tableRequest"
|
|
91
|
+
flat
|
|
92
|
+
virtual-scroll
|
|
93
|
+
:virtual-scroll-slice-ratio-before="20"
|
|
94
|
+
:virtual-scroll-slice-size="50"
|
|
95
|
+
:virtual-scroll-slice-ratio-after="20"
|
|
96
|
+
:dense="dense"
|
|
97
|
+
v-bind="$attrs"
|
|
98
|
+
>
|
|
99
|
+
<!-- 图片 -->
|
|
100
|
+
<template
|
|
101
|
+
v-for="imgName in tableImgNames"
|
|
102
|
+
v-slot:[`body-cell-${imgName}`]="props"
|
|
103
|
+
>
|
|
104
|
+
<q-td :props="props">
|
|
105
|
+
<!-- 缩略图 -->
|
|
106
|
+
<n-thumbnail
|
|
107
|
+
:src="props.row[imgName]"
|
|
108
|
+
preview
|
|
109
|
+
/>
|
|
110
|
+
</q-td>
|
|
111
|
+
</template>
|
|
112
|
+
|
|
113
|
+
<!-- 插槽 -->
|
|
114
|
+
<template
|
|
115
|
+
v-for="slotName in slotNames.table"
|
|
116
|
+
v-slot:[slotName]="props"
|
|
117
|
+
>
|
|
118
|
+
<q-td :props="props">
|
|
119
|
+
<slot
|
|
120
|
+
:name="slotName"
|
|
121
|
+
v-bind="props"
|
|
122
|
+
/>
|
|
123
|
+
</q-td>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<!-- 操作 -->
|
|
127
|
+
<template v-slot:body-cell-settings="props">
|
|
128
|
+
<n-table-column-fixed
|
|
129
|
+
:props="props"
|
|
130
|
+
/>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<!-- 合计 -->
|
|
134
|
+
<template v-slot:bottom-row="props" v-if="tableSummary">
|
|
135
|
+
<n-table-summary
|
|
136
|
+
:props="props"
|
|
137
|
+
/>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<!-- 翻页 -->
|
|
141
|
+
<template v-slot:pagination="props">
|
|
142
|
+
<n-table-pagination
|
|
143
|
+
:props="props"
|
|
144
|
+
/>
|
|
145
|
+
</template>
|
|
146
|
+
</n-table>
|
|
147
|
+
</q-page>
|
|
148
|
+
</q-page-container>
|
|
149
|
+
|
|
150
|
+
<!-- 右侧搜索 -->
|
|
151
|
+
<slot name="right-drawer" v-if="slotNames.rightDrawer" />
|
|
152
|
+
<n-drawer
|
|
153
|
+
:model-value="! hideRightDrawer"
|
|
154
|
+
:side="searchSide"
|
|
155
|
+
:min-width="320"
|
|
156
|
+
bordered
|
|
157
|
+
drag
|
|
158
|
+
cache
|
|
159
|
+
v-bind="rightDrawerProps"
|
|
160
|
+
v-else-if="! noSearch && tableSearchValue.length"
|
|
161
|
+
>
|
|
162
|
+
<!-- 搜索 -->
|
|
163
|
+
<n-search
|
|
164
|
+
v-model="tableSearchValue"
|
|
165
|
+
:options="tableSearchOptions"
|
|
166
|
+
:on-search="tableReload"
|
|
167
|
+
:on-reset="tableSearchReset"
|
|
168
|
+
v-if="showSearch"
|
|
169
|
+
>
|
|
170
|
+
<!-- 插槽 -->
|
|
171
|
+
<template
|
|
172
|
+
v-for="slotName in slotNames.search"
|
|
173
|
+
v-slot:[slotName]
|
|
174
|
+
>
|
|
175
|
+
<slot :name="`search-${slotName}`"/>
|
|
176
|
+
</template>
|
|
177
|
+
</n-search>
|
|
178
|
+
</n-drawer>
|
|
179
|
+
|
|
180
|
+
</q-layout>
|
|
181
|
+
</template>
|
|
182
|
+
|
|
183
|
+
<script>
|
|
184
|
+
import { ref, watch, computed, inject, onMounted } from 'vue'
|
|
185
|
+
|
|
186
|
+
import $n_isFunction from 'lodash/isFunction'
|
|
187
|
+
import $n_isNil from 'lodash/isNil'
|
|
188
|
+
|
|
189
|
+
import $n_isValidObject from '@netang/utils/isValidObject'
|
|
190
|
+
import $n_isValidValue from '@netang/utils/isValidValue'
|
|
191
|
+
import $n_collection from '@netang/utils/collection'
|
|
192
|
+
|
|
193
|
+
import { NDialogKey, NTableKey } from '../../utils/symbols'
|
|
194
|
+
|
|
195
|
+
import NToolbar from '../toolbar'
|
|
196
|
+
import NDrawer from '../drawer'
|
|
197
|
+
import NThumbnail from '../thumbnail'
|
|
198
|
+
import NTableColumnFixed from '../table-column-fixed'
|
|
199
|
+
import NTableSummary from '../table-summary'
|
|
200
|
+
import NTablePagination from '../table-pagination'
|
|
201
|
+
import NSearch from '../search'
|
|
202
|
+
|
|
203
|
+
export default {
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 标识
|
|
207
|
+
*/
|
|
208
|
+
name: 'NMixedTable',
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 关闭组件 attribute 透传行为
|
|
212
|
+
*/
|
|
213
|
+
inheritAttrs: false,
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 组件
|
|
217
|
+
*/
|
|
218
|
+
components: {
|
|
219
|
+
NToolbar,
|
|
220
|
+
NDrawer,
|
|
221
|
+
NThumbnail,
|
|
222
|
+
NTableColumnFixed,
|
|
223
|
+
NTableSummary,
|
|
224
|
+
NTablePagination,
|
|
225
|
+
NSearch,
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 声明属性
|
|
230
|
+
*/
|
|
231
|
+
props: {
|
|
232
|
+
// 树声明属性
|
|
233
|
+
treeProps: Object,
|
|
234
|
+
// 树节点唯一键值
|
|
235
|
+
treeNodeKey: {
|
|
236
|
+
type: String,
|
|
237
|
+
default: 'id',
|
|
238
|
+
},
|
|
239
|
+
// 树标签键值
|
|
240
|
+
treeLabelKey: {
|
|
241
|
+
type: String,
|
|
242
|
+
default: 'title',
|
|
243
|
+
},
|
|
244
|
+
// 树节点列表
|
|
245
|
+
treeNodes: {
|
|
246
|
+
type: Array,
|
|
247
|
+
default: ()=>[],
|
|
248
|
+
},
|
|
249
|
+
// 默认树已选节点 v-model:tree-selected
|
|
250
|
+
treeSelected: [ String, Number ],
|
|
251
|
+
// 树节点点击
|
|
252
|
+
treeNodeClick: Function,
|
|
253
|
+
// 是否选中第一个树节点
|
|
254
|
+
treeSelectFirstNode: {
|
|
255
|
+
type: Boolean,
|
|
256
|
+
default: true,
|
|
257
|
+
},
|
|
258
|
+
// 显示树筛选
|
|
259
|
+
treeFilter: Boolean,
|
|
260
|
+
// 树位置, 可选值: left / right
|
|
261
|
+
treeSide: {
|
|
262
|
+
type: String,
|
|
263
|
+
default: 'left',
|
|
264
|
+
},
|
|
265
|
+
// 不显示搜索
|
|
266
|
+
noSearch: Boolean,
|
|
267
|
+
// 搜索位置
|
|
268
|
+
searchSide: {
|
|
269
|
+
type: String,
|
|
270
|
+
default: 'right',
|
|
271
|
+
},
|
|
272
|
+
// 紧凑模式
|
|
273
|
+
dense: {
|
|
274
|
+
type: Boolean,
|
|
275
|
+
default: true,
|
|
276
|
+
},
|
|
277
|
+
// 隐藏左边侧滑菜单
|
|
278
|
+
hideLeftDrawer: Boolean,
|
|
279
|
+
// 左边侧滑菜单声明属性
|
|
280
|
+
leftDrawerProps: Object,
|
|
281
|
+
// 隐藏右边侧滑菜单
|
|
282
|
+
hideRightDrawer: Boolean,
|
|
283
|
+
// 右边侧滑菜单声明属性
|
|
284
|
+
rightDrawerProps: Object,
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 组合式
|
|
289
|
+
*/
|
|
290
|
+
setup(props, { slots, emit }) {
|
|
291
|
+
|
|
292
|
+
// ==========【数据】============================================================================================
|
|
293
|
+
|
|
294
|
+
// 获取表格注入
|
|
295
|
+
const $table = inject(NTableKey)
|
|
296
|
+
|
|
297
|
+
// 获取对话框注入
|
|
298
|
+
const $dialog = inject(NDialogKey)
|
|
299
|
+
const inDialog = !! $dialog
|
|
300
|
+
|
|
301
|
+
// 当前双击表格行
|
|
302
|
+
let currentTableRowDblclick
|
|
303
|
+
|
|
304
|
+
// 如果在对话框内部
|
|
305
|
+
if (inDialog) {
|
|
306
|
+
// 提交表格已选数据给对话框
|
|
307
|
+
$dialog.submit(() => $table.tableSelected.value)
|
|
308
|
+
|
|
309
|
+
// 对话框中的表格双击表格行
|
|
310
|
+
currentTableRowDblclick = function (e, row) {
|
|
311
|
+
// 如果不是多选
|
|
312
|
+
if ($table.tableSelection.value !== 'multiple') {
|
|
313
|
+
$table.tableSelected.value = [ row ]
|
|
314
|
+
$dialog.confirm()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
} else {
|
|
319
|
+
// 表格实例中的双击表格行
|
|
320
|
+
currentTableRowDblclick = $table.tableRowDblclick
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 树节点
|
|
324
|
+
const treeRef = ref(null)
|
|
325
|
+
|
|
326
|
+
// 树筛选值
|
|
327
|
+
const treeFilterValue = ref('')
|
|
328
|
+
|
|
329
|
+
// 树选择数据
|
|
330
|
+
const treeSelected = ref(props.treeSelected)
|
|
331
|
+
|
|
332
|
+
// 是否显示搜索
|
|
333
|
+
const showSearch = ref(false)
|
|
334
|
+
|
|
335
|
+
// ==========【计算属性】==========================================================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 插槽标识
|
|
339
|
+
*/
|
|
340
|
+
const slotNames = computed(function() {
|
|
341
|
+
|
|
342
|
+
const toolbar = []
|
|
343
|
+
const search = []
|
|
344
|
+
const table = []
|
|
345
|
+
let leftDrawer = false
|
|
346
|
+
let rightDrawer = false
|
|
347
|
+
|
|
348
|
+
// 如果有插槽
|
|
349
|
+
if ($n_isValidObject(slots)) {
|
|
350
|
+
for (const key in slots) {
|
|
351
|
+
if (key.startsWith('toolbar-')) {
|
|
352
|
+
toolbar.push(key.replace('toolbar-', ''))
|
|
353
|
+
} else if (key.startsWith('search-')) {
|
|
354
|
+
search.push(key.replace('search-', ''))
|
|
355
|
+
} else if (key === 'left-drawer') {
|
|
356
|
+
leftDrawer = true
|
|
357
|
+
} else if (key === 'right-drawer') {
|
|
358
|
+
rightDrawer = true
|
|
359
|
+
} else {
|
|
360
|
+
table.push(key)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
toolbar,
|
|
367
|
+
search,
|
|
368
|
+
table,
|
|
369
|
+
leftDrawer,
|
|
370
|
+
rightDrawer,
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
// ==========【监听数据】=========================================================================================
|
|
375
|
+
|
|
376
|
+
// 如果有树节点点击方法
|
|
377
|
+
if ($n_isFunction(props.treeNodeClick)) {
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* 树节点 all
|
|
381
|
+
*/
|
|
382
|
+
const treeNodesAll = computed(function () {
|
|
383
|
+
return $n_collection(props.treeNodes)
|
|
384
|
+
.keyBy(props.treeNodeKey)
|
|
385
|
+
.toObject()
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 监听树选择数据
|
|
390
|
+
*/
|
|
391
|
+
watch(treeSelected, function(nodeKey) {
|
|
392
|
+
|
|
393
|
+
// 触发更新已选树节点
|
|
394
|
+
emit('update:treeSelected', nodeKey)
|
|
395
|
+
|
|
396
|
+
// 如果节点值不是有效值
|
|
397
|
+
if (! $n_isValidValue(nodeKey)) {
|
|
398
|
+
// 则无任何操作
|
|
399
|
+
return
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// 树节点点击
|
|
403
|
+
const res = props.treeNodeClick(nodeKey, treeNodesAll.value[nodeKey])
|
|
404
|
+
|
|
405
|
+
if ($n_isValidObject(res)) {
|
|
406
|
+
|
|
407
|
+
// 设置表格传参
|
|
408
|
+
$table.setQuery(res)
|
|
409
|
+
|
|
410
|
+
// 表格重新加载
|
|
411
|
+
$table.tableReload()
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* 监听树节点数据
|
|
417
|
+
*/
|
|
418
|
+
watch(() => props.treeNodes, function (val) {
|
|
419
|
+
|
|
420
|
+
if (
|
|
421
|
+
// 如果关闭选中第一个树节点
|
|
422
|
+
! props.treeSelectFirstNode
|
|
423
|
+
|| ! $n_isNil(treeSelected.value)
|
|
424
|
+
) {
|
|
425
|
+
// 则无任何操作
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 选中第一个节点的值
|
|
430
|
+
treeSelected.value = val[0][props.treeNodeKey]
|
|
431
|
+
}, {
|
|
432
|
+
// 立即执行
|
|
433
|
+
immediate: true,
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ==========【生命周期】=========================================================================================
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* 实例被挂载后调用
|
|
441
|
+
*/
|
|
442
|
+
onMounted( async function() {
|
|
443
|
+
|
|
444
|
+
// 显示搜索
|
|
445
|
+
showSearch.value = true
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
// ==========【返回】=============================================================================================
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
// 解构表格实例
|
|
452
|
+
...$table,
|
|
453
|
+
|
|
454
|
+
// 树节点
|
|
455
|
+
treeRef,
|
|
456
|
+
// 树筛选值
|
|
457
|
+
treeFilterValue,
|
|
458
|
+
// 树选择数据
|
|
459
|
+
treeSelected,
|
|
460
|
+
// 是否显示搜索
|
|
461
|
+
showSearch,
|
|
462
|
+
|
|
463
|
+
// 插槽 body 单元格标识
|
|
464
|
+
slotNames,
|
|
465
|
+
|
|
466
|
+
// 当前双击表格行
|
|
467
|
+
currentTableRowDblclick,
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
}
|
|
471
|
+
</script>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
>
|
|
15
15
|
<!-- 表格 -->
|
|
16
16
|
<template v-slot:before="{ after, toggleAfter }">
|
|
17
|
-
<n-table
|
|
17
|
+
<n-mixed-table
|
|
18
18
|
v-bind="$attrs"
|
|
19
19
|
>
|
|
20
20
|
<!-- 工具栏右边插槽(手机端不显示) -->
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
/>
|
|
54
54
|
</template>
|
|
55
55
|
|
|
56
|
-
</n-table>
|
|
56
|
+
</n-mixed-table>
|
|
57
57
|
</template>
|
|
58
58
|
|
|
59
59
|
<!-- 渲染详情页面(手机端不显示) -->
|
|
@@ -112,7 +112,7 @@ export default {
|
|
|
112
112
|
/**
|
|
113
113
|
* 标识
|
|
114
114
|
*/
|
|
115
|
-
name: '
|
|
115
|
+
name: 'NMixedTableSplitter',
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* 组件
|
|
@@ -1,204 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<q-
|
|
3
|
-
class="
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
}"
|
|
7
|
-
view="lHr LpR lff"
|
|
8
|
-
container
|
|
2
|
+
<q-table
|
|
3
|
+
class="n-table"
|
|
4
|
+
:virtual-scroll-slice-size="virtualScrollItemSize"
|
|
5
|
+
v-bind="$attrs"
|
|
9
6
|
>
|
|
10
|
-
<!--
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
<!-- 插槽 -->
|
|
8
|
+
<template
|
|
9
|
+
v-for="slotName in slotNames"
|
|
10
|
+
v-slot:[slotName]="props"
|
|
14
11
|
>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
v-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</template>
|
|
22
|
-
</n-toolbar>
|
|
23
|
-
|
|
24
|
-
<!-- 左侧分类 -->
|
|
25
|
-
<slot name="left-drawer" v-if="slotNames.leftDrawer" />
|
|
26
|
-
<n-drawer
|
|
27
|
-
:model-value="! hideLeftDrawer"
|
|
28
|
-
:side="treeSide"
|
|
29
|
-
:width="200"
|
|
30
|
-
:min-width="150"
|
|
31
|
-
bordered
|
|
32
|
-
drag
|
|
33
|
-
cache
|
|
34
|
-
v-bind="leftDrawerProps"
|
|
35
|
-
v-else-if="treeNodes.length"
|
|
36
|
-
>
|
|
37
|
-
<q-scroll-area class="absolute-full">
|
|
38
|
-
|
|
39
|
-
<!-- 树筛选 -->
|
|
40
|
-
<div class="q-pa-sm q-gutter-sm" v-if="treeFilter">
|
|
41
|
-
<q-input
|
|
42
|
-
v-model="treeFilterValue"
|
|
43
|
-
placeholder="搜索"
|
|
44
|
-
dense
|
|
45
|
-
outlined
|
|
46
|
-
clearable
|
|
47
|
-
|
|
48
|
-
/>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<!-- 分类树 -->
|
|
52
|
-
<!--selected-color="primary"-->
|
|
53
|
-
<n-tree
|
|
54
|
-
color="grey-5"
|
|
55
|
-
ref="treeRef"
|
|
56
|
-
:nodes="treeNodes"
|
|
57
|
-
:filter="treeFilterValue"
|
|
58
|
-
:node-key="treeNodeKey"
|
|
59
|
-
:label-key="treeLabelKey"
|
|
60
|
-
v-model:selected="treeSelected"
|
|
61
|
-
no-selection-unset
|
|
62
|
-
default-expand-all
|
|
63
|
-
v-bind="treeProps"
|
|
64
|
-
/>
|
|
65
|
-
|
|
66
|
-
</q-scroll-area>
|
|
67
|
-
</n-drawer>
|
|
68
|
-
|
|
69
|
-
<!-- 列表 -->
|
|
70
|
-
<q-page-container>
|
|
71
|
-
<q-page>
|
|
72
|
-
<q-table
|
|
73
|
-
class="n-table absolute-full"
|
|
74
|
-
:class="{
|
|
75
|
-
'n-table--last-fixed': showTableFixed,
|
|
76
|
-
}"
|
|
77
|
-
v-model:pagination="tablePagination"
|
|
78
|
-
v-model:selected="tableSelected"
|
|
79
|
-
:row-key="tableRowKey"
|
|
80
|
-
:rows="tableRows"
|
|
81
|
-
:columns="tableColumns"
|
|
82
|
-
:visible-columns="tableVisibleColumns"
|
|
83
|
-
:selection="tableSelection"
|
|
84
|
-
:separator="tableSeparator"
|
|
85
|
-
:loading="tableLoading"
|
|
86
|
-
:rows-per-page-options="tableRowsPerPageOptions"
|
|
87
|
-
:grid="tableGrid"
|
|
88
|
-
@row-click="tableRowClick"
|
|
89
|
-
@row-dblclick="currentTableRowDblclick"
|
|
90
|
-
@request="tableRequest"
|
|
91
|
-
flat
|
|
92
|
-
virtual-scroll
|
|
93
|
-
:virtual-scroll-slice-ratio-before="20"
|
|
94
|
-
:virtual-scroll-slice-size="50"
|
|
95
|
-
:virtual-scroll-slice-ratio-after="20"
|
|
96
|
-
:dense="dense"
|
|
97
|
-
v-bind="$attrs"
|
|
98
|
-
>
|
|
99
|
-
<!-- 图片 -->
|
|
100
|
-
<template
|
|
101
|
-
v-for="imgName in tableImgNames"
|
|
102
|
-
v-slot:[`body-cell-${imgName}`]="props"
|
|
103
|
-
>
|
|
104
|
-
<q-td :props="props">
|
|
105
|
-
<!-- 缩略图 -->
|
|
106
|
-
<n-thumbnail
|
|
107
|
-
:src="props.row[imgName]"
|
|
108
|
-
preview
|
|
109
|
-
/>
|
|
110
|
-
</q-td>
|
|
111
|
-
</template>
|
|
112
|
-
|
|
113
|
-
<!-- 插槽 -->
|
|
114
|
-
<template
|
|
115
|
-
v-for="slotName in slotNames.table"
|
|
116
|
-
v-slot:[slotName]="props"
|
|
117
|
-
>
|
|
118
|
-
<q-td :props="props">
|
|
119
|
-
<slot
|
|
120
|
-
:name="slotName"
|
|
121
|
-
v-bind="props"
|
|
122
|
-
/>
|
|
123
|
-
</q-td>
|
|
124
|
-
</template>
|
|
125
|
-
|
|
126
|
-
<!-- 操作 -->
|
|
127
|
-
<template v-slot:body-cell-settings="props">
|
|
128
|
-
<n-table-column-fixed
|
|
129
|
-
:props="props"
|
|
130
|
-
/>
|
|
131
|
-
</template>
|
|
132
|
-
|
|
133
|
-
<!-- 合计 -->
|
|
134
|
-
<template v-slot:bottom-row="props" v-if="tableSummary">
|
|
135
|
-
<n-table-summary
|
|
136
|
-
:props="props"
|
|
137
|
-
/>
|
|
138
|
-
</template>
|
|
139
|
-
|
|
140
|
-
<!-- 翻页 -->
|
|
141
|
-
<template v-slot:pagination="props">
|
|
142
|
-
<n-table-pagination
|
|
143
|
-
:props="props"
|
|
144
|
-
/>
|
|
145
|
-
</template>
|
|
146
|
-
</q-table>
|
|
147
|
-
</q-page>
|
|
148
|
-
</q-page-container>
|
|
149
|
-
|
|
150
|
-
<!-- 右侧搜索 -->
|
|
151
|
-
<slot name="right-drawer" v-if="slotNames.rightDrawer" />
|
|
152
|
-
<n-drawer
|
|
153
|
-
:model-value="! hideRightDrawer"
|
|
154
|
-
:side="searchSide"
|
|
155
|
-
:min-width="320"
|
|
156
|
-
bordered
|
|
157
|
-
drag
|
|
158
|
-
cache
|
|
159
|
-
v-bind="rightDrawerProps"
|
|
160
|
-
v-else-if="! noSearch && tableSearchValue.length"
|
|
161
|
-
>
|
|
162
|
-
<!-- 搜索 -->
|
|
163
|
-
<n-search
|
|
164
|
-
v-model="tableSearchValue"
|
|
165
|
-
:options="tableSearchOptions"
|
|
166
|
-
:on-search="tableReload"
|
|
167
|
-
:on-reset="tableSearchReset"
|
|
168
|
-
v-if="showSearch"
|
|
169
|
-
>
|
|
170
|
-
<!-- 插槽 -->
|
|
171
|
-
<template
|
|
172
|
-
v-for="slotName in slotNames.search"
|
|
173
|
-
v-slot:[slotName]
|
|
174
|
-
>
|
|
175
|
-
<slot :name="`search-${slotName}`"/>
|
|
176
|
-
</template>
|
|
177
|
-
</n-search>
|
|
178
|
-
</n-drawer>
|
|
179
|
-
|
|
180
|
-
</q-layout>
|
|
12
|
+
<slot
|
|
13
|
+
:name="slotName"
|
|
14
|
+
v-bind="props"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
17
|
+
</q-table>
|
|
181
18
|
</template>
|
|
182
19
|
|
|
183
20
|
<script>
|
|
184
|
-
import {
|
|
185
|
-
|
|
186
|
-
import $n_isFunction from 'lodash/isFunction'
|
|
187
|
-
import $n_isNil from 'lodash/isNil'
|
|
21
|
+
import { computed } from 'vue'
|
|
188
22
|
|
|
189
23
|
import $n_isValidObject from '@netang/utils/isValidObject'
|
|
190
|
-
import $n_isValidValue from '@netang/utils/isValidValue'
|
|
191
|
-
import $n_collection from '@netang/utils/collection'
|
|
192
|
-
|
|
193
|
-
import { NDialogKey, NTableKey } from '../../utils/symbols'
|
|
194
|
-
|
|
195
|
-
import NToolbar from '../toolbar'
|
|
196
|
-
import NDrawer from '../drawer'
|
|
197
|
-
import NThumbnail from '../thumbnail'
|
|
198
|
-
import NTableColumnFixed from '../table-column-fixed'
|
|
199
|
-
import NTableSummary from '../table-summary'
|
|
200
|
-
import NTablePagination from '../table-pagination'
|
|
201
|
-
import NSearch from '../search'
|
|
202
24
|
|
|
203
25
|
export default {
|
|
204
26
|
|
|
@@ -207,130 +29,21 @@ export default {
|
|
|
207
29
|
*/
|
|
208
30
|
name: 'NTable',
|
|
209
31
|
|
|
210
|
-
/**
|
|
211
|
-
* 关闭组件 attribute 透传行为
|
|
212
|
-
*/
|
|
213
|
-
inheritAttrs: false,
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* 组件
|
|
217
|
-
*/
|
|
218
|
-
components: {
|
|
219
|
-
NToolbar,
|
|
220
|
-
NDrawer,
|
|
221
|
-
NThumbnail,
|
|
222
|
-
NTableColumnFixed,
|
|
223
|
-
NTableSummary,
|
|
224
|
-
NTablePagination,
|
|
225
|
-
NSearch,
|
|
226
|
-
},
|
|
227
|
-
|
|
228
32
|
/**
|
|
229
33
|
* 声明属性
|
|
230
34
|
*/
|
|
231
35
|
props: {
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
type: String,
|
|
237
|
-
default: 'id',
|
|
238
|
-
},
|
|
239
|
-
// 树标签键值
|
|
240
|
-
treeLabelKey: {
|
|
241
|
-
type: String,
|
|
242
|
-
default: 'title',
|
|
243
|
-
},
|
|
244
|
-
// 树节点列表
|
|
245
|
-
treeNodes: {
|
|
246
|
-
type: Array,
|
|
247
|
-
default: ()=>[],
|
|
36
|
+
// 以像素为单位的默认行大小
|
|
37
|
+
virtualScrollItemSize: {
|
|
38
|
+
type: [ Number, String ],
|
|
39
|
+
default: 50,
|
|
248
40
|
},
|
|
249
|
-
// 默认树已选节点 v-model:tree-selected
|
|
250
|
-
treeSelected: [ String, Number ],
|
|
251
|
-
// 树节点点击
|
|
252
|
-
treeNodeClick: Function,
|
|
253
|
-
// 是否选中第一个树节点
|
|
254
|
-
treeSelectFirstNode: {
|
|
255
|
-
type: Boolean,
|
|
256
|
-
default: true,
|
|
257
|
-
},
|
|
258
|
-
// 显示树筛选
|
|
259
|
-
treeFilter: Boolean,
|
|
260
|
-
// 树位置, 可选值: left / right
|
|
261
|
-
treeSide: {
|
|
262
|
-
type: String,
|
|
263
|
-
default: 'left',
|
|
264
|
-
},
|
|
265
|
-
// 不显示搜索
|
|
266
|
-
noSearch: Boolean,
|
|
267
|
-
// 搜索位置
|
|
268
|
-
searchSide: {
|
|
269
|
-
type: String,
|
|
270
|
-
default: 'right',
|
|
271
|
-
},
|
|
272
|
-
// 紧凑模式
|
|
273
|
-
dense: {
|
|
274
|
-
type: Boolean,
|
|
275
|
-
default: true,
|
|
276
|
-
},
|
|
277
|
-
// 隐藏左边侧滑菜单
|
|
278
|
-
hideLeftDrawer: Boolean,
|
|
279
|
-
// 左边侧滑菜单声明属性
|
|
280
|
-
leftDrawerProps: Object,
|
|
281
|
-
// 隐藏右边侧滑菜单
|
|
282
|
-
hideRightDrawer: Boolean,
|
|
283
|
-
// 右边侧滑菜单声明属性
|
|
284
|
-
rightDrawerProps: Object,
|
|
285
41
|
},
|
|
286
42
|
|
|
287
43
|
/**
|
|
288
44
|
* 组合式
|
|
289
45
|
*/
|
|
290
|
-
setup(props, { slots
|
|
291
|
-
|
|
292
|
-
// ==========【数据】============================================================================================
|
|
293
|
-
|
|
294
|
-
// 获取表格注入
|
|
295
|
-
const $table = inject(NTableKey)
|
|
296
|
-
|
|
297
|
-
// 获取对话框注入
|
|
298
|
-
const $dialog = inject(NDialogKey)
|
|
299
|
-
const inDialog = !! $dialog
|
|
300
|
-
|
|
301
|
-
// 当前双击表格行
|
|
302
|
-
let currentTableRowDblclick
|
|
303
|
-
|
|
304
|
-
// 如果在对话框内部
|
|
305
|
-
if (inDialog) {
|
|
306
|
-
// 提交表格已选数据给对话框
|
|
307
|
-
$dialog.submit(() => $table.tableSelected.value)
|
|
308
|
-
|
|
309
|
-
// 对话框中的表格双击表格行
|
|
310
|
-
currentTableRowDblclick = function (e, row) {
|
|
311
|
-
// 如果不是多选
|
|
312
|
-
if ($table.tableSelection.value !== 'multiple') {
|
|
313
|
-
$table.tableSelected.value = [ row ]
|
|
314
|
-
$dialog.confirm()
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
} else {
|
|
319
|
-
// 表格实例中的双击表格行
|
|
320
|
-
currentTableRowDblclick = $table.tableRowDblclick
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// 树节点
|
|
324
|
-
const treeRef = ref(null)
|
|
325
|
-
|
|
326
|
-
// 树筛选值
|
|
327
|
-
const treeFilterValue = ref('')
|
|
328
|
-
|
|
329
|
-
// 树选择数据
|
|
330
|
-
const treeSelected = ref(props.treeSelected)
|
|
331
|
-
|
|
332
|
-
// 是否显示搜索
|
|
333
|
-
const showSearch = ref(false)
|
|
46
|
+
setup(props, { slots }) {
|
|
334
47
|
|
|
335
48
|
// ==========【计算属性】==========================================================================================
|
|
336
49
|
|
|
@@ -338,133 +51,14 @@ export default {
|
|
|
338
51
|
* 插槽标识
|
|
339
52
|
*/
|
|
340
53
|
const slotNames = computed(function() {
|
|
341
|
-
|
|
342
|
-
const toolbar = []
|
|
343
|
-
const search = []
|
|
344
|
-
const table = []
|
|
345
|
-
let leftDrawer = false
|
|
346
|
-
let rightDrawer = false
|
|
347
|
-
|
|
348
|
-
// 如果有插槽
|
|
349
|
-
if ($n_isValidObject(slots)) {
|
|
350
|
-
for (const key in slots) {
|
|
351
|
-
if (key.startsWith('toolbar-')) {
|
|
352
|
-
toolbar.push(key.replace('toolbar-', ''))
|
|
353
|
-
} else if (key.startsWith('search-')) {
|
|
354
|
-
search.push(key.replace('search-', ''))
|
|
355
|
-
} else if (key === 'left-drawer') {
|
|
356
|
-
leftDrawer = true
|
|
357
|
-
} else if (key === 'right-drawer') {
|
|
358
|
-
rightDrawer = true
|
|
359
|
-
} else {
|
|
360
|
-
table.push(key)
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
toolbar,
|
|
367
|
-
search,
|
|
368
|
-
table,
|
|
369
|
-
leftDrawer,
|
|
370
|
-
rightDrawer,
|
|
371
|
-
}
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
// ==========【监听数据】=========================================================================================
|
|
375
|
-
|
|
376
|
-
// 如果有树节点点击方法
|
|
377
|
-
if ($n_isFunction(props.treeNodeClick)) {
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* 树节点 all
|
|
381
|
-
*/
|
|
382
|
-
const treeNodesAll = computed(function () {
|
|
383
|
-
return $n_collection(props.treeNodes)
|
|
384
|
-
.keyBy(props.treeNodeKey)
|
|
385
|
-
.toObject()
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* 监听树选择数据
|
|
390
|
-
*/
|
|
391
|
-
watch(treeSelected, function(nodeKey) {
|
|
392
|
-
|
|
393
|
-
// 触发更新已选树节点
|
|
394
|
-
emit('update:treeSelected', nodeKey)
|
|
395
|
-
|
|
396
|
-
// 如果节点值不是有效值
|
|
397
|
-
if (! $n_isValidValue(nodeKey)) {
|
|
398
|
-
// 则无任何操作
|
|
399
|
-
return
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// 树节点点击
|
|
403
|
-
const res = props.treeNodeClick(nodeKey, treeNodesAll.value[nodeKey])
|
|
404
|
-
|
|
405
|
-
if ($n_isValidObject(res)) {
|
|
406
|
-
|
|
407
|
-
// 设置表格传参
|
|
408
|
-
$table.setQuery(res)
|
|
409
|
-
|
|
410
|
-
// 表格重新加载
|
|
411
|
-
$table.tableReload()
|
|
412
|
-
}
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 监听树节点数据
|
|
417
|
-
*/
|
|
418
|
-
watch(() => props.treeNodes, function (val) {
|
|
419
|
-
|
|
420
|
-
if (
|
|
421
|
-
// 如果关闭选中第一个树节点
|
|
422
|
-
! props.treeSelectFirstNode
|
|
423
|
-
|| ! $n_isNil(treeSelected.value)
|
|
424
|
-
) {
|
|
425
|
-
// 则无任何操作
|
|
426
|
-
return
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// 选中第一个节点的值
|
|
430
|
-
treeSelected.value = val[0][props.treeNodeKey]
|
|
431
|
-
}, {
|
|
432
|
-
// 立即执行
|
|
433
|
-
immediate: true,
|
|
434
|
-
})
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// ==========【生命周期】=========================================================================================
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* 实例被挂载后调用
|
|
441
|
-
*/
|
|
442
|
-
onMounted( async function() {
|
|
443
|
-
|
|
444
|
-
// 显示搜索
|
|
445
|
-
showSearch.value = true
|
|
54
|
+
return $n_isValidObject(slots) ? Object.keys(slots) : []
|
|
446
55
|
})
|
|
447
56
|
|
|
448
57
|
// ==========【返回】=============================================================================================
|
|
449
58
|
|
|
450
59
|
return {
|
|
451
|
-
//
|
|
452
|
-
...$table,
|
|
453
|
-
|
|
454
|
-
// 树节点
|
|
455
|
-
treeRef,
|
|
456
|
-
// 树筛选值
|
|
457
|
-
treeFilterValue,
|
|
458
|
-
// 树选择数据
|
|
459
|
-
treeSelected,
|
|
460
|
-
// 是否显示搜索
|
|
461
|
-
showSearch,
|
|
462
|
-
|
|
463
|
-
// 插槽 body 单元格标识
|
|
60
|
+
// 插槽标识
|
|
464
61
|
slotNames,
|
|
465
|
-
|
|
466
|
-
// 当前双击表格行
|
|
467
|
-
currentTableRowDblclick,
|
|
468
62
|
}
|
|
469
63
|
},
|
|
470
64
|
}
|