Skip to content

Commit 9b2fae8

Browse files
committed
Fixed showLength attribute performance error, and add radio select mode(single select).
1 parent 45b2b69 commit 9b2fae8

File tree

9 files changed

+151
-94
lines changed

9 files changed

+151
-94
lines changed

example/App.vue

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
<h3>Options</h3>
2727
<div class="options">
2828
<div>
29-
<label>selectable-type</label>
29+
<label>selectableType</label>
3030
<select v-model="selectableType">
3131
<option>-</option>
3232
<option>checkbox</option>
33+
<option>radio</option>
3334
</select>
3435
</div>
3536
<div>
@@ -64,17 +65,19 @@
6465
</div>
6566
<div class="block">
6667
<h3>JSON Tree:</h3>
68+
{{value}}
6769
<vue-json-pretty
70+
v-if="renderOK"
6871
:data="json"
6972
:path="path"
7073
:deep="deep"
7174
:show-double-quotes="showDoubleQuotes"
7275
:show-mouse-over="showMouseOver"
7376
:show-length="showLength"
74-
v-model="pathSelected"
77+
v-model="value"
7578
:path-selectable="((path, data) => typeof data !== 'number')"
7679
:selectable-type="selectableType"
77-
@click="handleClick"
80+
@click="handleClick(...arguments, 'complexTree')"
7881
@change="handleChange">
7982
</vue-json-pretty>
8083
</div>
@@ -93,6 +96,7 @@ export default {
9396
},
9497
data () {
9598
return {
99+
renderOK: true,
96100
val: '',
97101
data: {
98102
status: 200,
@@ -109,11 +113,11 @@ export default {
109113
news_id: 51182,
110114
title: 'Teslamask\'s American Business Relations: The government does not pay billions to build factories',
111115
source: 'AI Finance',
112-
members: ['Daniel, Mike, John']
116+
members: ['Daniel', 'Mike', 'John']
113117
}]
114118
},
115-
pathSelected: ['res', 'res.error'],
116-
selectableType: 'checkbox',
119+
value: 'res.error',
120+
selectableType: 'radio',
117121
showLength: true,
118122
showDoubleQuotes: true,
119123
showMouseOver: true,
@@ -126,6 +130,20 @@ export default {
126130
created () {
127131
this.val = JSON.stringify(this.data)
128132
},
133+
watch: {
134+
selectableType (newVal) {
135+
this.renderOK = false
136+
if (newVal === 'radio') {
137+
this.value = 'res.error'
138+
} else if (newVal === 'checkbox') {
139+
this.value = ['res', 'res.error']
140+
}
141+
// 重新渲染, 因为2中情况的v-model格式不同
142+
this.$nextTick(() => {
143+
this.renderOK = true
144+
})
145+
}
146+
},
129147
computed: {
130148
json () {
131149
try {
@@ -137,8 +155,8 @@ export default {
137155
}
138156
},
139157
methods: {
140-
handleClick (path, data) {
141-
console.log('click: ', path, data)
158+
handleClick (path, data, treeName = '') {
159+
console.log('click: ', path, data, treeName)
142160
this.itemPath = path
143161
this.itemData = !data ? data + '' : data // 处理 data = null 的情况
144162
},

src/assets/less/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
@import "./checkbox.less";
2+
@import "./radio.less";
23
@import "./tree.less";

src/components/app.vue

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
<template>
22
<div
3-
class="vjs__tree"
4-
:style="{
5-
'background-color': treeContentBackground,
6-
'position': currentDeep > 1 ? '' : 'relative',
7-
'margin-left': currentDeep === 1 && existCheckbox ? '30px' : ''
3+
:class="{
4+
'vjs-tree': true,
5+
'has-selectable-control': existCheckbox || existRadio,
6+
'is-root': currentDeep === 1,
7+
'is-selectable': selectable,
8+
'is-mouseover': isMouseover
89
}"
910
@click.stop="handleClick($event)"
1011
@mouseover.stop="handleMouseover"
1112
@mouseout.stop="handleMouseout">
12-
<template v-if="selectable && existCheckbox" class="vjs-checkbox">
13-
<checkbox v-model="checkboxVal" @change="handleClick($event, true)"></checkbox>
13+
<template v-if="selectable">
14+
<vue-checkbox v-if="existCheckbox" v-model="currentCheckboxVal" @change="handleClick($event, 'checkbox')"></vue-checkbox>
15+
<vue-radio v-else-if="existRadio" v-model="model" @change="handleClick($event, 'radio')" :path="path"></vue-radio>
1416
</template>
1517

1618
<template v-if="Array.isArray(data) || isObject(data)">
@@ -19,18 +21,18 @@
1921
:visible.sync="visible"
2022
:data="data"
2123
:show-length="showLength"
22-
:not-last-key="notLastKey">
24+
:show-comma="notLastKey">
2325
<span v-if="currentDeep > 1 && !Array.isArray(parentData)">{{ keyFormatter(currentKey) }}:</span>
2426
</brackets-left>
2527

2628
<!-- 数据内容, data 为对象时, key 表示键名, 为数组时表示索引 -->
2729
<div
2830
v-for="(item, key) in data"
2931
v-show="visible"
30-
class="vjs__tree__content"
32+
class="vjs-tree__content"
3133
:key="key">
3234
<vue-json-pretty
33-
v-model="value"
35+
v-model="model"
3436
:parent-data="data"
3537
:data="item"
3638
:deep="deep"
@@ -51,38 +53,41 @@
5153
<brackets-right
5254
:visible.sync="visible"
5355
:data="data"
54-
:not-last-key="notLastKey">
56+
:show-comma="notLastKey">
5557
</brackets-right>
5658
</template>
5759

5860
<simple-text
5961
v-else
6062
:show-double-quotes="showDoubleQuotes"
61-
:parent-data-type="getDataType(parentData)"
62-
:data-type="getDataType(data)"
63-
:text="data + ''"
64-
:not-last-key="notLastKey"
65-
:currentKey="currentKey">
63+
:show-comma="notLastKey"
64+
:parent-data="parentData"
65+
:data="data"
66+
:current-key="currentKey">
67+
<span v-if="!Array.isArray(parentData)">{{ keyFormatter(currentKey) }}:</span>
6668
</simple-text>
6769
</div>
6870
</template>
6971

7072
<script>
7173
import SimpleText from './simple-text'
72-
import Checkbox from './checkbox'
74+
import VueCheckbox from './checkbox'
75+
import VueRadio from './radio'
7376
import BracketsLeft from './brackets-left'
7477
import BracketsRight from './brackets-right'
78+
import { getDataType } from 'src/utils'
7579
7680
export default {
7781
name: 'vue-json-pretty',
7882
components: {
7983
SimpleText,
80-
Checkbox,
84+
VueCheckbox,
85+
VueRadio,
8186
BracketsLeft,
8287
BracketsRight
8388
},
8489
props: {
85-
/* 外部可用 START */
90+
/* outer props */
8691
// 当前树的数据
8792
data: {},
8893
// 定义树的深度, 大于该深度的子树将不被展开
@@ -115,19 +120,20 @@
115120
type: String,
116121
default: '' // radio, checkbox
117122
},
118-
// 存在选择功能时,定义已选中的数据层级
123+
// 存在选择功能时, 定义已选中的数据层级
124+
// 多选时为数组['root.a', 'root.b'], 单选时为字符串'root.a'
119125
value: {
120-
type: Array,
121-
default: () => []
126+
type: [Array, String],
127+
default: () => ''
122128
},
123129
// 定义某个数据层级是否支持选中操作
124130
pathSelectable: {
125131
type: Function,
126132
default: () => true
127133
},
128-
/* 外部可用 END */
134+
/* outer props */
129135
130-
/* 内部使用 */
136+
/* inner props */
131137
// 当前树的父级数据
132138
parentData: {},
133139
// 当前树的深度, 以根节点作为0开始, 所以第一层树的深度为1, 递归逐次递增
@@ -137,18 +143,26 @@
137143
},
138144
// 当前树的数据 data 为数组时 currentKey 表示索引, 为对象时表示键名
139145
currentKey: [Number, String]
146+
/* outer props */
140147
},
141148
data () {
142149
return {
143150
visible: this.currentDeep <= this.deep,
144-
treeContentBackground: 'transparent',
145-
checkboxVal: this.value.includes(this.path) // 复选框的值
151+
isMouseover: false,
152+
currentCheckboxVal: Array.isArray(this.value) ? this.value.includes(this.path) : false
146153
}
147154
},
148155
computed: {
149-
model () {
150-
return this.value || []
156+
model: {
157+
get () {
158+
const defaultVal = this.selectableType === 'checkbox' ? [] : this.selectableType === 'radio' ? '' : null
159+
return this.value || defaultVal
160+
},
161+
set (val) {
162+
this.$emit('input', val)
163+
}
151164
},
165+
152166
// 获取当前 data 中最后一项的 key 或 索引, 便于界面判断是否添加 ","
153167
lastKey () {
154168
if (Array.isArray(this.parentData)) {
@@ -158,63 +172,75 @@
158172
return arr[arr.length - 1]
159173
}
160174
},
175+
161176
// 是否不是最后一项
162177
notLastKey () {
163178
return this.currentKey !== this.lastKey
164179
},
180+
165181
// 当前的树是否支持选中功能
166182
selectable () {
167183
return this.pathSelectable(this.path, this.data)
168184
},
185+
169186
// 存在复选框
170187
existCheckbox () {
171188
return this.selectableType === 'checkbox'
189+
},
190+
191+
existRadio () {
192+
return this.selectableType === 'radio'
172193
}
173194
},
174195
methods: {
175196
/**
176-
* 触发组件的 click 事件
177-
* @param {Boolean} fromSelect 是否来自复选框的事件
197+
* emit click event
198+
* @param {Boolean} emitType
178199
*/
179-
handleClick (e, fromSelect = false) {
180-
// 由于 checkbox 也依赖该函数, 因此通过 fromSelect 进行排除
200+
handleClick (e, emitType = '') {
181201
this.$emit('click', this.path, this.data)
182-
if (fromSelect) {
202+
if (emitType === 'checkbox') {
183203
const index = this.model.findIndex(item => item === this.path)
184204
if (index !== -1) {
185205
this.model.splice(index, 1)
186206
} else {
187207
this.model.push(this.path)
188208
}
189-
this.$emit('input', this.model)
190-
this.$emit('change', this.checkboxVal)
209+
this.$emit('change', this.currentCheckboxVal)
210+
} else if (emitType === 'radio') {
211+
if (this.model !== this.path) {
212+
this.model = this.path
213+
this.$emit('change', this.model)
214+
}
191215
}
192216
},
193-
// 处理子树触发的 click 事件, 并传递到顶层
194-
handleItemClick (path, data, checked) {
195-
this.$emit('click', path, data, checked)
217+
218+
// handle children's click, and propagation
219+
handleItemClick (path, data) {
220+
this.$emit('click', path, data)
196221
},
222+
223+
// handle children's change, and propagation
197224
handleItemChange (val) {
198225
// 不存在选择的时候change事件无意义
199226
if (this.existCheckbox) {
200227
this.$emit('change', val)
201228
}
202229
},
230+
203231
handleMouseover () {
204-
this.showMouseOver && this.selectable && (this.treeContentBackground = '#eee')
232+
this.showMouseOver && this.selectable && (this.isMouseover = true)
205233
},
234+
206235
handleMouseout () {
207-
this.showMouseOver && this.selectable && (this.treeContentBackground = 'transparent')
236+
this.showMouseOver && this.selectable && (this.isMouseover = false)
208237
},
238+
209239
// 是否对象
210240
isObject (value) {
211-
return this.getDataType(value) === 'object'
212-
},
213-
// 获取数据类型
214-
getDataType (value) {
215-
// 若使用 typeof 会影响 webpack 压缩后体积变大
216-
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
241+
return getDataType(value) === 'object'
217242
},
243+
218244
keyFormatter (key) {
219245
return this.showDoubleQuotes ? `"${key}"` : key
220246
}

src/components/brackets-left.vue

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
<template>
22
<div>
33
<slot></slot>
4+
5+
<!-- Expand -->
46
<span
57
v-show="dataVisiable"
6-
class="vjs__tree__node"
8+
class="vjs-tree__brackets"
79
@click.stop="toggleBrackets">
810
{{ Array.isArray(data) ? '[' : '{' }}
911
</span>
12+
13+
<!-- Collapse -->
1014
<span
1115
v-show="!dataVisiable"
12-
class="vjs__tree__node"
16+
class="vjs-tree__brackets"
1317
@click.stop="toggleBrackets">
1418
{{ doubleBracketsGenerator(data) }}
1519
</span>
@@ -28,15 +32,16 @@
2832
// 双括号内容生成器
2933
doubleBracketsGenerator (data) {
3034
const isArray = Array.isArray(data)
31-
let brackets = isArray ? '[...]' : '{...}'
35+
const brackets = isArray ? '[...]' : '{...}'
36+
let str = this.bracketsFormatter(brackets)
3237
if (this.showLength) {
3338
// 若展示长度, 形如 [...] // 3 items
3439
const text = isArray
3540
? `${data.length} items`
3641
: `${Object.keys(data).length} keys`
37-
brackets += ` // ${text}`
42+
str += ` // ${text}`
3843
}
39-
return this.bracketsFormatter(brackets)
44+
return str
4045
}
4146
}
4247
}

src/components/brackets-right.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div v-show="dataVisiable">
33
<span
4-
class="vjs__tree__node"
4+
class="vjs-tree__brackets"
55
@click.stop="toggleBrackets">
66
{{ bracketsFormatter(Array.isArray(data) ? ']' : '}') }}
77
</span>

0 commit comments

Comments
 (0)