vue实现xml在线编辑功能

先看效果 避免误会
在这里插入图片描述
这是一个在线编辑器 我们可以在这上面随意的编写xml代码格式

我们修改上面的内容之后 就可以在控制台输出内容

在这里插入图片描述
如果这正是您想要的东西 那就可以先创建一个vue项目

我们先引入依赖

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

然后在src 目录 下新建文件夹 就叫 components
在components下再创建一个文件 叫 editor

然后 在下面创建一个js文件 叫 data_format_utils

data_format_utils.js 参考代码如下


// 对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
}

然后在 editor目录下创建一个组件 我这里叫 index.vue

参考代码如下

<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>

然后在 src下的 main.js全局引入依赖 参考代码如下


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')

然后 因为这是个实验的项目 我就直接将最终引入的代码写在App.vue里啦

在src项目下找到根节点 App.vue组件
参考代码如下

<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>

启动项目即可实现最开始演示的效果

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
Spring Boot 是一个非常流行的 Java Web 开发框架,而 Vue.js 则是一个非常流行的 JavaScript 前端框架。结合两者,可以实现一个完整的 Web 应用程序。以下是基于 Spring Boot 和 Vue.js 技术栈的管理员后台公告模块的原理和代码实现。 ### 原理 此公告模块的基本原理是在后端使用 Spring Boot 框架创建 RESTful API,然后在前端使用 Vue.js 框架通过 AJAX 调用这些 API 来实现前后端的数据交互。 管理员可以添加、编辑和删除公告。当向后端发送请求时,后端会将其保存在数据库中。前端可以通过 AJAX 调用后端 API,获取所有公告的列表,并将其渲染到前端页面上。 ### 代码实现 #### 后端代码实现 首先,需要创建一个 Spring Boot 项目,使用 Maven 构建工具来管理依赖项。在 pom.xml 文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> ``` 接下来,在 src/main/resources/application.properties 文件中添加以下配置: ``` spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create ``` 这些配置将使用 H2 数据库作为后端数据库,并在内存中保存数据。 接下来,需要创建实体类和存储库接口。 ```java @Entity @Table(name = "announcement") public class Announcement { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String title; @Column(nullable = false) private String content; @Column(nullable = false) private Date createdTime; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Date getCreatedTime() { return createdTime; } public void setCreatedTime(Date createdTime) { this.createdTime = createdTime; } } @Repository public interface AnnouncementRepository extends JpaRepository<Announcement, Long> { } ``` 然后,需要创建一个控制器来处理 RESTful API 请求。 ```java @RestController @RequestMapping("/api/announcements") public class AnnouncementController { @Autowired private AnnouncementRepository announcementRepository; @GetMapping public List<Announcement> getAllAnnouncements() { return announcementRepository.findAll(); } @PostMapping public Announcement createAnnouncement(@RequestBody Announcement announcement) { announcement.setCreatedTime(new Date()); return announcementRepository.save(announcement); } @PutMapping("/{id}") public Announcement updateAnnouncement(@PathVariable Long id, @RequestBody Announcement announcement) { Optional<Announcement> existingAnnouncement = announcementRepository.findById(id); if (!existingAnnouncement.isPresent()) { throw new ResourceNotFoundException("Announcement with id " + id + " not found."); } announcement.setId(id); announcement.setCreatedTime(existingAnnouncement.get().getCreatedTime()); return announcementRepository.save(announcement); } @DeleteMapping("/{id}") public ResponseEntity<?> deleteAnnouncement(@PathVariable Long id) { Optional<Announcement> existingAnnouncement = announcementRepository.findById(id); if (!existingAnnouncement.isPresent()) { throw new ResourceNotFoundException("Announcement with id " + id + " not found."); } announcementRepository.delete(existingAnnouncement.get()); return ResponseEntity.ok().build(); } } ``` 在这个控制器中,使用 @RestController 和 @RequestMapping 注释来告诉 Spring Boot,这是一个处理 RESTful 端点的控制器。 使用 @Autowired 注释自动将 AnnouncementRepository 注入到控制器中,以便可以从数据库中检索和保存公告。 使用 @GetMapping 注释处理获取所有公告的请求,并使用 announcementRepository.findAll() 方法从数据库中检索所有公告。 使用 @PostMapping 注释处理创建新公告的请求,并使用 announcementRepository.save()方法将新公告保存到数据库中。 使用 @PutMapping 注释处理更新公告的请求,并使用 announcementRepository.save()方法将更新后的公告保存到数据库中。 使用 @DeleteMapping 注释处理删除公告的请求,并使用 announcementRepository.delete()方法从数据库中删除公告。 #### 前端代码实现 首先,需要在 Vue.js 中创建一个组件来渲染公告列表。 ```vue <template> <div> <h1>公告列表</h1> <table> <thead> <tr> <th>标题</th> <th>内容</th> <th>创建时间</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="announcement in announcements" :key="announcement.id"> <td>{{ announcement.title }}</td> <td>{{ announcement.content }}</td> <td>{{ announcement.createdTime }}</td> <td> <button @click="editAnnouncement(announcement)">编辑</button> <button @click="deleteAnnouncement(announcement)">删除</button> </td> </tr> </tbody> </table> <button @click="addAnnouncement()">添加公告</button> </div> </template> <script> import axios from 'axios'; export default { data() { return { announcements: [] }; }, methods: { loadAnnouncements() { axios.get('/api/announcements').then(response => { this.announcements = response.data; }); }, addAnnouncement() { this.$router.push('/add-announcement'); }, editAnnouncement(announcement) { this.$router.push('/edit-announcement/' + announcement.id); }, deleteAnnouncement(announcement) { axios.delete('/api/announcements/' + announcement.id).then(response => { this.loadAnnouncements(); }); } }, mounted() { this.loadAnnouncements(); } }; </script> ``` 这个组件将从后端加载公告列表,并将其渲染为 HTML 表格。它还提供了添加、编辑和删除公告的按钮。 接下来,需要创建一个添加公告的组件。 ```vue <template> <div> <h1>添加公告</h1> <form> <label>标题</label> <input type="text" v-model="announcement.title" /> <label>内容</label> <textarea v-model="announcement.content"></textarea> <button type="button" @click="submitAnnouncement()">添加</button> </form> </div> </template> <script> import axios from 'axios'; export default { data() { return { announcement: {} }; }, methods: { submitAnnouncement() { axios.post('/api/announcements', this.announcement).then(response => { this.$router.push('/'); }); } } }; </script> ``` 这个组件将允许管理员输入新公告的标题和内容,并将其提交到后端以保存。 最后,需要创建一个编辑公告的组件。 ```vue <template> <div> <h1>编辑公告</h1> <form> <label>标题</label> <input type="text" v-model="announcement.title" /> <label>内容</label> <textarea v-model="announcement.content"></textarea> <button type="button" @click="submitAnnouncement()">保存</button> </form> </div> </template> <script> import axios from 'axios'; export default { data() { return { announcement: {} }; }, methods: { loadAnnouncement(id) { axios.get('/api/announcements/' + id).then(response => { this.announcement = response.data; }); }, submitAnnouncement() { axios.put('/api/announcements/' + this.announcement.id, this.announcement).then(response => { this.$router.push('/'); }); } }, mounted() { this.loadAnnouncement(this.$route.params.id); } }; </script> ``` 这个组件将允许管理员编辑公告的标题和内容,并将其提交到后端以保存。 ### 总结 以上是基于 Spring Boot 和 Vue.js 技术栈的管理员后台公告模块的原理和代码实现。通过 RESTful API 和 AJAX,可以实现前后端的数据交互,并在 Web 应用程序中实现功能强大的管理员后台公告模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值