@dt-frames/ui 1.0.7 → 1.0.10
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/es/components/forms/src/types/form.type.d.ts +3 -3
- package/es/components/index.d.ts +3 -2
- package/es/components/source/src/hooks/useFetch.d.ts +1 -2
- package/es/components/source/src/hooks/useSource.d.ts +4 -4
- package/es/components/source/src/types/source.type.d.ts +1 -9
- package/es/components/source/src/types/table.type.d.ts +1 -1
- package/es/components/table/src/components/TableActions.d.ts +2 -2
- package/es/components/table/src/components/setting/Download.d.ts +2 -2
- package/es/components/table/src/components/setting/Size.d.ts +2 -2
- package/es/components/table/src/index.d.ts +2 -2
- package/es/components/tree/index.d.ts +2 -0
- package/es/components/tree/src/hooks/useTree.d.ts +14 -0
- package/es/components/tree/src/props.d.ts +101 -0
- package/es/components/tree/src/type/tree.d.ts +85 -0
- package/es/components/tree/src/utils/tree.d.ts +5 -0
- package/es/components/upload/index.d.ts +3 -0
- package/es/components/upload/src/helper.d.ts +4 -0
- package/es/components/upload/src/index.d.ts +2784 -0
- package/es/components/upload/src/props.d.ts +40 -0
- package/es/components/upload/src/upload.d.ts +1630 -0
- package/es/index.js +1705 -235
- package/es/style/components/table/index.less +2 -2
- package/es/style/components/tree/index.less +41 -0
- package/es/style/components/upload/index.less +43 -0
- package/es/theme/sider/components/basic-menu/basic-menu.d.ts +3 -3
- package/es/theme/tabs/components/TabContent.d.ts +2 -2
- package/package.json +2 -1
- package/src/components/curd/src/hooks/useCurd.tsx +1 -1
- package/src/components/forms/src/components/formItem.vue +15 -2
- package/src/components/forms/src/hooks/useFormEvents.ts +4 -3
- package/src/components/forms/src/hooks/useFormValues.ts +1 -1
- package/src/components/forms/src/types/form.type.ts +3 -3
- package/src/components/index.ts +9 -3
- package/src/components/modal/src/index.vue +1 -1
- package/src/components/source/src/hooks/useFetch.ts +13 -42
- package/src/components/source/src/hooks/useSource.ts +30 -10
- package/src/components/source/src/types/source.type.ts +1 -21
- package/src/components/source/src/types/table.type.ts +1 -1
- package/src/components/table/index.less +2 -2
- package/src/components/table/src/components/TableRender.vue +1 -3
- package/src/components/table/src/hooks/useDataSource.ts +0 -13
- package/src/components/tree/index.less +41 -0
- package/src/components/tree/index.ts +5 -0
- package/src/components/tree/src/components/TreeHeader.vue +97 -0
- package/src/components/tree/src/hooks/useTree.ts +239 -0
- package/src/components/tree/src/index.vue +392 -0
- package/src/components/tree/src/props.ts +133 -0
- package/src/components/tree/src/type/tree.ts +105 -0
- package/src/components/tree/src/utils/tree.ts +73 -0
- package/src/components/upload/index.less +43 -0
- package/src/components/upload/index.ts +7 -0
- package/src/components/upload/src/helper.ts +32 -0
- package/src/components/upload/src/index.vue +38 -0
- package/src/components/upload/src/props.ts +48 -0
- package/src/components/upload/src/upload.vue +166 -0
- package/src/theme/header/helper/menu-tree.ts +2 -2
- package/src/theme/sider/helper/split-menu.ts +2 -2
- package/es/components/dialog/index.d.ts +0 -2
- package/es/components/dialog/src/hooks/useDialog.d.ts +0 -3
- package/src/components/dialog/index.ts +0 -5
- package/src/components/dialog/src/hooks/useDialog.ts +0 -85
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dt-tree">
|
|
3
|
+
<template v-if="showTitle">
|
|
4
|
+
<TreeHeader
|
|
5
|
+
:title="title"
|
|
6
|
+
:toolbar="toolbar"
|
|
7
|
+
:search="search"
|
|
8
|
+
:checkable="checkable"
|
|
9
|
+
:searchText="searchState.searchText"
|
|
10
|
+
:checkAll="checkAll"
|
|
11
|
+
:expandAll="expandAll"
|
|
12
|
+
@search="handleSearch"
|
|
13
|
+
>
|
|
14
|
+
{{ extendSlots(slots) }}
|
|
15
|
+
</TreeHeader>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<Spin :spinning="loading" tip="加载中...">
|
|
19
|
+
<div v-show="!getNotFound">
|
|
20
|
+
<Tree
|
|
21
|
+
v-bind="getBindValues"
|
|
22
|
+
:showIcon="false"
|
|
23
|
+
:treeData="treeData"
|
|
24
|
+
></Tree>
|
|
25
|
+
</div>
|
|
26
|
+
<Empty v-show="getNotFound"></Empty>
|
|
27
|
+
</Spin>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script lang="tsx" setup>
|
|
32
|
+
import { computed, reactive, ref, toRaw, unref, useAttrs, useSlots as vueUseSlots, watch, watchEffect } from 'vue'
|
|
33
|
+
import { cloneDeep, omit, difference, get } from 'lodash-es'
|
|
34
|
+
import { Spin, Empty, Tree } from 'ant-design-vue'
|
|
35
|
+
import TreeHeader from './components/treeHeader.vue'
|
|
36
|
+
import { BasicProps } from './props'
|
|
37
|
+
import { CheckKeys, CreateContextOptions, FieldNames, TreeItem, TreeState, KeyType, TreeActionType } from './type/tree'
|
|
38
|
+
import { isArray, isBoolean, isFunction, Recordable, isEmpty, useSlots } from '@dt-frames/core'
|
|
39
|
+
import { eachTree, filter, treeToList } from './utils/tree'
|
|
40
|
+
import { useTree } from './hooks/useTree'
|
|
41
|
+
|
|
42
|
+
const props = defineProps( BasicProps )
|
|
43
|
+
const attrs = useAttrs()
|
|
44
|
+
const slots = vueUseSlots()
|
|
45
|
+
|
|
46
|
+
const { getSlot, extendSlots } = useSlots()
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits([
|
|
49
|
+
'update:expandedKeys',
|
|
50
|
+
'update:selectedKeys',
|
|
51
|
+
'update:value',
|
|
52
|
+
'check',
|
|
53
|
+
'change',
|
|
54
|
+
'clickNode',
|
|
55
|
+
'update:searchValue'
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
const state = reactive<TreeState>({
|
|
60
|
+
checkStrictly: props.checkStrictly,
|
|
61
|
+
expandedKeys: props.expandedKeys || [],
|
|
62
|
+
selectedKeys: props.selectedKeys || [],
|
|
63
|
+
checkedKeys: props.checkedKeys || [],
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const searchState = reactive({
|
|
67
|
+
startSearch: false,
|
|
68
|
+
searchText: '',
|
|
69
|
+
searchData: [] as TreeItem[],
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const treeDataRef = ref<TreeItem[]>([])
|
|
73
|
+
|
|
74
|
+
const getFieldNames = computed(() => {
|
|
75
|
+
const { fieldNames } = props
|
|
76
|
+
return {
|
|
77
|
+
children: 'children',
|
|
78
|
+
title: 'title',
|
|
79
|
+
key: 'key',
|
|
80
|
+
...(fieldNames as FieldNames)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const getTreeData = computed((): TreeItem[] => {
|
|
85
|
+
return searchState.startSearch ? searchState.searchData : unref(treeDataRef)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const getNotFound = computed((): boolean => {
|
|
89
|
+
return !getTreeData.value || getTreeData.value.length === 0;
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const getIcon = (params: Recordable, icon?: string) => {
|
|
93
|
+
if (!icon) {
|
|
94
|
+
if ( props.renderIcon && isFunction( props.renderIcon ) ) {
|
|
95
|
+
return props.renderIcon(params)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return icon
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const treeData = computed(() => {
|
|
103
|
+
const data = cloneDeep(getTreeData.value)
|
|
104
|
+
|
|
105
|
+
eachTree(data, ( item, _parent ) => {
|
|
106
|
+
const searchText = searchState.searchText
|
|
107
|
+
|
|
108
|
+
const { highlight } = unref(props)
|
|
109
|
+
const { title: titleField, key: keyField, children: childrenField } = unref(getFieldNames)
|
|
110
|
+
|
|
111
|
+
const icon = getIcon(item, item.icon)
|
|
112
|
+
const title = get(item, titleField)
|
|
113
|
+
|
|
114
|
+
const searchIdx = searchText ? title.indexOf(searchText) : -1
|
|
115
|
+
const isHighlight = searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1
|
|
116
|
+
const highlightStyle = `color: ${isBoolean(highlight) ? '#f50' : highlight}`
|
|
117
|
+
|
|
118
|
+
const titleDom = isHighlight ? (
|
|
119
|
+
// 切割高亮的字
|
|
120
|
+
<span class="tree-content">
|
|
121
|
+
<span>{title.substr(0, searchIdx)}</span>
|
|
122
|
+
<span style={ highlightStyle }>{ searchText }</span>
|
|
123
|
+
<span>{ title.substr(searchIdx + (searchText as string).length) }</span>
|
|
124
|
+
</span>
|
|
125
|
+
) : (
|
|
126
|
+
title
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
item[titleField] = (
|
|
130
|
+
<span
|
|
131
|
+
class="tree-title"
|
|
132
|
+
onClick={ handleClickNode.bind(null, item[keyField], item[childrenField], item) }
|
|
133
|
+
>
|
|
134
|
+
{slots?.title ? (
|
|
135
|
+
getSlot(slots, 'title', item)
|
|
136
|
+
) : (
|
|
137
|
+
<>
|
|
138
|
+
{icon && <i class={ `i ${icon}`} />}
|
|
139
|
+
{titleDom}
|
|
140
|
+
<span class='tree-action'>{renderAction(item)}</span>
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
</span>
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return data
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
const showTitle = computed(() => {
|
|
153
|
+
return true
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const getBindValues = computed(() => {
|
|
157
|
+
let propsData = {
|
|
158
|
+
blockNode: true,
|
|
159
|
+
...attrs,
|
|
160
|
+
...props,
|
|
161
|
+
expandedKeys: state.expandedKeys,
|
|
162
|
+
selectedKeys: state.selectedKeys,
|
|
163
|
+
checkedKeys: state.checkedKeys,
|
|
164
|
+
checkStrictly: state.checkStrictly,
|
|
165
|
+
fieldNames: unref(getFieldNames),
|
|
166
|
+
'onUpdate:expandedKeys': (v: KeyType[]) => {
|
|
167
|
+
state.expandedKeys = v;
|
|
168
|
+
emit('update:expandedKeys', v);
|
|
169
|
+
},
|
|
170
|
+
'onUpdate:selectedKeys': (v: KeyType[]) => {
|
|
171
|
+
state.selectedKeys = v;
|
|
172
|
+
emit('update:selectedKeys', v);
|
|
173
|
+
},
|
|
174
|
+
onCheck: (v: CheckKeys, e) => {
|
|
175
|
+
let currentValue = toRaw( state.checkedKeys ) as KeyType[]
|
|
176
|
+
|
|
177
|
+
if ( isArray( currentValue ) && searchState.startSearch ) {
|
|
178
|
+
const { key } = unref(getFieldNames)
|
|
179
|
+
|
|
180
|
+
currentValue = difference(currentValue, getChildrenKeys(e.node.$attrs.node[key]))
|
|
181
|
+
|
|
182
|
+
if (e.checked) {
|
|
183
|
+
currentValue.push(e.node.$attrs.node[key]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
state.checkedKeys = currentValue
|
|
187
|
+
} else {
|
|
188
|
+
state.checkedKeys = v;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const rawVal = toRaw(state.checkedKeys)
|
|
192
|
+
|
|
193
|
+
emit('update:value', rawVal)
|
|
194
|
+
emit('check', rawVal, e)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return omit(propsData, 'treeData', 'class')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// 设置节点点击事件
|
|
202
|
+
function handleClickNode(key: string, children: TreeItem[], node: TreeItem) {
|
|
203
|
+
emit('clickNode', node)
|
|
204
|
+
|
|
205
|
+
if (!props.clickRowToExpand || !children || children.length === 0) return
|
|
206
|
+
|
|
207
|
+
if (!state.expandedKeys.includes(key)) {
|
|
208
|
+
setExpandedKeys([...state.expandedKeys, key])
|
|
209
|
+
} else {
|
|
210
|
+
const keys = [...state.expandedKeys]
|
|
211
|
+
const index = keys.findIndex((item) => item === key)
|
|
212
|
+
if (index !== -1) {
|
|
213
|
+
keys.splice(index, 1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setExpandedKeys(keys)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 渲染操作栏
|
|
221
|
+
function renderAction(node: TreeItem) {
|
|
222
|
+
const { actionList } = props
|
|
223
|
+
if (!actionList || actionList.length === 0) return
|
|
224
|
+
|
|
225
|
+
return actionList.map((item, index) => {
|
|
226
|
+
let nodeShow = true
|
|
227
|
+
|
|
228
|
+
if (isFunction(item.show)) {
|
|
229
|
+
nodeShow = item.show?.(node);
|
|
230
|
+
} else if (isBoolean(item.show)) {
|
|
231
|
+
nodeShow = item.show;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!nodeShow) return null
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<span key={index} class='tree-action'>
|
|
238
|
+
{ item.render(node) }
|
|
239
|
+
</span>
|
|
240
|
+
)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const {
|
|
246
|
+
deleteNodeByKey,
|
|
247
|
+
insertNodeByKey,
|
|
248
|
+
insertNodesByKey,
|
|
249
|
+
filterByLevel,
|
|
250
|
+
updateNodeByKey,
|
|
251
|
+
getAllKeys,
|
|
252
|
+
getChildrenKeys,
|
|
253
|
+
getEnabledKeys,
|
|
254
|
+
getSelectedNode,
|
|
255
|
+
} = useTree( treeDataRef, getFieldNames )
|
|
256
|
+
|
|
257
|
+
// 设置实例暴露的方法
|
|
258
|
+
function setExpandedKeys(keys: KeyType[]) { state.expandedKeys = keys }
|
|
259
|
+
function getExpandedKeys() { return state.expandedKeys }
|
|
260
|
+
|
|
261
|
+
function setSelectedKeys( keys: KeyType[] ) { state.selectedKeys = keys }
|
|
262
|
+
function getSelectedKeys() { return state.selectedKeys }
|
|
263
|
+
|
|
264
|
+
function setCheckedKeys(keys: CheckKeys) { state.checkedKeys = keys }
|
|
265
|
+
function getCheckedKeys() { return state.checkedKeys }
|
|
266
|
+
|
|
267
|
+
function checkAll(checkAll: boolean) { state.checkedKeys = checkAll ? getEnabledKeys() : ([] as KeyType[]) }
|
|
268
|
+
function expandAll(expandAll: boolean) { state.expandedKeys = expandAll ? getAllKeys() : ([] as KeyType[]) }
|
|
269
|
+
|
|
270
|
+
function onStrictlyChange(strictly: boolean) { state.checkStrictly = strictly }
|
|
271
|
+
|
|
272
|
+
const instance: TreeActionType = {
|
|
273
|
+
setExpandedKeys,
|
|
274
|
+
getExpandedKeys,
|
|
275
|
+
setSelectedKeys,
|
|
276
|
+
getSelectedKeys,
|
|
277
|
+
setCheckedKeys,
|
|
278
|
+
getCheckedKeys,
|
|
279
|
+
insertNodeByKey,
|
|
280
|
+
insertNodesByKey,
|
|
281
|
+
deleteNodeByKey,
|
|
282
|
+
updateNodeByKey,
|
|
283
|
+
getSelectedNode,
|
|
284
|
+
checkAll,
|
|
285
|
+
expandAll,
|
|
286
|
+
onStrictlyChange,
|
|
287
|
+
filterByLevel: (level: number) => {
|
|
288
|
+
state.expandedKeys = filterByLevel(level);
|
|
289
|
+
},
|
|
290
|
+
setSearchValue: (value: string) => {
|
|
291
|
+
handleSearch(value);
|
|
292
|
+
},
|
|
293
|
+
getSearchValue: () => {
|
|
294
|
+
return searchState.searchText;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function handleSearch(searchValue: string) {
|
|
299
|
+
if (searchValue !== searchState.searchText) searchState.searchText = searchValue
|
|
300
|
+
emit('update:searchValue', searchValue)
|
|
301
|
+
|
|
302
|
+
if (!searchValue) {
|
|
303
|
+
searchState.startSearch = false
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } = unref(props)
|
|
308
|
+
const { title: titleField, key: keyField } = unref(getFieldNames)
|
|
309
|
+
const matchedKeys: string[] = []
|
|
310
|
+
|
|
311
|
+
searchState.startSearch = true
|
|
312
|
+
|
|
313
|
+
searchState.searchData = filter(
|
|
314
|
+
unref(treeDataRef),
|
|
315
|
+
(node) => {
|
|
316
|
+
const result = filterFn
|
|
317
|
+
? filterFn(searchValue, node, unref(getFieldNames))
|
|
318
|
+
: node[titleField]?.includes(searchValue) ?? false
|
|
319
|
+
|
|
320
|
+
if (result) {
|
|
321
|
+
matchedKeys.push(node[keyField]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return result;
|
|
325
|
+
},
|
|
326
|
+
unref(getFieldNames),
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
if (expandOnSearch) {
|
|
330
|
+
const expandKeys = treeToList(searchState.searchData).map((val) => {
|
|
331
|
+
return val[keyField]
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
if (expandKeys && expandKeys.length) {
|
|
335
|
+
setExpandedKeys(expandKeys)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (checkOnSearch && checkable && matchedKeys.length) {
|
|
340
|
+
setCheckedKeys(matchedKeys)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (selectedOnSearch && matchedKeys.length) {
|
|
344
|
+
setSelectedKeys(matchedKeys)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
watch(
|
|
349
|
+
() => props.searchValue,
|
|
350
|
+
(val) => {
|
|
351
|
+
if (val !== searchState.searchText) {
|
|
352
|
+
searchState.searchText = val
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
{ immediate: true }
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
watch(
|
|
359
|
+
() => props.treeData,
|
|
360
|
+
(val) => {
|
|
361
|
+
if (val) {
|
|
362
|
+
handleSearch(searchState.searchText)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
watch(
|
|
368
|
+
() => props.value,
|
|
369
|
+
() => {
|
|
370
|
+
state.checkedKeys = toRaw(props.value || [])
|
|
371
|
+
},
|
|
372
|
+
{ immediate: true }
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
watch(
|
|
376
|
+
() => state.checkedKeys,
|
|
377
|
+
() => {
|
|
378
|
+
const v = toRaw(state.checkedKeys)
|
|
379
|
+
emit('update:value', v)
|
|
380
|
+
emit('change', v)
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
watchEffect(() => { treeDataRef.value = props.treeData as TreeItem[] })
|
|
385
|
+
watchEffect(() => { state.expandedKeys = props.expandedKeys })
|
|
386
|
+
watchEffect(() => { state.selectedKeys = props.selectedKeys })
|
|
387
|
+
watchEffect(() => { state.checkedKeys = props.checkedKeys })
|
|
388
|
+
watchEffect(() => { state.checkStrictly = props.checkStrictly })
|
|
389
|
+
|
|
390
|
+
defineExpose(instance)
|
|
391
|
+
|
|
392
|
+
</script>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Recordable } from "@dt-frames/core";
|
|
2
|
+
import { TreeDataItem } from "ant-design-vue/lib/tree";
|
|
3
|
+
import { PropType } from "vue";
|
|
4
|
+
import { CheckKeys, ContextMenuItem, FieldNames, TreeActionItem, TreeItem } from "./type/tree"
|
|
5
|
+
|
|
6
|
+
export const BasicProps = {
|
|
7
|
+
loading: {
|
|
8
|
+
type: Boolean,
|
|
9
|
+
default: false,
|
|
10
|
+
},
|
|
11
|
+
// 粘性选择
|
|
12
|
+
checkStrictly: Boolean,
|
|
13
|
+
// 展开的key值
|
|
14
|
+
expandedKeys: {
|
|
15
|
+
type: Array as PropType<KeyType[]>,
|
|
16
|
+
default: () => []
|
|
17
|
+
},
|
|
18
|
+
// 设置选中的key
|
|
19
|
+
selectedKeys: {
|
|
20
|
+
type: Array as PropType<KeyType[]>,
|
|
21
|
+
default: () => []
|
|
22
|
+
},
|
|
23
|
+
// 选中的节点
|
|
24
|
+
checkedKeys: {
|
|
25
|
+
type: Array as PropType<CheckKeys>,
|
|
26
|
+
default: () => []
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// 定义树节点的lable title children 名称
|
|
30
|
+
fieldNames: {
|
|
31
|
+
type: Object as PropType<FieldNames>,
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
beforeRightClick: {
|
|
35
|
+
type: Function as PropType<(...arg: any) => ContextMenuItem[]>,
|
|
36
|
+
default: undefined,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
rightMenuList: {
|
|
40
|
+
type: Array as PropType<ContextMenuItem[]>,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
//
|
|
44
|
+
renderIcon: {
|
|
45
|
+
type: Function as PropType<(params: Recordable) => string>,
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
|
|
49
|
+
highlight: {
|
|
50
|
+
type: [Boolean, String] as PropType<Boolean | String>,
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
actionList: {
|
|
55
|
+
type: Array as PropType<TreeActionItem[]>,
|
|
56
|
+
default: () => [],
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
clickRowToExpand: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
default: false,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
searchValue: {
|
|
65
|
+
type: String,
|
|
66
|
+
default: ''
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
filterFn: {
|
|
70
|
+
type: Function as PropType< (searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean >,
|
|
71
|
+
default: undefined,
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
checkable: Boolean,
|
|
75
|
+
|
|
76
|
+
// 搜索完成时自动展开结果
|
|
77
|
+
expandOnSearch: Boolean,
|
|
78
|
+
|
|
79
|
+
// 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
|
|
80
|
+
checkOnSearch: Boolean,
|
|
81
|
+
|
|
82
|
+
// 搜索完成自动select所有结果
|
|
83
|
+
selectedOnSearch: Boolean,
|
|
84
|
+
|
|
85
|
+
// 树数据
|
|
86
|
+
treeData: {
|
|
87
|
+
type: Array as PropType<TreeDataItem[]>
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
value: {
|
|
91
|
+
type: [Object, Array] as PropType<KeyType[] | CheckKeys>
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
title: {
|
|
95
|
+
type: String,
|
|
96
|
+
default: '',
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
toolbar: Boolean,
|
|
100
|
+
search: Boolean,
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const searchProps = {
|
|
105
|
+
title: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: '',
|
|
108
|
+
},
|
|
109
|
+
toolbar: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: false,
|
|
112
|
+
},
|
|
113
|
+
checkable: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: false,
|
|
116
|
+
},
|
|
117
|
+
search: {
|
|
118
|
+
type: Boolean,
|
|
119
|
+
default: false,
|
|
120
|
+
},
|
|
121
|
+
searchText: {
|
|
122
|
+
type: String,
|
|
123
|
+
default: '',
|
|
124
|
+
},
|
|
125
|
+
checkAll: {
|
|
126
|
+
type: Function,
|
|
127
|
+
default: undefined,
|
|
128
|
+
},
|
|
129
|
+
expandAll: {
|
|
130
|
+
type: Function,
|
|
131
|
+
default: undefined,
|
|
132
|
+
},
|
|
133
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Recordable } from "@dt-frames/core";
|
|
2
|
+
import { Fn } from "@vueuse/core"
|
|
3
|
+
import { TreeDataItem } from "ant-design-vue/lib/tree"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export enum ToolbarEnum {
|
|
7
|
+
SELECT_ALL,
|
|
8
|
+
UN_SELECT_ALL,
|
|
9
|
+
EXPAND_ALL,
|
|
10
|
+
UN_EXPAND_ALL,
|
|
11
|
+
CHECK_STRICTLY,
|
|
12
|
+
CHECK_UN_STRICTLY,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type KeyType = string | number
|
|
16
|
+
|
|
17
|
+
export type CheckKeys =
|
|
18
|
+
| KeyType[]
|
|
19
|
+
| {
|
|
20
|
+
checked: string[] | number[];
|
|
21
|
+
halfChecked: string[] | number[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TreeState {
|
|
25
|
+
expandedKeys: KeyType[]
|
|
26
|
+
selectedKeys: KeyType[]
|
|
27
|
+
checkedKeys: CheckKeys
|
|
28
|
+
checkStrictly: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FieldNames {
|
|
32
|
+
children?: string
|
|
33
|
+
title?: string
|
|
34
|
+
key?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TreeItem extends TreeDataItem {
|
|
38
|
+
icon?: any
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface CreateContextOptions {
|
|
42
|
+
event: MouseEvent
|
|
43
|
+
icon?: string
|
|
44
|
+
styles?: any
|
|
45
|
+
items?: ContextMenuItem[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface InsertNodeParams {
|
|
49
|
+
parentKey: string | null
|
|
50
|
+
node: TreeDataItem
|
|
51
|
+
list?: TreeDataItem[]
|
|
52
|
+
push?: 'push' | 'unshift'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ContextMenuItem {
|
|
56
|
+
label: string
|
|
57
|
+
icon?: string
|
|
58
|
+
hidden?: boolean
|
|
59
|
+
disabled?: boolean
|
|
60
|
+
handler?: Fn
|
|
61
|
+
divider?: boolean
|
|
62
|
+
children?: ContextMenuItem[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface TreeActionItem {
|
|
66
|
+
render: (record: Recordable) => any
|
|
67
|
+
show?: boolean | ((record: Recordable) => boolean)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface InsertNodeParams {
|
|
71
|
+
parentKey: string | null
|
|
72
|
+
node: TreeDataItem
|
|
73
|
+
list?: TreeDataItem[]
|
|
74
|
+
push?: 'push' | 'unshift'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface TreeHelperConfig {
|
|
78
|
+
id: string;
|
|
79
|
+
children: string;
|
|
80
|
+
pid: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TreeActionType {
|
|
84
|
+
checkAll: (checkAll: boolean) => void
|
|
85
|
+
expandAll: (expandAll: boolean) => void
|
|
86
|
+
setExpandedKeys: (keys: KeyType[]) => void
|
|
87
|
+
getExpandedKeys: () => KeyType[]
|
|
88
|
+
setSelectedKeys: (keys: KeyType[]) => void
|
|
89
|
+
getSelectedKeys: () => KeyType[]
|
|
90
|
+
setCheckedKeys: (keys: CheckKeys) => void
|
|
91
|
+
getCheckedKeys: () => CheckKeys
|
|
92
|
+
filterByLevel: (level: number) => void
|
|
93
|
+
insertNodeByKey: (opt: InsertNodeParams) => void
|
|
94
|
+
insertNodesByKey: (opt: InsertNodeParams) => void
|
|
95
|
+
deleteNodeByKey: (key: string) => void
|
|
96
|
+
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void
|
|
97
|
+
setSearchValue: (value: string) => void
|
|
98
|
+
getSearchValue: () => string
|
|
99
|
+
onStrictlyChange: ( strictly: boolean ) => void
|
|
100
|
+
getSelectedNode: (
|
|
101
|
+
key: KeyType,
|
|
102
|
+
treeList?: TreeItem[],
|
|
103
|
+
selectNode?: TreeItem | null,
|
|
104
|
+
) => TreeItem | null
|
|
105
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TreeHelperConfig } from "../type/tree"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 递归遍历树结构
|
|
5
|
+
* @param treeDatas 树数据
|
|
6
|
+
* @param callBack 回调
|
|
7
|
+
* @param parentNode 父节点
|
|
8
|
+
*/
|
|
9
|
+
export function eachTree(treeDatas: any[], callBack: Function, parentNode = {}) {
|
|
10
|
+
treeDatas.forEach( el => {
|
|
11
|
+
const newNode = callBack(el, parentNode) || el
|
|
12
|
+
|
|
13
|
+
if (el.children) {
|
|
14
|
+
eachTree( el.children, callBack, newNode )
|
|
15
|
+
}
|
|
16
|
+
} )
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
|
|
21
|
+
export function forEach<T = any>(
|
|
22
|
+
tree: T[],
|
|
23
|
+
func: (n: T) => any,
|
|
24
|
+
config: Partial<TreeHelperConfig> = {},
|
|
25
|
+
): void {
|
|
26
|
+
const list: any[] = [...tree]
|
|
27
|
+
const { children = 'children' } = config
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < list.length; i++) {
|
|
30
|
+
if (func(list[i])) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
children && list[i][children] && list.splice(i + 1, 0, ...list[i][children])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function filter<T = any>(
|
|
38
|
+
tree: T[],
|
|
39
|
+
func: (n: T) => boolean,
|
|
40
|
+
config: Partial<TreeHelperConfig> = {},
|
|
41
|
+
): T[] {
|
|
42
|
+
// 获取配置
|
|
43
|
+
const children = config.children as string || 'children';
|
|
44
|
+
|
|
45
|
+
function listFilter(list: T[]) {
|
|
46
|
+
return list
|
|
47
|
+
.map((node: any) => ({ ...node }))
|
|
48
|
+
.filter((node) => {
|
|
49
|
+
|
|
50
|
+
// 递归调用 对含有children项 进行再次调用自身函数 listFilter
|
|
51
|
+
node[children] = node[children] && listFilter(node[children]);
|
|
52
|
+
|
|
53
|
+
// 执行传入的回调 func 进行过滤
|
|
54
|
+
return func(node) || (node[children] && node[children].length);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return listFilter(tree);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function treeToList<T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T {
|
|
62
|
+
const { children = 'children' } = config
|
|
63
|
+
|
|
64
|
+
const result: any = [...tree]
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < result.length; i++) {
|
|
67
|
+
if (!result[i][children!]) continue
|
|
68
|
+
result.splice(i + 1, 0, ...result[i][children!])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result
|
|
72
|
+
}
|
|
73
|
+
|