背景
在最近的一个项目中,有个需求是实现json编辑和提交。在我的实现中,使用了在textarea编辑,然后在无序列表中显示json格式化的内容。textarea内编辑json内容会使用“tab”键加入缩进、“shift+tab”减少缩进和格式化json内容的功能,下面主要的内容就是关于这个三个功能。
在线示例
html
<!-- 编辑区域 -->
<textarea
rows="10"
cols="30"
v-on:keydown="tabHandle"
v-model="content"></textarea>
<!--格式化后的显示区域-->
<ol
style="height: 60vh;border: 1px solid #ccc;padding-left: 50px;overflow-y: auto"
>
<li
style="list-style: decimal;background: #f8f8f8;"
v-for="(j, index) in formatJsonList"
:key="index"
>
<pre style="margin:0">{{ j }}</pre>
</li>
</ol>
核心代码
new Vue({
el: '#app',
data: {
content: '', // 存放编辑的内容
formatJsonList: [] // 显示json格式化后的内容数组
},
watch: {
// 监听编辑的内容发生改变,这里没有验证json格式是否正确
content: {
handler: function(newValue) {
// 如果值不为空
if (newValue.length !== 0) {
this.$set(this, 'formatJsonList', newValue.split('\n'))
return
}
this.$set(this, 'formatJsonList', [])
},
deep: true
},
},
methods: {
// 监听按tab键
tabHandle(e) {
if (e.keyCode === 9 || e.which === 9) {
// shift按键也按下了,就是减少缩进
if (e.shiftKey) {
this.shiftTabHandle(e.target)
e.returnValue = false
return
}
// 只是按了tab,直接插入空格就可以了
this.insertText(e.target, ' ')
e.returnValue = false
}
},
// 插入空格
insertText(el, str) {
if (document.selection) {
let sel = document.selection.createRange()
sel.text = str
} else if (typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number') {
// 选中的开始位置
let startPos = el.selectionStart
// 选中的结束位置
let endPos = el.selectionEnd
// 鼠标位置
let cursorPos = startPos
let tmpStr = el.value
// 如果当前文本域内容未被选中,加入空格后直接把光标放到后面即可
if (el.selectionStart === el.selectionEnd) {
el.value = tmpStr.substring(0, startPos) + str + tmpStr.substring(startPos)
cursorPos += str.length
el.selectionStart = el.selectionEnd = cursorPos
this.moveCursor(el, startPos + str.length)
return
}
// 如果文本域内容被选中了,需要在选中的内容里加入空格
let selectionStr = tmpStr.substring(startPos, endPos)
let newStr = ''
// 把选中的内容添加对应的空格填充
selectionStr.split('\n').forEach((e, index) => {
newStr += index === 0 ? ' ' + e : '\n ' + e
})
// 改变为新值
el.value = tmpStr.substring(0, startPos) + newStr + tmpStr.substring(endPos)
// 选中当前编辑的内容
this.selectionContent(el, startPos, startPos + newStr.length)
return
} else {
el.value += str
}
this.moveCursor(el, el.value.length)
},
// shift和tab同时按下
shiftTabHandle(el) {
if (document.selection) {
let sel = document.selection.createRange()
sel.text = sel.text.replace(/(\s{0,2}$)/g, '')
} else if (typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number') {
let startPos = el.selectionStart
let endPos = el.selectionEnd
let cursorPos = startPos
let tmpStr = el.value
// 如果没有选中内容,直接把选中焦点前的内容进行处理,去掉尾空格
if (el.selectionStart === el.selectionEnd) {
let reverseStr = tmpStr.substring(0, startPos).replace(/(\s{0,2}$)/g, '')
cursorPos = reverseStr.length
el.value = reverseStr + tmpStr.substring(startPos, tmpStr.length)
el.selectionStart = el.selectionEnd = cursorPos
this.moveCursor(el, cursorPos)
return
}
// 获取到选中的内容
let selectionStr = tmpStr.substring(startPos, endPos)
let newStr = ''
// 把选中的内容切成数组,并去除数组内的前两个空格
const selectionStrArray = selectionStr.split('\n')
selectionStrArray.forEach((e, index) => {
newStr += index !== 0 ? e.replace(/(^\s{0,2})/, '\n') : e.replace(/(^\s{0,2})/, '')
})
// 改变为新值
el.value = tmpStr.substring(0, startPos) + newStr + tmpStr.substring(endPos)
// 选中当前编辑的内容
this.selectionContent(el, startPos, startPos + newStr.length)
return
} else {
el.value = el.value.replace(/(\s{0,2}$)/g, '')
}
this.moveCursor(el, el.value.length)
},
// 移动光标
moveCursor(el, toPos) {
var len = toPos
if (document.selection) {
var sel = el.createTextRange()
sel.moveStart('character', len)
sel.collapse()
sel.select()
} else if (typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number') {
el.selectionStart = el.selectionEnd = len
}
},
// 选中内容
selectionContent(el, start, end) {
if (el.setSelectionRange) {
el.setSelectionRange(start, end)
} else if (el.createTextRange) {
let rang = el.createTextRange()
rang.collapse(true)
rang.moveStart('character', start)
rang.moveEnd('character', end - start)
rang.select()
}
},
// 格式化json
formatJson() {
let detail = {}
if (this.content.trim() === '') {
this.$message.error('当前JSON内容为空!')
return
}
// 验证一下json内容是否正确
try {
detail = JSON.parse(this.content)
} catch (e) {
console.log(e)
this.$message({
showClose: true,
message: 'JSON格式错误,请检查后再重新尝试格式化',
type: 'warning'
})
return
}
// 格式化编辑内容
this.content = JSON.stringify(detail, null, 2)
},
}
})
json内容获取的方法比较简单,就没有在上面写出,可以直接通过“JSON.parse(this.content)”获取到。
代码自测没有问题,如果发现有问题的话,欢迎指正。