oxy-uni-ui 1.2.0 → 1.2.3
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/attributes.json +1 -1
- package/components/common/abstracts/variable.scss +51 -1
- package/components/common/path.ts +9 -0
- package/components/common/util.ts +42 -0
- package/components/composables/useGlobalLoading.ts +42 -0
- package/components/composables/useGlobalMessage.ts +48 -0
- package/components/composables/useGlobalToast.ts +84 -0
- package/components/composables/useVirtualScroll.ts +3 -2
- package/components/oxy-cell/oxy-cell.vue +15 -2
- package/components/oxy-cell/types.ts +4 -0
- package/components/oxy-checkbox/index.scss +1 -1
- package/components/oxy-checkbox/oxy-checkbox.vue +2 -2
- package/components/oxy-col-picker/oxy-col-picker.vue +3 -0
- package/components/oxy-col-picker/types.ts +5 -1
- package/components/oxy-corner/oxy-corner.vue +15 -3
- package/components/oxy-corner/types.ts +15 -1
- package/components/oxy-date-strip/index.scss +10 -0
- package/components/oxy-date-strip/oxy-date-strip.vue +198 -0
- package/components/oxy-date-strip/types.ts +98 -0
- package/components/oxy-date-strip/utils.ts +67 -0
- package/components/oxy-date-strip-item/index.scss +94 -0
- package/components/oxy-date-strip-item/oxy-date-strip-item.vue +102 -0
- package/components/oxy-date-strip-item/types.ts +53 -0
- package/components/oxy-datetime-picker/oxy-datetime-picker.vue +3 -1
- package/components/oxy-datetime-picker/types.ts +5 -1
- package/components/oxy-echarts/index.scss +17 -0
- package/components/oxy-echarts/index.ts +1 -0
- package/components/oxy-echarts/oxy-echarts.vue +32 -0
- package/components/oxy-echarts/types.ts +12 -0
- package/components/oxy-file-list/index.scss +26 -0
- package/components/oxy-file-list/oxy-file-list.vue +208 -34
- package/components/oxy-file-list/types.ts +58 -2
- package/components/oxy-global-loading/oxy-global-loading.vue +53 -0
- package/components/oxy-global-message/oxy-global-message.vue +64 -0
- package/components/oxy-global-toast/oxy-global-toast.vue +53 -0
- package/components/oxy-img-lazy/index.scss +17 -0
- package/components/oxy-img-lazy/oxy-img-lazy.vue +332 -0
- package/components/oxy-img-lazy/types.ts +69 -0
- package/components/oxy-link/index.scss +57 -0
- package/components/oxy-link/oxy-link.vue +130 -0
- package/components/oxy-link/types.ts +81 -0
- package/components/oxy-list/index.scss +7 -1
- package/components/oxy-list/types.ts +1 -1
- package/components/oxy-picker/oxy-picker.vue +3 -0
- package/components/oxy-picker/types.ts +5 -1
- package/components/oxy-radio/index.scss +3 -3
- package/components/oxy-radio/oxy-radio.vue +1 -1
- package/components/oxy-rich-text/icon/emjio.svg +1 -0
- package/components/oxy-rich-text/icon/quote.svg +1 -0
- package/components/oxy-rich-text/icon/text.svg +1 -0
- package/components/oxy-rich-text/icon/title.svg +1 -0
- package/components/oxy-rich-text/index.scss +159 -0
- package/components/oxy-rich-text/mp-html/card/card.vue +122 -0
- package/components/oxy-rich-text/mp-html/card/index.js +7 -0
- package/components/oxy-rich-text/mp-html/editable/config.js +15 -0
- package/components/oxy-rich-text/mp-html/editable/index.js +553 -0
- package/components/oxy-rich-text/mp-html/emoji/index.js +203 -0
- package/components/oxy-rich-text/mp-html/highlight/config.js +5 -0
- package/components/oxy-rich-text/mp-html/highlight/index.js +96 -0
- package/components/oxy-rich-text/mp-html/highlight/prism.css +1 -0
- package/components/oxy-rich-text/mp-html/highlight/prism.min.js +7 -0
- package/components/oxy-rich-text/mp-html/img-cache/index.js +138 -0
- package/components/oxy-rich-text/mp-html/latex/index.js +80 -0
- package/components/oxy-rich-text/mp-html/latex/katex.css +1 -0
- package/components/oxy-rich-text/mp-html/latex/katex.min.js +1 -0
- package/components/oxy-rich-text/mp-html/markdown/index.js +50 -0
- package/components/oxy-rich-text/mp-html/markdown/marked.min.js +71 -0
- package/components/oxy-rich-text/mp-html/mp-html.d.ts +184 -0
- package/components/oxy-rich-text/mp-html/mp-html.vue +675 -0
- package/components/oxy-rich-text/mp-html/node/node.vue +1161 -0
- package/components/oxy-rich-text/mp-html/parser.js +1428 -0
- package/components/oxy-rich-text/mp-html/search/index.js +132 -0
- package/components/oxy-rich-text/mp-html/style/index.js +129 -0
- package/components/oxy-rich-text/mp-html/style/parser.js +175 -0
- package/components/oxy-rich-text/mp-html/template/index.js +67 -0
- package/components/oxy-rich-text/mp-html/txv-video/index.js +46 -0
- package/components/oxy-rich-text/oxy-rich-text.vue +642 -0
- package/components/oxy-rich-text/types.ts +71 -0
- package/components/oxy-select/index.scss +255 -0
- package/components/oxy-select/oxy-select.vue +421 -0
- package/components/oxy-select/types.ts +71 -0
- package/components/oxy-select-picker/oxy-select-picker.vue +3 -0
- package/components/oxy-select-picker/types.ts +5 -1
- package/components/oxy-stream-render/index.scss +6 -0
- package/components/oxy-stream-render/oxy-stream-render.vue +204 -0
- package/components/oxy-stream-render/types.ts +5 -0
- package/components/oxy-tree/index.scss +17 -1
- package/components/oxy-tree/oxy-tree.vue +89 -8
- package/components/oxy-tree/types.ts +11 -1
- package/components/oxy-waterfall/index.scss +18 -0
- package/components/oxy-waterfall/oxy-waterfall.vue +218 -0
- package/components/oxy-waterfall/types.ts +90 -0
- package/components/oxy-waterfall-item/index.scss +8 -0
- package/components/oxy-waterfall-item/oxy-waterfall-item.vue +89 -0
- package/components/oxy-waterfall-item/types.ts +16 -0
- package/global.d.ts +7 -0
- package/index.ts +3 -0
- package/locale/lang/en-US.ts +26 -0
- package/locale/lang/zh-CN.ts +26 -0
- package/oxy-uni-ui.zip +0 -0
- package/package.json +1 -1
- package/tags.json +1 -1
- package/uni-echarts/changelog.md +2 -0
- package/uni-echarts/components/index.js +1 -0
- package/uni-echarts/components/uni-echarts/events.js +95 -0
- package/uni-echarts/components/uni-echarts/types.d.ts +183 -0
- package/uni-echarts/components/uni-echarts/types.js +1 -0
- package/uni-echarts/components/uni-echarts/uni-echarts.vue +530 -0
- package/uni-echarts/components/uni-echarts/uni-echarts.vue.d.ts +19 -0
- package/uni-echarts/global.d.ts +7 -0
- package/uni-echarts/index.d.ts +440 -0
- package/uni-echarts/index.js +2 -0
- package/uni-echarts/package.json +105 -0
- package/uni-echarts/shared-core.d.ts +269 -0
- package/uni-echarts/shared-core.js +900 -0
- package/web-types.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ExtractPropTypes } from 'vue'
|
|
2
|
+
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStringProp } from '../common/props'
|
|
3
|
+
|
|
4
|
+
export const selectProps = {
|
|
5
|
+
...baseProps,
|
|
6
|
+
/**
|
|
7
|
+
* 选中项
|
|
8
|
+
*/
|
|
9
|
+
modelValue: makeRequiredProp([String, Number, Array]),
|
|
10
|
+
/**
|
|
11
|
+
* 是否多选
|
|
12
|
+
*/
|
|
13
|
+
multiple: makeBooleanProp(false),
|
|
14
|
+
/**
|
|
15
|
+
* 是否禁用
|
|
16
|
+
*/
|
|
17
|
+
disabled: makeBooleanProp(false),
|
|
18
|
+
/** 只读 */
|
|
19
|
+
readonly: makeBooleanProp(false),
|
|
20
|
+
/**
|
|
21
|
+
* 多作为 key 唯一标识的键名
|
|
22
|
+
*/
|
|
23
|
+
labelKey: makeStringProp('label'),
|
|
24
|
+
/**
|
|
25
|
+
* 多作为 value 唯一标识的键名
|
|
26
|
+
*/
|
|
27
|
+
valueKey: makeStringProp('value'),
|
|
28
|
+
/**
|
|
29
|
+
* 是否开启搜索
|
|
30
|
+
*/
|
|
31
|
+
filterable: makeBooleanProp(false),
|
|
32
|
+
/**
|
|
33
|
+
* 是否折叠显示标签
|
|
34
|
+
*/
|
|
35
|
+
collapseTags: makeBooleanProp(false),
|
|
36
|
+
/**
|
|
37
|
+
* 超过多少个标签才折叠
|
|
38
|
+
*/
|
|
39
|
+
collapseTagsNum: makeNumberProp(1),
|
|
40
|
+
/**
|
|
41
|
+
* 选择器数据
|
|
42
|
+
*/
|
|
43
|
+
columns: makeArrayProp<Record<string, any>>(),
|
|
44
|
+
/**
|
|
45
|
+
* 左侧标题
|
|
46
|
+
*/
|
|
47
|
+
label: makeStringProp(''),
|
|
48
|
+
/** 设置左侧标题宽度 */
|
|
49
|
+
labelWidth: makeStringProp(''),
|
|
50
|
+
/** 选择器的值靠右展示 */
|
|
51
|
+
alignRight: makeBooleanProp(false),
|
|
52
|
+
/**
|
|
53
|
+
* 输入框的提示文字
|
|
54
|
+
*/
|
|
55
|
+
placeholder: makeStringProp('请选择'),
|
|
56
|
+
/**
|
|
57
|
+
* 无选项提示
|
|
58
|
+
*/
|
|
59
|
+
emptyTips: makeStringProp('无选项'),
|
|
60
|
+
/**
|
|
61
|
+
* 是否可清除
|
|
62
|
+
*/
|
|
63
|
+
clear: makeBooleanProp(true),
|
|
64
|
+
/**
|
|
65
|
+
* 格式化输出
|
|
66
|
+
*/
|
|
67
|
+
format: makeStringProp(''),
|
|
68
|
+
defItem: makeNumberProp(0)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type SelectProps = ExtractPropTypes<typeof selectProps>
|
|
@@ -320,6 +320,9 @@ function close() {
|
|
|
320
320
|
|
|
321
321
|
function open() {
|
|
322
322
|
if (props.disabled || props.readonly) return
|
|
323
|
+
if (isFunction(props.beforeOpen) && !props.beforeOpen()) {
|
|
324
|
+
return
|
|
325
|
+
}
|
|
323
326
|
selectList.value = valueFormat(props.modelValue)
|
|
324
327
|
pickerShow.value = true
|
|
325
328
|
isConfirm.value = false
|
|
@@ -105,7 +105,11 @@ export const selectPickerProps = {
|
|
|
105
105
|
/**
|
|
106
106
|
* 必填标记位置,可选值:before、after
|
|
107
107
|
*/
|
|
108
|
-
markerSide: makeStringProp<'before' | 'after'>('before')
|
|
108
|
+
markerSide: makeStringProp<'before' | 'after'>('before'),
|
|
109
|
+
/**
|
|
110
|
+
* 打开pop之前的校验
|
|
111
|
+
*/
|
|
112
|
+
beforeOpen: Function as PropType<() => boolean>
|
|
109
113
|
}
|
|
110
114
|
export type SelectPickerProps = ExtractPropTypes<typeof selectPickerProps>
|
|
111
115
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- #ifdef H5 || APP-PLUS -->
|
|
3
|
+
<view class="oxy-stream-render" :change:stream-request="appStreamHandler.handleStreamRequest" :stream-request="streamRequest" />
|
|
4
|
+
<!-- #endif -->
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
export default {
|
|
9
|
+
name: 'oxy-stream-render',
|
|
10
|
+
props: {
|
|
11
|
+
streamRequest: {
|
|
12
|
+
type: Object,
|
|
13
|
+
default: () => ({})
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
options: {
|
|
17
|
+
addGlobalClass: true,
|
|
18
|
+
virtualHost: true,
|
|
19
|
+
styleIsolation: 'shared'
|
|
20
|
+
},
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
requestTask: null
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
watch: {
|
|
27
|
+
streamRequest: {
|
|
28
|
+
handler(newVal) {
|
|
29
|
+
// #ifndef H5 || APP-PLUS
|
|
30
|
+
this.handleStreamRequest(newVal)
|
|
31
|
+
// #endif
|
|
32
|
+
},
|
|
33
|
+
deep: true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
methods: {
|
|
37
|
+
handleAppStreamData({ chunk, finished }) {
|
|
38
|
+
this.$emit('stream-data', { chunk, finished })
|
|
39
|
+
},
|
|
40
|
+
handleAppStreamError({ message, type }) {
|
|
41
|
+
this.$emit('stream-error', { message, type })
|
|
42
|
+
},
|
|
43
|
+
// #ifndef H5 || APP-PLUS
|
|
44
|
+
handleStreamRequest(newVal) {
|
|
45
|
+
if (newVal && newVal.cancel) {
|
|
46
|
+
if (this.requestTask) {
|
|
47
|
+
this.requestTask.abort()
|
|
48
|
+
this.requestTask = null
|
|
49
|
+
}
|
|
50
|
+
this.handleAppStreamData({
|
|
51
|
+
chunk: null,
|
|
52
|
+
finished: true
|
|
53
|
+
})
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!newVal || !newVal.url) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.requestTask) {
|
|
62
|
+
this.requestTask.abort()
|
|
63
|
+
this.requestTask = null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { url, method, headers, body } = newVal
|
|
67
|
+
|
|
68
|
+
this.requestTask = uni.request({
|
|
69
|
+
url,
|
|
70
|
+
method: method || 'GET',
|
|
71
|
+
header: headers || {},
|
|
72
|
+
data: body,
|
|
73
|
+
enableChunked: true,
|
|
74
|
+
success: (res) => {
|
|
75
|
+
this.handleAppStreamData({
|
|
76
|
+
chunk: null,
|
|
77
|
+
finished: true
|
|
78
|
+
})
|
|
79
|
+
this.requestTask = null
|
|
80
|
+
},
|
|
81
|
+
fail: (err) => {
|
|
82
|
+
this.handleAppStreamError({
|
|
83
|
+
message: err.errMsg || 'Request failed',
|
|
84
|
+
type: err.errMsg === 'request:fail abort' ? 'AbortError' : 'Error'
|
|
85
|
+
})
|
|
86
|
+
this.requestTask = null
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
// 这里监听消息
|
|
90
|
+
this.requestTask.onChunkReceived((res) => {
|
|
91
|
+
let decoder = new TextDecoder('utf-8')
|
|
92
|
+
let chunk = decoder.decode(new Uint8Array(res.data))
|
|
93
|
+
this.handleAppStreamData({
|
|
94
|
+
chunk,
|
|
95
|
+
finished: false
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
// #endif
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<script module="appStreamHandler" lang="renderjs">
|
|
105
|
+
// #ifdef H5 || APP-PLUS
|
|
106
|
+
module.exports = {
|
|
107
|
+
data() {
|
|
108
|
+
return {
|
|
109
|
+
abortController: null,
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
methods: {
|
|
113
|
+
handleStreamRequest(newVal, _oldVal, _ownerInstance) {
|
|
114
|
+
if (newVal && newVal.cancel) {
|
|
115
|
+
if (this.abortController) {
|
|
116
|
+
this.abortController.abort()
|
|
117
|
+
this.abortController = null
|
|
118
|
+
}
|
|
119
|
+
this.$ownerInstance.callMethod('handleAppStreamData', {
|
|
120
|
+
chunk: null,
|
|
121
|
+
finished: true,
|
|
122
|
+
})
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!newVal || !newVal.url) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (this.abortController) {
|
|
131
|
+
this.abortController.abort()
|
|
132
|
+
this.abortController = null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.abortController = new AbortController()
|
|
136
|
+
|
|
137
|
+
const { url, method, headers, body } = newVal
|
|
138
|
+
|
|
139
|
+
fetch(url, {
|
|
140
|
+
method,
|
|
141
|
+
headers,
|
|
142
|
+
body,
|
|
143
|
+
signal: this.abortController.signal,
|
|
144
|
+
})
|
|
145
|
+
.then((response) => {
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw new Error(`HTTP error! status: ${response.status}`)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!response.body || !response.body.getReader) {
|
|
151
|
+
throw new Error('ReadableStream not supported')
|
|
152
|
+
}
|
|
153
|
+
const reader = response.body.getReader()
|
|
154
|
+
const decoder = new TextDecoder()
|
|
155
|
+
|
|
156
|
+
const readStream = async () => {
|
|
157
|
+
try {
|
|
158
|
+
const { done, value } = await reader.read()
|
|
159
|
+
|
|
160
|
+
if (done) {
|
|
161
|
+
this.abortController = null
|
|
162
|
+
this.$ownerInstance.callMethod('handleAppStreamData', {
|
|
163
|
+
chunk: null,
|
|
164
|
+
finished: true,
|
|
165
|
+
})
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const chunk = decoder.decode(value, { stream: true })
|
|
170
|
+
|
|
171
|
+
this.$ownerInstance.callMethod('handleAppStreamData', {
|
|
172
|
+
chunk,
|
|
173
|
+
finished: false,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
return readStream()
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.abortController = null
|
|
180
|
+
this.$ownerInstance.callMethod('handleAppStreamError', {
|
|
181
|
+
message: error.message,
|
|
182
|
+
type:error.name
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return readStream()
|
|
188
|
+
})
|
|
189
|
+
.catch((error) => {
|
|
190
|
+
this.abortController = null
|
|
191
|
+
this.$ownerInstance.callMethod('handleAppStreamError', {
|
|
192
|
+
message: error.message,
|
|
193
|
+
type:error.name
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
// #endif
|
|
200
|
+
</script>
|
|
201
|
+
|
|
202
|
+
<style lang="scss" scoped>
|
|
203
|
+
@import './index.scss';
|
|
204
|
+
</style>
|
|
@@ -23,6 +23,22 @@
|
|
|
23
23
|
left: 0;
|
|
24
24
|
right: 0;
|
|
25
25
|
}
|
|
26
|
+
.oxy-tree__search{
|
|
27
|
+
display: flex;
|
|
28
|
+
:deep(.oxy-button){
|
|
29
|
+
min-width: 60px;
|
|
30
|
+
}
|
|
31
|
+
:deep(.oxy-tree__search-input) {
|
|
32
|
+
flex: 1;
|
|
33
|
+
background-color: $-tree-search-input-color;
|
|
34
|
+
padding: 6px 12px;
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
margin-bottom: 8px;
|
|
37
|
+
margin-right: 12px;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
.oxy-tree-node-content {
|
|
@@ -80,4 +96,4 @@
|
|
|
80
96
|
.oxy-tree-node {
|
|
81
97
|
flex: 1;
|
|
82
98
|
}
|
|
83
|
-
}
|
|
99
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="oxy-tree" :class="customClass" :style="customStyle">
|
|
3
|
+
<slot name="search-button" v-if="showFilter">
|
|
4
|
+
<view class="oxy-tree__search">
|
|
5
|
+
<oxy-input
|
|
6
|
+
type="text"
|
|
7
|
+
no-border
|
|
8
|
+
class="oxy-tree__search-input"
|
|
9
|
+
prefix-icon="search"
|
|
10
|
+
:placeholder="filterPlaceholder"
|
|
11
|
+
v-model="searchValue"
|
|
12
|
+
@confirm="handleFilter"
|
|
13
|
+
/>
|
|
14
|
+
<oxy-button :round="false" @click="handleFilter">筛选</oxy-button>
|
|
15
|
+
</view>
|
|
16
|
+
</slot>
|
|
17
|
+
|
|
3
18
|
<view v-if="data.length" class="oxy-tree__virtual-scroll" :style="{ height: height }">
|
|
4
19
|
<scroll-view class="oxy-tree__view" scroll-y :scroll-x="true" :scroll-top="scrollTop" @scroll="handleScroll">
|
|
5
20
|
<view class="oxy-tree__container" :style="{ height: totalHeight + 'px' }">
|
|
@@ -82,11 +97,12 @@ const emit = defineEmits<{
|
|
|
82
97
|
|
|
83
98
|
const { getDisabled, getChildren, getLabel, getKey } = useTreeMethods(props)
|
|
84
99
|
const expandedKeySet = ref<Set<string>>(new Set())
|
|
100
|
+
const bakExpandedKeySet = ref<Set<string> | null>(null)
|
|
85
101
|
const hiddenNodeKeySet = ref<Set<string>>(new Set())
|
|
86
102
|
const checkedKeys = ref<Set<string>>(new Set())
|
|
87
103
|
const immediateKeySet = ref<Set<string>>(new Set())
|
|
88
104
|
const currentNode = ref<TreeNode>()
|
|
89
|
-
|
|
105
|
+
const searchValue = ref('')
|
|
90
106
|
const tree = ref<Tree>() as Ref<Tree>
|
|
91
107
|
const flattenTree = computed<TreeNode[]>(() => {
|
|
92
108
|
const expandedKeys = expandedKeySet.value
|
|
@@ -169,7 +185,6 @@ const createTree = (data: RawTreeNode[]) => {
|
|
|
169
185
|
node.checked = checkedKeys.value.has(value)
|
|
170
186
|
}
|
|
171
187
|
if (expandAll.value) {
|
|
172
|
-
node.expanded = true
|
|
173
188
|
expandedKeySet.value.add(value)
|
|
174
189
|
}
|
|
175
190
|
const children = getChildren(rawNode)
|
|
@@ -202,9 +217,8 @@ const createTree = (data: RawTreeNode[]) => {
|
|
|
202
217
|
}
|
|
203
218
|
|
|
204
219
|
const toggleExpand = (node: TreeNode, flag: boolean) => {
|
|
205
|
-
node.expanded = flag
|
|
206
220
|
const key = node.key
|
|
207
|
-
if (
|
|
221
|
+
if (flag) {
|
|
208
222
|
expandedKeySet.value.add(key)
|
|
209
223
|
emit('node-expand', node)
|
|
210
224
|
} else {
|
|
@@ -279,7 +293,6 @@ const updateNode = (node: TreeNode | undefined) => {
|
|
|
279
293
|
const expandNode = (node: TreeNode | undefined) => {
|
|
280
294
|
if (!node) return
|
|
281
295
|
!node.isLeaf && expandedKeySet.value.add(node.key)
|
|
282
|
-
node.expanded = true
|
|
283
296
|
if (node.parent) {
|
|
284
297
|
expandNode(node.parent)
|
|
285
298
|
}
|
|
@@ -287,6 +300,12 @@ const expandNode = (node: TreeNode | undefined) => {
|
|
|
287
300
|
const initValue = () => {
|
|
288
301
|
if (showCheckbox.value) {
|
|
289
302
|
if (isSetsEqual(checkedKeys.value, new Set(modelValue.value as string[]))) return
|
|
303
|
+
Array.from(checkedKeys.value).map((value) => {
|
|
304
|
+
const node = tree.value?.treeNodeMap.get(value as string)
|
|
305
|
+
if (node) {
|
|
306
|
+
toggleChecked(node, false)
|
|
307
|
+
}
|
|
308
|
+
})
|
|
290
309
|
checkedKeys.value = new Set(modelValue.value as string[])
|
|
291
310
|
|
|
292
311
|
Array.isArray(modelValue.value) &&
|
|
@@ -322,7 +341,7 @@ const getNodeStyle = (item: TreeNode) => {
|
|
|
322
341
|
const getNodeClass = (item: TreeNode) => {
|
|
323
342
|
const currentValue = currentNode?.value
|
|
324
343
|
return {
|
|
325
|
-
expanded: item.
|
|
344
|
+
expanded: expandedKeySet.value.has(item.key),
|
|
326
345
|
checked: item.checked,
|
|
327
346
|
'is-leaf': item.isLeaf,
|
|
328
347
|
immediate: item.immediate,
|
|
@@ -335,7 +354,7 @@ const handleNodeClick = (item: TreeNode) => {
|
|
|
335
354
|
handleClick(item)
|
|
336
355
|
}
|
|
337
356
|
const handleClickExpand = (item: TreeNode) => {
|
|
338
|
-
toggleExpand(item, !item.
|
|
357
|
+
toggleExpand(item, !expandedKeySet.value.has(item.key))
|
|
339
358
|
}
|
|
340
359
|
watch(
|
|
341
360
|
() => data.value,
|
|
@@ -387,17 +406,79 @@ const scrollToElementById = (id: string | number) => {
|
|
|
387
406
|
})
|
|
388
407
|
}
|
|
389
408
|
}
|
|
409
|
+
const getCheckedNodes = (): (TreeNode | any)[] => {
|
|
410
|
+
const checkNodes = Array.from(checkedKeys.value).map((key) => {
|
|
411
|
+
return tree.value?.treeNodeMap.get(key as string) || { [props.nodeKey]: key }
|
|
412
|
+
})
|
|
413
|
+
return checkNodes
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const filter = (query: string) => {
|
|
417
|
+
if (bakExpandedKeySet.value === null) {
|
|
418
|
+
bakExpandedKeySet.value = new Set(expandedKeySet.value)
|
|
419
|
+
}
|
|
420
|
+
const keys = doFilter(query)
|
|
421
|
+
if (query && keys) {
|
|
422
|
+
expandedKeySet.value = keys
|
|
423
|
+
} else if (!query) {
|
|
424
|
+
bakExpandedKeySet.value && (expandedKeySet.value = new Set(bakExpandedKeySet.value))
|
|
425
|
+
bakExpandedKeySet.value = null
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const doFilter = (query: string) => {
|
|
430
|
+
if (typeof props.filterMethod !== 'function') {
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
const expandKeySet = new Set<string>()
|
|
434
|
+
const hiddenKeys = hiddenNodeKeySet.value
|
|
435
|
+
const family: TreeNode[] = []
|
|
436
|
+
const nodes = tree.value.treeNodes || []
|
|
437
|
+
const filter = props.filterMethod
|
|
438
|
+
hiddenKeys.clear()
|
|
439
|
+
function traverse(nodes: TreeNode[]) {
|
|
440
|
+
nodes.forEach((node: TreeNode) => {
|
|
441
|
+
family.push(node)
|
|
442
|
+
if (filter(query, node.data)) {
|
|
443
|
+
family.forEach((member) => {
|
|
444
|
+
expandKeySet.add(member.key)
|
|
445
|
+
})
|
|
446
|
+
} else if (node.isLeaf) {
|
|
447
|
+
hiddenKeys.add(node.key)
|
|
448
|
+
}
|
|
449
|
+
const children = node.children
|
|
450
|
+
if (children) {
|
|
451
|
+
traverse(children)
|
|
452
|
+
}
|
|
453
|
+
if (!node.isLeaf) {
|
|
454
|
+
if (!expandKeySet.has(node.key)) {
|
|
455
|
+
hiddenKeys.add(node.key)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
family.pop()
|
|
459
|
+
})
|
|
460
|
+
}
|
|
461
|
+
traverse(nodes)
|
|
462
|
+
return expandKeySet
|
|
463
|
+
}
|
|
464
|
+
const handleFilter = () => {
|
|
465
|
+
filter(searchValue.value)
|
|
466
|
+
}
|
|
467
|
+
|
|
390
468
|
defineExpose<TreeExpose>({
|
|
391
469
|
toggleExpand,
|
|
392
470
|
toggleChecked,
|
|
393
471
|
getNodeById,
|
|
472
|
+
getCheckedNodes,
|
|
473
|
+
getCurrentNode: () => currentNode.value,
|
|
394
474
|
useTree: () => tree.value,
|
|
395
475
|
getTree: () => tree,
|
|
396
476
|
scrollToTop,
|
|
397
477
|
scrollToBottom,
|
|
398
478
|
scrollToPosition,
|
|
399
479
|
scrollToElement,
|
|
400
|
-
scrollToElementById
|
|
480
|
+
scrollToElementById,
|
|
481
|
+
filter
|
|
401
482
|
})
|
|
402
483
|
</script>
|
|
403
484
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentPublicInstance, ExtractPropTypes, Ref } from 'vue'
|
|
2
2
|
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeNumericProp, makeStringProp } from '../common/props'
|
|
3
|
+
import { type PropType } from 'vue'
|
|
3
4
|
|
|
4
5
|
// 树结构类型定义
|
|
5
6
|
export type Tree = {
|
|
@@ -67,12 +68,20 @@ export const treeProps = {
|
|
|
67
68
|
*/
|
|
68
69
|
itemHeight: makeStringProp('44px'),
|
|
69
70
|
indent: makeNumberProp(16),
|
|
70
|
-
selectionLeafOnly: makeBooleanProp(false)
|
|
71
|
+
selectionLeafOnly: makeBooleanProp(false),
|
|
72
|
+
filterMethod: {
|
|
73
|
+
type: Function as PropType<(keyword: string, node: RawTreeNode) => boolean>,
|
|
74
|
+
default: null
|
|
75
|
+
},
|
|
76
|
+
showFilter: makeBooleanProp(false),
|
|
77
|
+
filterPlaceholder: makeStringProp('请输入节点名称')
|
|
71
78
|
}
|
|
72
79
|
export type TreeExpose = {
|
|
73
80
|
toggleExpand: (node: TreeNode, flag: boolean) => void
|
|
74
81
|
toggleChecked: (node: TreeNode, flag: boolean, isClick: boolean) => void
|
|
75
82
|
getNodeById: (id: string | number) => TreeNode | undefined
|
|
83
|
+
getCheckedNodes: () => TreeNode[] | any
|
|
84
|
+
getCurrentNode: () => TreeNode | undefined
|
|
76
85
|
useTree: () => Tree
|
|
77
86
|
getTree: () => Ref<Tree>
|
|
78
87
|
scrollToTop: () => void
|
|
@@ -80,6 +89,7 @@ export type TreeExpose = {
|
|
|
80
89
|
scrollToPosition: (position: number | string) => void
|
|
81
90
|
scrollToElement: (item: any) => void
|
|
82
91
|
scrollToElementById: (id: string | number) => void
|
|
92
|
+
filter: (keyword: string) => void
|
|
83
93
|
}
|
|
84
94
|
export type TreeProps = ExtractPropTypes<typeof treeProps>
|
|
85
95
|
export type TreeInstance = ComponentPublicInstance<TreeProps, TreeExpose>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@import '../common/abstracts/variable';
|
|
2
|
+
@import '../common/abstracts/mixin';
|
|
3
|
+
|
|
4
|
+
@include b(waterfall) {
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
@include e(wrapper) {
|
|
7
|
+
height: 100%;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
}
|
|
10
|
+
@include e(container) {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: row;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
}
|
|
15
|
+
@include e(column) {
|
|
16
|
+
flex: 1;
|
|
17
|
+
}
|
|
18
|
+
}
|