Vue implements xml online editing function

Look at the effect first to avoid misunderstanding.
insert image description here
This is an online editor, we can freely write xml code format on it.

After we modify the above content, we can output the content on the console

insert image description here
If this is exactly what you want, you can create a vue project first

We first introduce the dependency

npm install brace -S
npm install element-ui  -S
npm install vue-clipboard2 -S
npm install vkbeautify --save

Then create a new folder in the src directory called components
and create a file called editor under components

Then create a js file called data_format_utils below

The data_format_utils.js reference code is as follows


// 对json和xml进行格式化
import vkbeautify from 'vkbeautify'

export function string_to_json_wrap(v) {
    
    
  try {
    
    
    if (is_json(v)) {
    
    
      return unicode_to_china(JSON.stringify(string_to_json(v), null, '\t'))
    } else {
    
    
      return v
    }
  } catch (e) {
    
    
    console.log(e);
  }
  return v
}

export function json_wrap_to_string(v) {
    
    
  try {
    
    
    if (is_json(v)) {
    
    
      return unicode_to_china(JSON.stringify(string_to_json(v)))

    } else {
    
    
      return v
    }

  } catch (e) {
    
    
    console.log(e);
  }
  return v
}


export function string_to_xml_wrap(v) {
    
    
  try {
    
    
    return vkbeautify.xml(v);
  } catch (e) {
    
    
    return v
  }
}

export function xml_wrap_to_string(v) {
    
    
  try {
    
    
    return vkbeautify.xmlmin(v);
  } catch (e) {
    
    
    return v
  }
}

export function is_json(str) {
    
    
  if (typeof str == 'string') {
    
    
    try {
    
    
      let obj = JSON.parse(str);
      if (typeof obj == 'object' && obj) {
    
    
        return true;
      } else {
    
    
        return false;
      }

    } catch (e) {
    
    
      return false;
    }
  }
}

export function check_string_type(v) {
    
    
  try {
    
    
    if (v.startsWith("<!DOCTYPE html")) {
    
    
      return 'HTML'
    } else if (v.startsWith("<")) {
    
    
      return 'XML'
    } else if (is_json(v)) {
    
    
      return 'JSON'
    } else {
    
    
      return 'TEXT'
    }
  } catch (e) {
    
    
    return 'TEXT'
  }
}

export function wrap_to_string(v, t) {
    
    
  let type = t || check_string_type(v)
  switch (type) {
    
    
    case 'JSON': {
    
    
      return json_wrap_to_string(v)
    }
    case 'XML': {
    
    
      return xml_wrap_to_string(v)
    }
    case 'HTML': {
    
    
      return xml_wrap_to_string(v)
    }
  }
  return v
}

export function string_to_wrap(v, t) {
    
    
  let type = t || check_string_type(v)
  switch (type) {
    
    
    case 'JSON': {
    
    
      return string_to_json_wrap(v)
    }
    case 'XML': {
    
    
      return string_to_xml_wrap(v)
    }
    case 'HTML': {
    
    
      return string_to_xml_wrap(v)
    }
  }
  return v
}

export function string_to_json(v) {
    
    
  try {
    
    
    if (is_json(v)) {
    
    
      return v
    } else {
    
    
      return v
    }
  } catch (e) {
    
    
    return v
  }
}

export function unicode_to_china(input) {
    
    
  try {
    
    
    return input.replace(/\\\\u([0-9a-fA-F]{2,4})/g, function (string, matched) {
    
    
      return String.fromCharCode(parseInt(matched, 16))
    })
  } catch (e) {
    
    
    console.log(e);
  }
  return input
}

Then create a component in the editor directory, which I call index.vue

The reference code is as follows

<template>
  <div>
    <el-card class="box-card">
      <!-- 操作栏 -->
      <el-row slot="header" class="clearfix" v-if="toolbar == true">
        <el-col :span="5">
          <el-button type="primary" @click="toolsBarLeft">格式化</el-button>
          <el-button type="primary" @click="toolsBarRight">恢复</el-button>
        </el-col>
        <el-col :span="6">
          <el-select v-model="value_type">
            <el-option label="JSON" value="JSON"></el-option>
            <el-option label="TEXT" value="TEXT"></el-option>
            <el-option label="XML" value="XML"></el-option>
            <el-option label="HTML" value="HTML"></el-option>
          </el-select>
        </el-col>
        <el-col :span="2" style="float:right">
          <el-button type="primary" v-clipboard:copy="contentBackup" @click="copy_value">复制</el-button>
        </el-col>
      </el-row>
      <!-- 编辑器 -->
      <div ref="vue_editor" style="height: 50vh;width: 100%"></div>
    </el-card>
  </div>
