可以画图,导入导出xml文件,更改cell样式(填充颜色)

上代码:

editor.vue


<template><div><div class="topbar"><div class="topbar-left"><el-selectv-model="requestInfo.provinceId"placeholder="请选择省份"filterablesize="small"clearablestyle="width: 200px;"@change="proChange"><el-optionv-for="item in provinceList":key="item.areaId":value="item.areaId":label="item.name"/></el-select><el-selectv-model="requestInfo.cityId"placeholder="请选择城市"filterablesize="small"clearablestyle="width: 200px;"@change="cityChange"><el-optionv-for="item in cityList":key="item.areaId":value="item.areaId":label="item.name"/></el-select><el-selectv-model="requestInfo.communityId"placeholder="请选择小区"filterablesize="small"style="width: 200px;"clearable@change="communityChange"><el-optionv-for="item in communityList":key="item.id":value="item.id":label="item.cmtName"/></el-select><el-selectv-model="requestInfo.garageId"placeholder="请选择车库"filterablestyle="width: 200px;"size="small"clearable@change="garageChange"><el-option v-for="item in garageList" :key="item.id" :value="item.id" :label="item.name"/></el-select><el-button size="small" type="primary" @click.stop="importBgImg">上传车库图</el-button><el-button size="small" type="primary" @click.stop="save">保存</el-button></div><div class="topbar-right"><input ref="importInput" class="hide" hidden type="file" @change="readFile"><el-button size="small" type="primary" plain @click.stop="exportFile">导出xml文件</el-button><el-color-picker v-model="color" style="margin: 0 10px;" @change="colorChange"/><el-button type="primary" size="small" @click.stop="importFile">导入xml文件</el-button></div></div><el-dialog:visible.sync="uploadVisible":close-on-click-modal="false"title="提示"width="30%"style="text-align: center"><el-uploadref="uploadImg":action="uploadUrl":show-file-list="false":on-error="importError":on-success="uploadImgSuccess":headers="headers"drag><i class="el-icon-upload"/><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div></el-upload></el-dialog><div class="m_graph_container" tabindex><div id="container" ref="container"/></div><div id="sidebar" class="sidebar"><el-collapse><el-collapse-item title="基本" name="基本"><div id="toolbar-basic" class="toolbar-basic"/></el-collapse-item></el-collapse><el-collapse><el-collapse-itemv-for="(palette, paletteIndex) in Object.values(palettes)":key="paletteIndex":title="palette['title']":name="palette['name']"><av-for="(_, shapeIndex) in palette['shapes']"ref="dragItem":key="shapeIndex":shapeIndex="shapeIndex":paletteIndex="paletteIndex"/></el-collapse-item></el-collapse></div></div>
</template><script>
import mxgraph from '@/graph/index'
const {mxToolbar,mxGraphModel,mxGraph,mxCell,mxGeometry,mxEvent,mxUtils,mxCodec,mxUndoManager,mxKeyHandler,mxClipboard,mxImage,mxCellTracker,mxStencil,mxStencilRegistry,mxConstants
} = mxgraph
import FileSaver from 'file-saver'
import { update, info } from '@/api/region/garage'
//我自己的接口
import { provinceList, cityList, communityList, garageList } from '@/api/public'const path = require('path')export default {name: 'Editor',data() {return {model: null,graph: null,toolbar: null,color: '',provinceList: [],cityList: [],communityList: [],garageList: [],requestInfo: {},garageEdit: {},uploadUrl: process.env.VUE_APP_BASE_API + 'api-web/ossUpload/uploadFile',excelFlag: false,uploadVisible: false,sidebar: null,palettes: {}}},computed: {headers() {return {'Authorization': 'Bearer ' + this.$store.getters.token}}},watch: {uploadVisible(val) {!val && setTimeout(() => {this.$refs['uploadImg'].clearFiles()this.$refs['uploadImg'].abort()}, 100)}},mounted() {this.init()this.getPro()this.addStencilPalette('箭头', 'arrows', path.join('./arrows.xml'))},beforeDestroy() {this.graph.destroy()},methods: {init() {// eslint-disable-next-line new-capthis.model = new mxGraphModel()// eslint-disable-next-line new-capthis.graph = new mxGraph(this.$refs.container, this.model)this.graph.setConnectable(false)this.graph.setMultigraph(false)// Highlights the vertices when the mouse enters// eslint-disable-next-line new-capnew mxCellTracker(this.graph, '#00FF00')// Enables tooltips for the overlaysthis.graph.setTooltips(true)this.graph.gridSize = 20// 禁用鼠标右键mxEvent.disableContextMenu(this.$refs.container)this.initContentMenu() // 初始化上下文菜单this.initUndoManager() // 初始化undo/redothis.initKeyHandler() // 初始化键盘事件this.$nextTick(() => {this.initToolBar()})},addStencilPalette(title, name, file) {const req = mxUtils.load(file)const root = req.getDocumentElement()let shape = root.firstChildthis.$set(this.palettes, name, { title, name, shapes: [] })while (shape != null) {if (shape.nodeType === mxConstants.NODETYPE_ELEMENT) {const shapeName = shape.getAttribute('name')const w = shape.getAttribute('w')const h = shape.getAttribute('h')// eslint-disable-next-line new-capmxStencilRegistry.addStencil(shapeName, new mxStencil(shape))this.palettes[name]['shapes'].push({ name: shape.getAttribute('name'), width: w / 2, height: h / 2 })}shape = shape.nextSibling}},initToolBar() {var containerBasic = document.getElementById('toolbar-basic')// eslint-disable-next-line new-capvar toolbarBasic = new mxToolbar(containerBasic)toolbarBasic.enabled = false// toolbar 自带shapethis.addVertex('./mxgraph/examples/editors/images/rectangle.gif', 40, 100, '', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/rounded.gif', 40, 100, 'shape=rounded', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/ellipse.gif', 40, 100, 'shape=ellipse', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/rhombus.gif', 40, 100, 'shape=rhombus', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/triangle.gif', 40, 100, 'shape=triangle', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/cylinder.gif', 40, 100, 'shape=cylinder', toolbarBasic)this.addVertex('./mxgraph/examples/editors/images/actor.gif', 40, 100, 'shape=actor', toolbarBasic)// 箭头const domArray = this.$refs.dragItemif (!(domArray instanceof Array) || domArray.length <= 0) {return}domArray.forEach(dom => {const shapeIndex = dom.getAttribute('shapeIndex')const paletteIndex = dom.getAttribute('paletteIndex')const shapeItem = Object.values(this.palettes)[paletteIndex]['shapes'][shapeIndex]const width = shapeItem['width']const height = shapeItem['height']const dragHandler = (graph, evt, cell, x, y) => {this.instanceGraph(this.graph, shapeItem, x, y, width, height)}const createDragPreview = () => {const elt = document.createElement('div')elt.style.border = '1px solid black'elt.style.width = `${width}px`elt.style.height = `${height}px`return elt}dom.appendChild(this.createThumb(shapeItem, width, height))mxUtils.makeDraggable(dom, this.graph, dragHandler, createDragPreview(), 0, 0, false, true)})},createThumb(item, width, height) {// eslint-disable-next-line new-capconst tmpGraph = new mxGraph(document.createElement('div'))const thumbBorder = 2tmpGraph.labelsVisible = falsetmpGraph.view.scaleAndTranslate(1, 0, 0)this.instanceGraph(tmpGraph, item, 0, 0, width, height)const bounds = tmpGraph.getGraphBounds()const s = Math.floor(Math.min((width - 2 * thumbBorder) / bounds.width, (height - 2 * thumbBorder) / bounds.height) * 100) / 100tmpGraph.view.scaleAndTranslate(s, Math.floor((width - bounds.width * s) / 2 / s - bounds.x), Math.floor((height - bounds.height * s) / 2 / s - bounds.y))const node = tmpGraph.view.getCanvas().ownerSVGElement.cloneNode(true)node.style.position = 'relative'node.style.overflow = 'hidden'node.style.cursor = 'move'node.style.width = `${width}px`node.style.height = `${height}px`node.style.left = `${thumbBorder}px`node.style.top = `${thumbBorder}px`node.style.display = 'inline-block'return node},instanceGraph(graph, shapeItem, x, y, width, height) {const parent = graph.getDefaultParent()graph.getModel().beginUpdate()try {const vertex = graph.insertVertex(parent, null, null, x, y, width, height, `shape=${shapeItem['name']};`)vertex.customer = true} finally {graph.getModel().endUpdate()}},addVertex(icon, w, h, style, toolbar) {// eslint-disable-next-line new-capconst vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style)vertex.setVertex(true)const img = this.addToolbarItem(this.graph, toolbar, vertex, icon)img.enabled = truethis.graph.getSelectionModel().addListener(mxEvent.CHANGE, () => {const tmp = this.graph.isSelectionEmpty()mxUtils.setOpacity(img, (tmp) ? 100 : 20)img.enabled = tmp})},addToolbarItem(graph, toolbar, prototype, image) {const funct = function(graph, evt, cell, x, y) {graph.stopEditing(false)const vertex = graph.getModel().cloneCell(prototype)vertex.geometry.x = xvertex.geometry.y = ygraph.addCell(vertex)graph.setSelectionCell(vertex)}const img = toolbar.addMode(null, image, function(evt, cell) {const pt = this.graph.getPointForEvent(evt)funct(graph, evt, cell, pt.x, pt.y)})mxEvent.addListener(img, 'mousedown', function(evt) {if (img.enabled === false) {mxEvent.consume(evt)}})mxEvent.addListener(img, 'mouseenter', function(evt) {if (img.enabled === false) {mxEvent.consume(evt)}img.style = 'background: gainsboro'})mxEvent.addListener(img, 'mouseleave', function(evt) {if (img.enabled === false) {mxEvent.consume(evt)}img.style = 'background: white'})mxUtils.makeDraggable(img, graph, funct)return img},logXML() {// eslint-disable-next-line new-capvar encoder = new mxCodec()var node = encoder.encode(this.graph.getModel())return mxUtils.getPrettyXml(node)},exportFile() {// eslint-disable-next-line new-capvar encoder = new mxCodec()var node = encoder.encode(this.graph.getModel())var xml = mxUtils.getXml(node)const blob = new Blob([xml], { type: 'text/plain;charset=utf-8' })FileSaver.saveAs(blob, '车位.xml')},readFile(evt) {const file = evt.target.files[0]const reader = new FileReader()reader.onload = e => {var txt = e.target.resultthis.importModelXML(txt)}reader.readAsText(file)},importModelXML(xml) {this.graph.getModel().beginUpdate()try {const doc = mxUtils.parseXml(xml)// eslint-disable-next-line new-capconst dec = new mxCodec(doc)var node = doc.documentElementdec.decode(node, this.graph.getModel())} finally {this.graph.getModel().endUpdate()}},colorChange() {this.graph.getModel().beginUpdate()try {const cells = this.graph.getSelectionCells()for (let i = 0; i < cells.length; i++) {this.graph.getModel().setStyle(cells[i], 'fillColor=' + this.color + ';')}} finally {this.graph.getModel().endUpdate()}},importFile() {this.$refs.importInput.click()},deleteCells({ cells = [], includeEdges = false, multilevel = true }) {if (!cells || !(cells instanceof Array)) {throw new Error('cells 必须是一个数组')}const tmpSet = new Set(cells)if (multilevel) {cells.forEach(cell => {this.findDeleteCell(cell, tmpSet)})}this.graph.removeCells(Array.from(tmpSet), includeEdges)},findDeleteCell(cell, deleteSet) {deleteSet.add(cell)if (cell.edges) {cell.edges.forEach(tmpEdge => {if (tmpEdge.target !== cell) {deleteSet.add(tmpEdge.target)this.findDeleteCell(tmpEdge.target, deleteSet)}})}},cut() {var cells = []cells = this.graph.getSelectionCells()mxClipboard.cut(this.graph, cells)},copy() {var cells = []cells = this.graph.getSelectionCells()mxClipboard.copy(this.graph, cells)},paste() {mxClipboard.paste(this.graph)},undo() {if (!this.undoMng) {throw new Error('mxUndoManager 没有初始化')}this.undoMng.undo()},redo() {if (!this.undoMng) {throw new Error('mxUndoManager 没有初始化')}this.undoMng.redo()},initContentMenu() {this.graph.popupMenuHandler.factoryMethod = (menu /*, cell*/) => {menu.addItem('删除', null, () => {this.deleteCells({cells: this.graph.getSelectionCells(),includeEdges: true})})menu.addSeparator()menu.addItem('剪切', null, () => {this.cut()})menu.addItem('复制', null, () => {this.copy()})menu.addItem('粘贴', null, () => {this.paste()})menu.addSeparator()menu.addItem('前进', null, () => {this.redo()})menu.addItem('后退', null, () => {this.undo()})}},initUndoManager() {// eslint-disable-next-line new-capthis.undoMng = new mxUndoManager()const listen = (sender, evt) => {this.undoMng.undoableEditHappened(evt.getProperty('edit'))}this.graph.getModel().addListener(mxEvent.UNDO, listen)this.graph.getView().addListener(mxEvent.UNDO, listen)},initKeyHandler() {if (!this.graph) {throw new Error('graph 没有初始化')}// eslint-disable-next-line new-capthis.keyHandler = new mxKeyHandler(this.graph)this.keyHandler.bindControlKey(67, () => {this.copy()})this.keyHandler.bindControlKey(88, () => {this.cut()})this.keyHandler.bindControlKey(86, () => {this.paste()})this.keyHandler.bindControlKey(89, () => {this.redo()})this.keyHandler.bindControlKey(90, () => {this.undo()})},uploadImgSuccess(response, file, fileList) {this.garageEdit.id = this.requestInfo.garageIdthis.$set(this.garageEdit, 'baseImg', response.data)update(this.garageEdit).then((response) => {if (response.data) {this.changeBgImg(this.garageEdit.baseImg)this.uploadVisible = falsethis.$message.success('相应车库图片上传更新成功')} else {this.$message.error('上传失败')}})},changeBgImg(url) {const width = this.$refs.container.offsetWidthconst height = this.$refs.container.offsetHeight// eslint-disable-next-line new-capvar img = new mxImage(url, width, height)this.graph.setBackgroundImage(img)this.graph.view.validate()},// 非graphgetPro() {provinceList().then(response => {this.provinceList = response.data})},proChange() {this.getCity()},getCity() {cityList({ areaId: this.requestInfo.provinceId }).then(response => {this.cityList = response.data})},cityChange() {this.getCommuntiy()},getCommuntiy() {communityList({ provinceId: this.requestInfo.provinceId, cityId: this.requestInfo.cityId }).then(response => {this.communityList = response.data})},communityChange() {this.getGarage()},getGarage() {garageList({ communityId: this.requestInfo.communityId }).then(response => {this.garageList = response.data})},save() {this.garageEdit.id = this.requestInfo.garageIdthis.garageEdit.xmlInfo = this.logXML()update(this.garageEdit).then((response) => {if (response.data) {this.$message.success('保存成功')} else {this.$message.success('保存失败')}})},importError(err, file, fileList) {this.$message.error('上传出现错误')console.log(err)},garageChange() {info({ id: this.requestInfo.garageId }).then(response => {this.garageEdit = response.datathis.changeBgImg(response.data.baseImg)this.importModelXML(response.data.xmlInfo)})},importBgImg() {if (!this.requestInfo.garageId) {this.$message.error('请先选择车库')return false}this.uploadVisible = true},// 文件大小限制提示beforeImgUpload(file) {if (!this.requestInfo.garageId) {this.$message.error('请先选择车库')return false}const isLt2M = file.size / 1024 / 1024 < 100if (!isLt2M) {this.$message.error('上传大小不能超过 100MB!')}return isLt2M}}
}
</script><style  lang="scss">
.m_graph_container {position: absolute;height: 90%;width: calc(95% - 200px);margin-left: 200px;#container {position: absolute;overflow: hidden;left: 24px;top: 0;right: 0;bottom: 0;background-image: url("../../../public/mxgraph/examples/editors/images/grid.gif");}
}
.sidebar {width: 200px;padding-left: 10px;.el-collapse-item__header {padding-left: 10px;}.el-collapse-item__content {padding-left: 10px;padding-bottom: 0;}
}
.topbar {display: flex;align-items: center;padding: 10px;.topbar-left {display: flex;align-items: center;margin-right: 20px;}.topbar-right {display: flex;align-items: center;}
}
</style>

graph/index.js

import mx from 'mxgraph'const mxgraph = mx({mxBasePath: '/public/mxgraph'
})// decode bug https://github.com/jgraph/mxgraph/issues/49
window.mxGraph = mxgraph.mxGraph
window.mxGraphModel = mxgraph.mxGraphModel
window.mxEditor = mxgraph.mxEditor
window.mxGeometry = mxgraph.mxGeometry
window.mxDefaultKeyHandler = mxgraph.mxDefaultKeyHandler
window.mxDefaultPopupMenu = mxgraph.mxDefaultPopupMenu
window.mxStylesheet = mxgraph.mxStylesheet
window.mxDefaultToolbar = mxgraph.mxDefaultToolbar
window.mxToolbar = mxgraph.mxToolbar
window.mxUndoManager = mxgraph.mxUndoManager
window.mxImage = mxgraph.mxImageexport default mxgraph

生于所有需要引入的资源(图片,xml)均可以在mxgraph的github仓库找到,根据自己的文件位置更改路径

第一次用mxgraph,感谢大佬的帮助lanniu(https://github.com/lanniu/vue-mxgraph-example)的帮助

mxgraph vue 简陋编辑器相关推荐

  1. antv-x6 vue流程图编辑器demo

    antv-x6 vue流程图编辑器demo 效果如下: 效果如下: <template><div class="content"><div class ...

  2. vue使用图像编辑器tui-image-editor

    vue使用图像编辑器tui-image-editor 前言 效果展示 涂鸦 裁剪 标注 旋转 滤镜 一.安装 二.使用 1.快速体验 2.国际化 3.自定义样式 4.按钮优化 5.完整代码 总结 前言 ...

  3. vue数学公式编辑器_将Vue包装器用于MathLive数学编辑器的示例

    vue数学公式编辑器 Vue-Mathlive (vue-mathlive) The MathLive Vue wrapper provides a Vue component that implem ...

  4. vue + mavon-editor编辑器

    vue + mavon-editor编辑器 我个人使用过 quill-editor编辑器 和 mavon-editor编辑器 这个编辑器的优点还挺多的,但我个人的观点预览功能很棒 如果后续想了解 qu ...

  5. Vue - Markdown编辑器

    Hello,我是 Alex 007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫 Alex 的人太多了,再加上每天007的生活,Alex 007就诞生了. 最近在做工作室的官网,需要一套 ...

  6. vue获取编辑器纯文字_前端富文本编辑器 vue-html5-editor

    1..项目创建与初始化 在安装好脚手架的依赖后,要执行 npm install vue-html5-editor -S 来安装这个富文本插件,由于这个富文本插件的图标是依赖font-awesome.c ...

  7. vue获取编辑器纯文字_vue中使用富文本编辑器

    前端使用富文本编辑器的插件有很多,今天献上wangeditor的使用教程,教你如何在vue中使用富文本编辑器 wangeditor是一个萌新富文本编辑器,基于js和css,重点在于它轻量,如果你需要的 ...

  8. vue项目 编辑器保存代码后自动更新浏览器页面内容

    第一步,现在用vue脚手架创建vue项目需要自己创建vue.config.js文件(注意,直接在项目创建vue.config.js文件即可),注意:(2019年10月后创建的新vue项目,之前的是we ...

  9. 【一个简单的vue公式编辑器组件】

    vue 一个简单的公式编辑器组件 示例 一个基于vue实现数据统计公式的基本功能. 新建一个 formula.vue 组件, 使用 <formula ref="formulaPage& ...

最新文章

  1. 这家剑桥校友创办的苏州AI独角兽,再获4.1亿投资,将在国内IPO
  2. ios 获得通讯录中联系人的所有属性 亲测,可行 兼容io6 和 ios 7
  3. 使用diskpart命令修复U盘分区
  4. 超级实用!如何为机器学习算法准备数据?
  5. 如何将两个虚拟机ping通?如何让虚拟机连网?
  6. 原创 | 为什么阿里巴巴建议开发者谨慎使用继承?
  7. python适合机器视觉_Python机器视觉编程常用数据结构与示例
  8. hive中导入csv,本地CSV导入hive表
  9. 《中国人工智能学会通讯》——1.13 总结与展望
  10. 给大家安利一个买电脑好去处(内有福利)
  11. php 字符串中 数组变量,PHP返回变量或数组的字符串表示:var_export()
  12. 理解C/C++运行时库
  13. 中彩分析家 打字软件
  14. python 区块链开发教程_区块链开发教程分享【201904】
  15. matlab由滤波的系数得到传输函数 设计带通滤波器 design fdatool设计IIR带通滤波器
  16. PPT文件压缩方法有哪些?
  17. 【剑指 Offer(专项突击版)前100题】
  18. 如何写好JAVA代码
  19. 泰兰德的记忆·悲情伊利丹
  20. 大规模机器学习在爱奇艺视频分析理解中的实践

热门文章

  1. 爬取微信朋友圈信息-可视化
  2. 洛克希德马丁可安装在卡车的小型核聚变反应堆10年内诞生
  3. 洛克希德·马丁定义的“杀伤链”
  4. 第5章 网站前台-活动与招聘
  5. Utopia unlimited: reassessing American literary utopias【翻译】
  6. layui实现导出全部数据Excel
  7. MDF智能合约靠谱吗?
  8. android 来电显示号码,android监控来电显示
  9. mybatis之(Oracle,MySql)批量更新
  10. Redis 集合高级用法