记录JSON编辑器和JSON文件对比

930 阅读2分钟

需求:在页面中实现一个json编辑器和实现一个json文件对比并且显示差异

json编译器效果图如下

image.png

代码对比效果图如下

image.png

实现代码

//编译器用到的是 vue-codemirror  codemirror
//代码对比用到的是 diff-match-patch

npm i -S vue-codemirror  codemirror
npm i -S diff-match-patch

新建一个JsonEditor.vue文件

<template>
  <div class="json-editor">
    <codemirror
      ref="myCode"
      :value="value"
      :options="codeOptions"
      @input="inputValue"
    ></codemirror>
  </div>
</template>
  
  <script>
import { codemirror } from "vue-codemirror";
import "codemirror/addon/lint/lint.css";
import "codemirror/lib/codemirror.css";
// //主题
import "codemirror/theme/rubyblue.css";
require("script-loader!jsonlint");
import "codemirror/mode/javascript/javascript";
import "codemirror/addon/lint/lint";
import "codemirror/addon/lint/json-lint";
// html代码高亮
import "codemirror/mode/htmlmixed/htmlmixed.js";
import "codemirror/addon/selection/active-line.js";
// 折叠代码
// foldGutter
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/xml-fold.js";

export default {
  name: "JsonEditor",
  props: {
    value: {//内容初始值
      type: String,
      default: "",
    },
    
    disabled: {//代码是否可以编辑
      type: Boolean,
      default: false,
    },
  },
  components: {
    codemirror,
  },
  data() {
    return {
      codeOptions: {
        autoRefresh: true, // 重点是这句,为true 自动刷新
        mode: "application/json", //格式 可以为html、sql等等
        gutters: ["CodeMirror-lint-markers", "CodeMirror-foldgutter"],
        // theme: "rubyblue", //主题 baspin、rubyblue
        lint: true, 
        foldGutter: true, //折叠代码
        lineNumbers: true,
        smartIndent: true,
        cursorBlinkRate: 350,
      },
    };
  },
  methods: {
    inputValue(value) {
      this.$emit("changed", value);//每次输入的时候都会传给父组件当前值
    },
  },
  watch: {
    disabled: {
      handler(v) {
        this.codeOptions.readOnly = v;
        if (v) {
          this.codeOptions.cursorBlinkRate = -1;
          this.codeOptions.lint = false;
          this.codeOptions.theme = "baspin"
        } else {
          this.codeOptions.cursorBlinkRate = 350;
          this.codeOptions.lint = true;
          // this.codeOptions.theme = "rubyblue"
        }
      },
      immediate: true,
    },
  },
};
</script>
  
  <style lang="scss" scoped>
.json-editor {
  // height: 400px;
  position: relative;
  display: flex;
  flex-direction: column;

  ::v-deep {
    .CodeMirror {
      flex: 1;
      border: 1px solid #efefef ;
    }

    .CodeMirror-scroll {
      max-height: 400px;
     
    }

    .cm-s-rubyblue span.cm-string {
      color: #f08047;
    }
  }
}
</style>
  

父组件使用

import JsonEditor from "@/components/JsonEditor";

 <json-editor
     ref="jsonEditor"
     v-model="appConfig"
     @changed="changeValue"
     :disabled="!editFlag"/>

小tips 获取编译器鼠标位置 ,并且往编译器插入文本了


 let pos1 = this.$refs.myCode.codemirror.getCursor();
 let pos2 = {};
 pos2.line = pos1.line; //行
 pos2.ch = pos1.ch; //列
 this.$refs.myCode.codemirror.replaceRange(value, pos2);

代码对比

新建一个CodeCompare.vue 文件


<template>
  <div id="contrastDiv">
    <div ref="contrastDiv"></div>
  </div>
</template>
   
  <script>
