功能说明
- 实现一个对json进行格式化的功能
- 添加搜索框,回车进行关键词搜索,并对关键词高亮显示
- 搜索到的多个关键词,回车逐一匹配
- 监听json框,如果发生了编辑,需要在退出时提示,在得到用户确认的情况下再退出
效果展示
说明:如上图中,输入“编程”两个字,每回车一下,就定位到匹配到的第二个位置,并将当前匹配到的文字滚动到顶部。
实现步骤
第一步:编写HTML
分别添加一个输入框、一个用于展示json的pre标签、和一个取消按钮
说明:此处的其它功能有省略
<template>
<div>
<el-input class="input" v-model="searchValue" @keyup.enter.native="handleEnter" placeholder="输入关键词回车搜索"></el-input>
<pre class="content" id="json-pre" contenteditable="true" v-html="preJsonHtml"></pre>
<el-button @click="handleCancle">取消</el-button>
</div>
</template>
第二步:实现JavaScript方法
2.1 定义必要的变量和进行变量监听
<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessageBox } from 'element-plus';
const route = useRoute()
const router = useRouter()
// 存放获取到的json文件的内容
let jsonData = ref()
// 搜索框内容
let searchValue = ref()
// 用于在pre标签里展示的HTML
let preJsonHtml = ref()
// 表示json是否被修改
let isPreEdit = ref(false)
// 当前搜索匹配到第几个
let matchNum = ref(0)
// 拼接一个获取json文件的地址
const jsonUrl = computed(() => {
// 这是亚马逊上面部署的一个pdfjs服务,及存放json文件的文件夹拼接地址
const aws_server = 'http://xxx.s3-website-xxx.amazonaws.com/pdfjs-4.0.379-dist/web/docs/'
// 从路由中获取到文件名称
const json_name = route.query.json as string
// 拼接得到最终的文件地址
return `${aws_server}${json_name}`
})
// 监听jsonData,修改后需要动态更新文本框里显示的内容
watch(() => jsonData.value, val => {
preJsonHtml.value = JSON.parse(JSON.stringify(val, null, 4))
})
// 监听搜索框的内容,主要目的是:当搜索框中的内容变化了,需要将matchNum重置为0,开始新输入内容的第一个搜索
watch(() => searchValue.value, val => {
matchNum.value = 0
})
onMounted(() => {
// 这里注意,需要使用nexttick,在页面渲染出来以后再做计算
nextTick(() => {
// json-pre
var preTag = document.getElementById('json-pre');
var originalContent = preTag?.textContent;
// 添加监听事件
preTag?.addEventListener('input', function() {
if (preTag?.textContent !== originalContent) {
console.log('内容已被修改');
isPreEdit.value = true // 标记json被修改了
}
});
})
// 获取json文件,并赋值给jsonData
fetch(jsonUrl.value).then(res => res.json()).then(r => {
jsonData.value = JSON.stringify(r, null, 4)
})
})
</script>
2.2 输入框回车事件
// 输入框回车事件
// 之所以使用这个方法,是因为input输入框在输入一个文字后,需要多次联系重复搜索
const handleEnter = (event) => {
// 判断下jsonData的类型,需要一个字符串格式
let result = typeof jsonData.value !== 'string' && JSON.stringify(jsonData.value) || jsonData.value
// 正则表达式,对输入框中的内容全匹配
const Reg = new RegExp(searchValue.value, 'g')
// 获取这种全匹配下,有多少个匹配结果
// 这一步非常重要,因为需要多次进行搜索和修改样式,所以必须要记录匹配结果
let matchs = result.match(Reg)
// 判断:
// 匹配到结果 且 当前搜索到匹配结果的最后一位时,重新开始搜索第一个
if((matchs && matchs.length) && matchNum.value + 1 >= matchs.length) {
matchNum.value = 0
}
// 否则,继续向下
matchNum.value += 1
// 如果匹配有结果
if(result) {
// 定义一个替换方案
// 将匹配结果替换为一个span标签,id为highLight,颜色为黄色
let replacement = `<span id="highlight" style="background: yellow;">${searchValue.value}</span>`
// 调用字符串匹配方法:在后文中有写
const res = replaceNumMatch(Reg, result, replacement, matchNum.value);
// preJsonHtml赋值,将它包裹在了一个div中
preJsonHtml.value = `<div>${JSON.parse(JSON.stringify(res, null, 4))}</div>`
/**
* 这里是实现dom结构的滚动
*/
const div = document.getElementById('json-pre');
const span = document.getElementById('highlight');
if (div && span) {
// 计算滚动位置
const scrollTo = span.offsetTop - div.offsetTop - 10; // 这里多了一个10px的间距
// 滚动到指定位置
// 当搜索第一个时,滚动到顶部
if(matchNum.value === 1) {
div.scrollTop = 0
return
}
// 其它滚动到指定为止
div.scrollTop = scrollTo;
}
}
}
2.3 正则表达式进行字符串匹配和替换方法
/**
* 使用正则表达式进行字符串替换的方法
*/
function replaceNumMatch(regexp, str, replacement, i) {
let count = 0;
return str.replace(regexp, function(matched) {
count++;
if (count === i) {
return replacement; // 替换第i个匹配字符为指定的字符串
} else {
return matched; // 保持其他匹配字符不变
}
});
}
2.4 json对话框编辑取消方法
/**
* 取消方法:
* 这里的要求是,当json被修改后退出需要提醒
* 所以这里增加了一个isPreEdit的变量,标记json是否被修改
*/
const handleCancle = () => {
if(isPreEdit.value) {
// 如果被修改增加提醒
ElMessageBox.confirm('内容有修改,是否进行保存?', '提示',
{
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning',
closeOnClickModal: false,
showClose: false
}).then(() => {
// do something
// 如可以保存数据等
}).catch(() => {
// 否则 跳转到指定路由
router.push({ path: '/xxx' })
})
return
}
// 如果没有被修改,则跳转到指定路由
router.push({ path: '/xxx' })
}
第三步:样式
pre在文字过长时,不换行。此处的目的是为了让长段的pre换行
<style lang="scss" scoped>
// 让pre里的文字自动换行
pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
</style>