</template>
<style>
.box-card {
      
      
  margin: 20px;
}
.btn-hover {
      
      
  padding-left: 6px;
  padding-right: 6px;
}
.btn-hover:hover {
      
      
  background: #e0e0e0 !important;
}
.ace-xcode .ace_gutter {
      
      
  border-right: none !important;
  background: #fafafa !important;
}
.ace_content_disable {
      
      
  background: #fafafa !important;
}
</style>
<script>
// 引入ace代码编辑器
import ace from "brace/index";
import "brace/ext/emmet";
import "brace/ext/language_tools";
import "brace/mode/html";
import "brace/mode/json";
import "brace/mode/text";
import "brace/mode/xml";
import "brace/mode/javascript";
import "brace/theme/xcode";
import "brace/theme/terminal";
import "brace/snippets/javascript";
// 代码格式化方法
import {
      
      
  string_to_json_wrap,
  json_wrap_to_string,
  string_to_xml_wrap,
  check_string_type,
  wrap_to_string,
  string_to_wrap
} from "./data_format_utils";
// 主要代码
export default {
      
      
  name: "vue_editor",
  /**
   * 参数介绍:
   * value:(必填)双向绑定的数据;
   * theme:(非必填)ace编辑器主题默认xcode,可根据官网自行更换;
   * height:(必填)高度;
   * width:(必填)宽度;
   * options:(非必填)ace编辑器的设置
   * toolbar: (非必填)操作栏;
   * disable:(必填)是否启用编辑功能;
   * type:(非必填)json/xml/html/text,也支持更多,自行引用
   *
   */
  props: {
      
      
    value: {
      
      
      required: true
    },
    theme: {
      
      
      type: String,
      default: "xcode",
      required: false
    },
    options: Object,
    toolbar: {
      
      
      required: false,
      default: true,
      type: Boolean
    },
    disable: {
      
      
      required: false,
      type: Boolean,
      default: false
    },
    type: {
      
      
      required: false,
      type: String
    }
  },
  data() {
      
      
    return {
      
      
      editor: null,
      contentBackup: "",
      value_type: null,
      internalChange: false
    };
  },
  watch: {
      
      
    theme(v) {
      
      
      this.editor.setTheme("ace/theme/" + v);
    },
    options(v) {
      
      
      this.editor.setOptions(v);
    },
    height() {
      
      
      this.$nextTick(function() {
      
      
        this.editor.resize();
      });
    },
    width() {
      
      
      this.$nextTick(function() {
      
      
        this.editor.resize();
      });
    },
    value(v) {
      
      
      if (this.editor && !this.internalChange) {
      
      
        v = v && v !== null ? v : "";
        typeof v === "object" && (v = JSON.stringify(v));
        this.contentBackup = string_to_wrap(v);
        this.value_type = this.type || check_string_type(this.contentBackup);
        this.editor.session.setValue(this.contentBackup);
      }
    },
    value_type(nv) {
      
      
      switch (nv) {
      
      
        case "JSON": {
      
      
          this.contentBackup = string_to_json_wrap(this.contentBackup);
          this.editor.getSession().setMode("ace/mode/" + nv.toLowerCase());
          this.editor.session.setValue(this.contentBackup);
          break;
        }
        case "TEXT": {
      
      
          this.contentBackup = json_wrap_to_string(this.contentBackup);
          this.editor.getSession().setMode("ace/mode/" + nv.toLowerCase());
          this.editor.session.setValue(this.contentBackup);
          break;
        }
        case "XML": {
      
      
          this.contentBackup = string_to_xml_wrap(this.contentBackup);
          this.editor.getSession().setMode("ace/mode/" + nv.toLowerCase());
          this.editor.session.setValue(this.contentBackup);
          break;
        }
        case "HTML": {
      
      
          this.contentBackup = string_to_xml_wrap(this.contentBackup);
          this.editor.getSession().setMode("ace/mode/" + nv.toLowerCase());
          this.editor.session.setValue(this.contentBackup);
          break;
        }
        // 新增类别
        case "javascript": {
      
      
          this.editor.getSession().setMode("ace/mode/" + nv.toLowerCase());
          this.editor.session.setValue(this.contentBackup);
          break;
        }
      }
    },
    disable(v) {
      
      
      if (this.editor) {
      
      
        this.editor.setReadOnly(v);
        v
          ? this.$refs.vue_editor.classList.add("ace_content_disable")
          : this.$refs.vue_editor.classList.remove("ace_content_disable");
      }
    }
  },
  methods: {
      
      
    // 单位校验
    px(n) {
      
      
      if (/^\d*$/.test(n)) {
      
      
        return n + "px";
      }
      return n;
    },
    // 格式化
    toolsBarLeft() {
      
      
      this.contentBackup = string_to_wrap(this.contentBackup, this.value_type);
      this.editor.session.setValue(this.contentBackup);
    },
    // 数据转字符串
    toolsBarRight() {
      
      
      this.contentBackup = wrap_to_string(this.contentBackup, this.value_type);
      this.editor.session.setValue(this.contentBackup);
    },
    copy_value() {
      
      
      this.$copyText(this.contentBackup).then(
        () => {
      
      
          this.$message.success("已经复制到粘贴板!");
        },
        () => {
      
      
          this.$message.error("复制失败!");
        }
      );
    },
    onChange() {
      
      
      let error = false;
      let v;
      try {
      
      
        v = this.editor.getValue();
        error = false;
      } catch (err) {
      
      
        error = true;
      }
      if (error) {
      
      
        this.$emit("error");
      } else {
      
      
        if (this.editor) {
      
      
          this.internalChange = true;
          this.contentBackup = v;
          this.$emit("input", v);
          this.$nextTick(() => {
      
      
            this.internalChange = false;
          });
        }
      }
    },
    // 编辑器
    initView() {
      
      
      this.contentBackup = this.value && this.value !== null ? this.value : "";
      this.value_type = check_string_type(this.value);
      let vm = this;
      let lang = this.lang || "text";
      let theme = this.theme && this.theme !== "xcode" ? this.theme : "xcode";
      let editor_div = this.$refs.vue_editor;
      let editor = (vm.editor = ace.edit(editor_div));
      this.$emit("init", editor);
      editor.$blockScrolling = Infinity;
      editor.setOption("enableEmmet", false);
      editor.getSession().setMode("ace/mode/" + lang);
      editor.setTheme("ace/theme/" + theme);
      editor.getSession().setUseWrapMode(true);
      editor.setShowPrintMargin(false);
      editor.setValue(this.contentBackup);
      editor.on("change", vm.onChange);
      if (vm.options) editor.setOptions(vm.options);
      if (vm.disable) {
      
      
        editor.setReadOnly(true);
      }
      // 启用提示
      editor.setOption({
      
      
        enableBasicAutocompletion: true,
        enableSnippets: true,
        enableLiveAutocompletion: true
      });
    }
  },
  beforeDestroy() {
      
      
    this.editor.destroy();
    this.editor.container.remove();
  },
  mounted() {
      
      
    this.initView();
  }
};
</script>