require("codemirror/mode/javascript/javascript.js");
import CodeMirror from "codemirror";
import "codemirror/lib/codemirror.css";
import "codemirror/addon/merge/merge.js";
import "codemirror/addon/merge/merge.css";
import DiffMatchPatch from "diff-match-patch";
window.diff_match_patch = DiffMatchPatch;
window.DIFF_DELETE = -1;
window.DIFF_INSERT = 1;
window.DIFF_EQUAL = 0;

export default {
  name: "CodeMirror",
  props: {
    oldVal: {
      default: "",
    },
    newVal: {
      default: "",
    },
  },
  watch: {
    newVal(val) {
      this.contrast();
    },
  },
  mounted() {
    this.prepareData();
    this.contrast();
  },
  methods: {
    prepareData() {},
    //初始化
    contrast() {

      this.$nextTick(() => {
        const target = this.$refs.contrastDiv;
        target.innerHTML = "";
        CodeMirror.MergeView(target, {
          value: this.prettyJson(this.oldVal) ?? "",
          //   origLeft: null,
          orig: this.prettyJson(this.newVal) ?? "",
          lineNumbers: true, // 显示行号
          mode: "text/html",
          highlightDifferences: true,
          connect: "align",
          readOnly: true, // 只读 不可修改
        });
      });
    },
    prettyJson(json) {
      return JSON.stringify(json, null, 2);
    },
  },
};
</script>
  <style lang="scss">
.compareClass {
  height: 100%;
  overflow-y: auto;
  overflow-x: auto;

  .CodeMirror-merge {
    display: flex;
  }

  //.CodeMirror-linenumber {
  //  left: -32px !important;
  //}

  .CodeMirror-merge,
  .CodeMirror-merge .CodeMirror {
    height: 100vh;
  }

  .CodeMirror-merge-2pane .CodeMirror-merge-pane {
    height: 100%;
  }

  .CodeMirror-merge-r-chunk {
    background: rgba(30, 144, 255, 0.5);
  }

  .CodeMirror-merge-r-chunk-start {
    border-top: 1px solid dodgerblue;
  }

  .CodeMirror-merge-r-chunk-end {
    border-bottom: 1px solid dodgerblue;
  }

  .CodeMirror-merge-r-connect {
    fill: rgba(30, 144, 255, 0.5);
    stroke: rgba(30, 144, 255, 0.5);
    stroke-width: 1px;
  }

  .CodeMirror-merge-l-chunk {
    background: rgba(30, 144, 255, 0.5);
  }

  .CodeMirror-merge-l-chunk-start {
    border-top: 1px solid dodgerblue;
  }

  .CodeMirror-merge-l-chunk-end {
    border-bottom: 1px solid dodgerblue;
  }

  .CodeMirror-merge-l-connect {
    fill: rgba(30, 144, 255, 0.5);
    stroke: rgba(30, 144, 255, 0.5);
    stroke-width: 1px;
  }

  .CodeMirror-merge-l-chunk {
    background: rgba(30, 144, 255, 0.5);
  }

  .CodeMirror-merge-r-chunk {
    background: rgba(30, 144, 255, 0.5);
  }

  .CodeMirror-merge-l-chunk-start {
    border-top: 1px solid dodgerblue;
  }

  .CodeMirror-merge-r-chunk-start {
    border-top: 1px solid dodgerblue;
  }

  .CodeMirror-merge-l-chunk-end {
    border-bottom: 1px solid dodgerblue;
  }

  .CodeMirror-merge-r-chunk-end {
    border-bottom: 1px solid dodgerblue;
  }
}
</style>
对比的时候对比的是文本 将json文件JSON.parse格式化一下然后再转为文本对比
<el-row class="version-notice">
    <el-col :span="12"><span>当前版本</span></el-col>
    <el-col :span="12"><span>历史版本</span></el-col>
</el-row>
<code-compare :oldVal="editCodeValue" :newVal="newCodeValue" :isReadOnly="true" class="compare-box"/>