开发过程中对于http请求,json数据是必不可少的处理,无论是前端向后端传参还是后端返回参数,现有技术的流行都是json数据的格式输出,那么对于复杂json数据的直观展示无疑可以提升我们开发及解决问题的效率,话不多说,我们来实现一个通过递归方式实现的一个json数据解析的小工具
首先,我们需要一个父组件:FormatJson.vue
<template>
<div class="monitor-analysis-page">
<div class="monitor-data-input">
<el-input type="textarea" v-model="logData" :autosize="{ minRows: 6, maxRows: 20 }"
placeholder=" Please Input Log Data" @input="dataBlur"></el-input>
<span v-if="isWrong" class="data-tips">数据格式错误</span>
</div>
<div class="data-table">
<ParamsTable @updateShowStatus="updateShowStatus" :tableData="tableDataArray" :key="setKey" />
</div>
</div>
</template>
<script>
import ParamsTable from '../../components/paramsTable/index.vue'
export default {
components: {
ParamsTable
},
data () {
return {
setKey: 0, // 用于更新视图的key
isWrong: false,
logData: '', // json原始数据
tableDataArray: [] // 用于展示在表格的数据
}
},
methods: {
dataBlur (val) {
if (!val) {
this.tableDataArray = []
this.isWrong = false
}
try { // 判断数据类型
const jsonStr = JSON.parse(this.logData)
console.log(jsonStr)
this.tableDataArray = this.formatDataToArr(jsonStr, 1)
this.setKey++
this.isWrong = false
} catch (error) {
console.log(error)
this.isWrong = true
}
},
/**
* 递归方法,将json数据解析为可展示在表格的数据结构
*/
formatDataToArr (obj = {}, level = 1) {
console.log('monitorData', obj)
let tmpArr = []
if (this.$proxy._typeof(obj) === 'array') {
obj.forEach((o, i) => {
let indexObj = {}
indexObj.prop = i
indexObj.type = this.$proxy._typeof(o)
if (indexObj.type === 'object') {
indexObj.value = 'Object'
indexObj.level = level
indexObj.isShow = false
tmpArr.push(indexObj)
indexObj.children = this.formatDataToArr(o, level + 1)
} else {
indexObj.value = o
indexObj.level = level
tmpArr.push(indexObj)
}
})
} else {
Object.keys(obj).map(k => {
let tmpObj = {}
tmpObj.prop = k
tmpObj.type = this.$proxy._typeof(obj[k])
tmpObj.value = obj[k]
tmpObj.level = level
console.log('tmpObj.type', tmpObj.type)
if (tmpObj.type === 'array' && tmpObj.value.length && this.$proxy._typeof(tmpObj.value[0]) === 'object') {
tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
tmpObj.value = 'Array'
tmpObj.isShow = false
} else if (tmpObj.type === 'object') {
tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
tmpObj.value = 'Object'
tmpObj.isShow = false
} else if (tmpObj.type === 'array' && tmpObj.value.length && this.$proxy._typeof(tmpObj.value[0]) === 'string') {
tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
tmpObj.value = 'Array'
tmpObj.isShow = false
}
tmpArr.push(tmpObj)
})
}
return tmpArr
},
updateShowStatus (o, i, isShow) {
this.setKey++
o.isShow = isShow
console.log('tableDataArray', this.tableDataArray)
}
}
}
</script>
<style lang="scss" scoped>
@media (min-width: 1024px) {
.monitor-analysis-page {
width: 1024px;
}
}
.monitor-analysis-page {
margin: 30px auto;
.data-tips {
color: crimson;
}
.data-table {
margin-top: 40px;
}
}
</style>
还需要一个表格父组件:ParamsTable.vue
<template>
<div class="tools-table-container">
<div class="table-header-container">
<div class="header-col">属性</div>
<div class="header-col">值</div>
<div class="header-col">类型</div>
</div>
<div class="table-body-container">
<div class="table-line-container" v-for="(o, i) in tableData" :key="i">
<div class="line-type-include" :style="{'backgroundColor': colorList[o.level - 1]}" v-if="o.type === 'array' || o.type === 'object'">
<div class="line-col">{{o.prop}}</div>
<div class="line-col">{{o.value}} <el-button type="text" size="small" class="expand-fold" @click="changeStatus(o, i)">{{o.isShow ? '折叠' : '展开'}}</el-button></div>
<div class="line-col">{{o.type}}</div>
</div>
<div class="line-type-include" :style="{'backgroundColor': colorList[o.level - 1]}" v-else>
<div class="line-col">{{o.prop}}</div>
<div class="line-col">{{o.value}}</div>
<div class="line-col">{{o.type}}</div>
</div>
<tableBody @updateShowStatus="sendToParentShowStatus" :tableData="o.children" v-if="o.children && o.children.length && o.isShow === true"></tableBody>
</div>
</div>
</div>
</template>
<script>
import tableBody from '@/components/paramsTable/tableBody.vue'
export default {
components: {
tableBody
},
props: {
tableData: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
colorList: ['#FFF3E6', '#FFE8D9', '#FFDDD0', '#FFD2C2', '#FFC7B5', '#FFBCA8'] // 用于表格背景色以区分数据的不同层级
}
},
methods: {
changeStatus (o, i) { // 通知父组件更新数据及视图
console.log(o)
this.$emit('updateShowStatus', o, i, !o.isShow)
},
sendToParentShowStatus (o, i, isShow) {
this.$emit('updateShowStatus', o, i, isShow)
}
}
}
</script>
<style lang="scss" scoped>
.tools-table-container {
width: 1024px;
border: 1px solid #d3d7d4;
.table-header-container {
width: 100%;
height: 40px;
display: grid;
grid-template-columns: repeat(3, 1fr);
justify-content: space-between;
box-sizing: border-box;
background-color: #B0E0E6;
.header-col {
// width: 50%;
text-align: center;
line-height: 40px;
}
.header-col {
border-right: 1px solid #d3d7d4;
}
.header-col:last-child {
border-right: none;
}
}
.table-body-container {
.table-line-container {
.line-type-include {
display: grid;
grid-template-columns: repeat(3, 1fr);
min-height: 30px;
border-bottom: 1px solid #CCCCCC;
.line-col {
// overflow: auto;
flex-grow: 1;
// box-sizing: border-box;
word-break: break-all;
text-align: center;
line-height: 25px;
border-right: 1px solid #CCCCCC;
color: #000000;
position: relative;
.expand-fold {
position: absolute;
right: 20px;
top: 5px;
}
}
}
}
}
}
</style>
一个表格子组件:TableBody.vue,这里注意不要省略组件的name属性,因为它引用了自己
<template>
<div>
<div class="table-line-container" v-for="(o, i) in tableData" :key="i">
<div class="line-type-include" :style="{ 'backgroundColor': colorList[o.level - 1] }"
v-if="o.type === 'array' || o.type === 'object'">
<div class="line-col">{{ o.prop }}</div>
<div class="line-col">{{ o.value }} <el-button class="expand-fold" size="small" type="text"
@click="changeShowStatus(o, i)">{{ o.isShow ? '折叠' : '展开' }}</el-button></div>
<div class="line-col">{{ o.type }}</div>
</div>
<div class="line-type-include" :style="{ 'backgroundColor': colorList[o.level - 1] }" v-else>
<div class="line-col">{{ o.prop }}</div>
<div class="line-col">{{ o.value }}</div>
<div class="line-col">{{ o.type }}</div>
</div>
<tableBody @updateShowStatus="sendToParentShowStatus" :tableData="o.children"
v-if="o.children && o.children.length && o.isShow === true"></tableBody>
</div>
</div>
</template>
<script>
export default {
name: 'tableBody',
props: {
tableData: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
colorList: ['#FFF3E6', '#FFE8D9', '#FFDDD0', '#FFD2C2', '#FFC7B5', '#FFBCA8']
}
},
methods: {
sendToParentShowStatus (o, i) {
this.$emit('updateShowStatus', o, i, !o.isShow)
},
changeShowStatus (o, i) {
console.log(o)
this.$emit('updateShowStatus', o, i, !o.isShow)
}
}
}
</script>
<style lang="scss" scoped>
.table-line-container {
.line-type-include {
display: grid;
grid-template-columns: repeat(3, 1fr);
min-height: 30px;
border-bottom: 1px solid #CCCCCC;
.line-col {
// overflow: auto;
flex-grow: 1;
// box-sizing: border-box;
word-break: break-all;
text-align: center;
line-height: 25px;
border-right: 1px solid #CCCCCC;
color: #000000;
position: relative;
.expand-fold {
position: absolute;
right: 20px;
top: 5px;
}
}
}
}
</style>
至此结束,点击 查看效果!