@lambo-design/schema-tree 1.0.0-beta.2

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.
Files changed (3) hide show
  1. package/index.js +2 -0
  2. package/package.json +24 -0
  3. package/src/index.vue +416 -0
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import LamboSchemaTree from './src/index';
2
+ export default LamboSchemaTree
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@lambo-design/schema-tree",
3
+ "version": "1.0.0-beta.2",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "author": "lambo",
7
+ "license": "ISC",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
12
+ "devDependencies": {
13
+ "@lambo-design/shared": "^1.0.0-beta.318",
14
+ "@lambo-design/core": "^4.7.1-beta.171"
15
+ },
16
+ "scripts": {
17
+ "release": "pnpm release-beta && git push --follow-tags && pnpm re-publish",
18
+ "release-major": "standard-version --release-as major",
19
+ "release-minor": "standard-version --release-as minor",
20
+ "release-patch": "standard-version --release-as patch",
21
+ "release-beta": "standard-version --prerelease beta",
22
+ "re-publish": "pnpm publish --access public --no-git-checks"
23
+ }
24
+ }
package/src/index.vue ADDED
@@ -0,0 +1,416 @@
1
+ <template>
2
+ <div>
3
+ <Input style="width: 100%" v-if="isShowSearch" search clearable v-model="searchQuery" :placeholder="placeholder" @on-search="handleSearch" @on-clear="handleSearch" />
4
+ <Tree ref="treeRef"
5
+ v-bind="$attrs"
6
+ :data="treeData"
7
+ :expand-node="expandNode"
8
+ :multiple="showCheckbox"
9
+ :show-checkbox="showCheckbox"
10
+ :render="renderContent"
11
+ @on-check-change="handleCheckChange"
12
+ @on-select-change="handleSelectChange">
13
+ </Tree>
14
+ </div>
15
+ </template>
16
+ <script>
17
+ import ajax from '@lambo-design/shared/utils/ajax'
18
+ import {deepCopy} from '@lambo-design/shared/utils/assist';
19
+
20
+ function deepEach(list, callback) {
21
+ if (Array.isArray(list) && list.length) {
22
+ for (let idx = 0; idx < list.length; idx++) {
23
+ const node = list[idx];
24
+ let breakDeep = false;
25
+ if (typeof callback === 'function') {
26
+ try {
27
+ const callbackResult = callback(node, idx);
28
+ if (typeof callbackResult === 'boolean') {
29
+ breakDeep = callbackResult;
30
+ }
31
+ } catch (error) {
32
+ console.error(error);
33
+ }
34
+ }
35
+ if (!breakDeep) {
36
+ deepEach(node.children, callback);
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ export default {
43
+ name: "schema-tree",
44
+ components: {},
45
+ data() {
46
+ return {
47
+ level: 1,
48
+ searchQuery: '',
49
+ tempData: [],
50
+ treeData: [],
51
+ expandAll: false,
52
+ checkNodes: [],
53
+ }
54
+ },
55
+ props: {
56
+ dataUrl: {
57
+ type: String,
58
+ default: ''
59
+ },
60
+ placeholder: {
61
+ type: String,
62
+ default: '回车键搜索'
63
+ },
64
+ isShowSearch: {
65
+ type: Boolean,
66
+ required: false,
67
+ default: false
68
+ },
69
+ showCheckbox: {
70
+ type: Boolean,
71
+ required: false,
72
+ default: false
73
+ },
74
+ isCustomIcon: {
75
+ type: Boolean,
76
+ required: false,
77
+ default: false
78
+ },
79
+ isCustomStatus: {
80
+ type: Boolean,
81
+ required: false,
82
+ default: false
83
+ },
84
+ isExpand: {
85
+ type: Boolean,
86
+ required: false,
87
+ default: false
88
+ },
89
+ expandNode: {
90
+ type: Boolean,
91
+ required: false,
92
+ default: false
93
+ },
94
+ lazyModel: {
95
+ type: Boolean,
96
+ required: false,
97
+ default: false
98
+ },
99
+ expandLevel: {
100
+ type: Number,
101
+ required: false,
102
+ default: 0
103
+ },
104
+ icon: {
105
+ type: String,
106
+ required: false,
107
+ default: ''
108
+ },
109
+ iconList: {
110
+ type: Array,
111
+ required: false,
112
+ default(){
113
+ return []
114
+ }
115
+ },
116
+ statusList: {
117
+ type: Array,
118
+ required: false,
119
+ default(){
120
+ return []
121
+ }
122
+ },
123
+ rootId: {
124
+ type: String,
125
+ default: 'root'
126
+ },
127
+ queryParams: {
128
+ type: Object,
129
+ required: false,
130
+ default: () => ({})
131
+ },
132
+ },
133
+ computed: {},
134
+ watch: {
135
+ queryParams: function () {
136
+ this.getRootData();
137
+ },
138
+ },
139
+ created() {
140
+ this.init();
141
+ },
142
+ methods: {
143
+ init() {
144
+ if (this.isExpand) {
145
+ if (this.expandLevel === 0) {
146
+ this.expandAll = true
147
+ } else if (this.expandLevel > 0){
148
+ this.expandAll = false
149
+ }
150
+ }
151
+ this.getRootData()
152
+ },
153
+ // 点击复选框时触发
154
+ handleCheckChange(nodes, curNode) {
155
+ if (!this.showCheckbox) {
156
+ this.setCheckStatus([curNode.id])
157
+ } else {
158
+ deepEach(nodes, (node) => {
159
+ if (node.selected || node.checked) {
160
+ this.checkNodes.push(node.id)
161
+ }
162
+ })
163
+ if (!curNode.checked) {
164
+ this.checkNodes = this.checkNodes.filter(item => item !== curNode.id)
165
+ }
166
+ this.setCheckStatus(this.checkNodes)
167
+ }
168
+ this.$emit('on-check-change', nodes, curNode)
169
+ },
170
+ setCheckStatus(selectNodes) {
171
+ deepEach(this.treeData, (node) => {
172
+ if (selectNodes.includes(node.id)) {
173
+ node.checked = true
174
+ } else if (this.treeData.length==1) {
175
+ this.treeData[0].checked = false
176
+ node.checked = false
177
+ }
178
+ if (node.checked && node.children) {
179
+ // 如果子节点匹配到数据,标记父节点
180
+ deepEach(node.children, (child) => {
181
+ if (!selectNodes.includes(child.id)) {
182
+ node.checked = false
183
+ }
184
+ })
185
+ }
186
+ })
187
+ deepEach(this.tempData, (node) => {
188
+ if (selectNodes.includes(node.id)) {
189
+ node.checked = true
190
+ } else if (this.tempData.length==1) {
191
+ this.tempData[0].checked = false
192
+ node.checked = false
193
+ }
194
+ if (node.checked && node.children) {
195
+ // 如果子节点匹配到数据,标记父节点
196
+ deepEach(node.children, (child) => {
197
+ if (!selectNodes.includes(child.id)) {
198
+ node.checked = false
199
+ }
200
+ })
201
+ }
202
+ })
203
+ },
204
+ getRootData(name) {
205
+ let url = name ? this.getUrl(this.rootId, name) : this.getUrl(this.rootId)
206
+ let self = this
207
+ ajax.get(url).then(function (resp) {
208
+ if (resp.data && resp.data.code === 1) {
209
+ self.processData(resp.data.data);
210
+ self.treeData = resp.data.data
211
+
212
+ if (!self.lazyModel) {
213
+ self.tempData = deepCopy(self.treeData)
214
+ self.handleSearch()
215
+ } else if (self.treeData.length > 0 && self.treeData[0].children.length == 0) {
216
+ self.getNode(self.treeData)
217
+ }
218
+ } else {
219
+ self.$Message.error('获取数据失败');
220
+ }
221
+ }).catch((err) => {
222
+ self.$Message.error('获取数据失败,请联系管理员');
223
+ })
224
+ },
225
+ getNode(node) {
226
+ if (this.isExpand && this.level <= this.expandLevel) {
227
+ this.level++
228
+ let self = this
229
+ ajax.get(this.getUrl(node[0].id)).then(function (resp) {
230
+ if (resp.data && resp.data.code === 1) {
231
+ self.processData(resp.data.data);
232
+ node[0].children = resp.data.data
233
+ self.getNode(node[0].children)
234
+
235
+ } else {
236
+ self.$Message.error('获取数据失败');
237
+ }
238
+ }).catch((err) => {
239
+ self.$Message.error('获取数据失败,请联系管理员');
240
+ })
241
+ }
242
+ },
243
+ updateNode(node) {
244
+ let self = this
245
+ ajax.get(this.getUrl(node[0].id)).then(function (resp) {
246
+ if (resp.data && resp.data.code === 1) {
247
+ self.processData(resp.data.data);
248
+ node[0].children = resp.data.data
249
+ self.getNode(node[0].children)
250
+ } else {
251
+ self.$Message.error('获取数据失败');
252
+ }
253
+ }).catch((err) => {
254
+ self.$Message.error('获取数据失败,请联系管理员');
255
+ })
256
+ },
257
+ processData(data) {
258
+ data.forEach(item => {
259
+ item.iconType = item.iconType ? item.iconType : 1;
260
+ item.expand = this.isExpand ? this.expandAll ? true : this.expandLevel >= this.level++ : false;
261
+ item.children = item.children ? item.children : [];
262
+ if (item.children.length > 0) {
263
+ this.processData(item.children);
264
+ this.lazyModel = false
265
+ }
266
+ });
267
+ },
268
+ renderContent(h, {root, node, data}) {
269
+ // 根据 icontype 过滤 iconList 并获取 icon
270
+ let iconRender = null
271
+ if (this.isCustomIcon && data.icon) {
272
+ const iconItem = this.iconList.find(item => item.id == data.icon);
273
+ if (iconItem && iconItem.type) {
274
+ iconRender = h('Icon', {
275
+ props: {
276
+ type: iconItem.type,
277
+ size: '16'
278
+ },
279
+ style: {
280
+ marginRight: '8px'
281
+ }
282
+ })
283
+ }
284
+ } else if (this.icon) {
285
+ iconRender = h('Icon', {
286
+ props: {
287
+ type: this.icon,
288
+ size: '16'
289
+ },
290
+ style: {
291
+ marginRight: '8px'
292
+ }
293
+ })
294
+ }
295
+ let statusRender = null
296
+ if (this.isCustomStatus && data.status) {
297
+ const statusItem = this.statusList.find(item => item.id == data.status);
298
+ if (statusItem) {
299
+ statusRender = h('span', {
300
+ style: {
301
+ color: statusItem.color,
302
+ },
303
+ }, "[" + statusItem.name + "]")
304
+ }
305
+ }
306
+ return h('span', {
307
+ style: {
308
+ width: '100%',
309
+ }
310
+ }, [
311
+ iconRender,
312
+ h('span', data.title),
313
+ statusRender
314
+ ]);
315
+ },
316
+ handleSelectChange(data) {
317
+ this.$emit('on-select-change', data)
318
+ if (data.length > 0 && data[0].children.length == 0 && this.tempData.length == 0) {
319
+ this.updateNode(data)
320
+ }
321
+ },
322
+ getUrl(paramValue, name) {
323
+ const hasQuery = this.dataUrl.includes('?');
324
+ const separator = hasQuery ? '&' : '?';
325
+ const params = this.queryParams
326
+ const queryString = new URLSearchParams(params).toString();
327
+ if (name) {
328
+ return `${this.dataUrl}${separator}${encodeURIComponent('id')}=${encodeURIComponent(paramValue)}&${encodeURIComponent('name')}=${encodeURIComponent(name)}&${queryString}`;
329
+ }
330
+ return `${this.dataUrl}${separator}${encodeURIComponent('id')}=${encodeURIComponent(paramValue)}&${queryString}`;
331
+ },
332
+ handleSearch() {
333
+ if (this.searchQuery.trim() === '') {
334
+ // 清空搜索结果
335
+ if (!this.lazyModel) {
336
+ this.treeData = deepCopy(this.tempData); // 恢复原始数据
337
+ } else {
338
+ this.getRootData()
339
+ }
340
+ return;
341
+ }
342
+ if (!this.lazyModel) {
343
+ this.expandNodesByQuery(this.searchQuery);
344
+ let temp = deepCopy(this.tempData)
345
+ this.removeUnexpandedNodes(temp)
346
+ this.treeData = temp
347
+ } else {
348
+ this.getRootData(this.searchQuery)
349
+ }
350
+ this.$emit('handleSearch', this.searchQuery)
351
+
352
+ },
353
+ resetExpand() {
354
+ // 重置所有节点的 expand 状态
355
+ if (!this.lazyModel) {
356
+ this.tempData.forEach(node => this.resetNodeExpand(node));
357
+ } else {
358
+ this.treeData.forEach(node => this.resetNodeExpand(node));
359
+
360
+ }
361
+ },
362
+ resetNodeExpand(node) {
363
+ node.expand = false;
364
+ if (node.children) {
365
+ node.children.forEach(child => this.resetNodeExpand(child));
366
+ }
367
+ },
368
+ expandNodesByQuery(query) {
369
+ this.resetExpand();
370
+ if (!this.lazyModel) {
371
+ this.tempData.forEach(node => this.expandNodeIfMatch(node, query));
372
+ } else {
373
+ this.treeData.forEach(node => this.expandNodeIfMatch(node, query));
374
+ }
375
+ },
376
+ expandNodeIfMatch(node, query) {
377
+ if (node.children) {
378
+ node.children.forEach(child => {
379
+ if (this.expandNodeIfMatch(child, query)) {
380
+ node.expand = true;
381
+ }
382
+ });
383
+ }
384
+ if (this.matchNode(node, query)) {
385
+ node.expand = true;
386
+ return true;
387
+ }
388
+ return node.expand
389
+ },
390
+ matchNode(node, query) {
391
+ // 根据需要调整匹配逻辑
392
+ const queryLower = query.toLowerCase();
393
+ return node.title.toLowerCase().includes(queryLower) || node.id.toLowerCase().includes(queryLower);
394
+ },
395
+ removeUnexpandedNodes(nodes) {
396
+ for (let i = nodes.length - 1; i >= 0; i--) {
397
+ const node = nodes[i];
398
+ if (!node.expand) {
399
+ // 如果节点 expand 为 false,则删除该节点
400
+ nodes.splice(i, 1);
401
+ } else {
402
+ // 如果节点 expand 为 true,则递归处理其子节点
403
+ if (node.children && node.children.length > 0) {
404
+ this.removeUnexpandedNodes(node.children);
405
+ }
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ </script>
412
+
413
+
414
+ <style lang="less" scoped>
415
+ </style>
416
+