Then introduce the dependent reference code globally in main.js under src as follows


import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
Vue.use(ElementUI);

// 引入复制
import VueClipboard from 'vue-clipboard2'
VueClipboard.config.autoSetContainer = true
Vue.use(VueClipboard)

new Vue({
    
    
  render: h => h(App),
}).$mount('#app')

Then because this is an experimental project, I will directly write the final introduced code in App.vue

Find the root node App.vue component under the src project.
The reference code is as follows

<template>
  <div>
    <!-- 引用插件 -->
    <VueDataEditor
      @input="codeChange"
      :value="code"
      :disable="false"
    ></VueDataEditor>
    <hr />
    <el-button @click="update" type="primary">提交新代码块</el-button>
  </div>
</template>
<script>
import VueDataEditor from "./components/editor";

export default {
      
      
  data() {
      
      
    return {
      
      
      // 双向绑定的值
      code:'<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <build>        <plugins>            <plugin>                <groupId>org.mybatis.generator</groupId>                <artifactId>mybatis-generator-maven-plugin</artifactId>                <version>1.3.2</version>                <configuration>                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>                    <verbose>true</verbose>                    <overwrite>true</overwrite>                </configuration>            </plugin>        </plugins>    </build></project>',
      disable: false,
    };
  },
  components: {
      
      
    VueDataEditor
  },
  methods: {
      
      
    // 子组件传递过来的最新值的方法
    codeChange(event) {
      
      
      this.code = event;
    },
    // 打印
    update() {
      
      
      console.log(this.code);
    }
  }
};
</script>

Start the project to achieve the effect of the initial demonstration

Guess you like

Origin blog.csdn.net/weixin_45966674/article/details/129106655#comments_26023653