1.codemirror插件
相关网址
- Vue 官方插件库推荐的集成实现
- 这个实现做的比较全面,但不支持动态语法高亮的切换
- codemirror 支持的语言类型
- codemirror 官网
参考文章:
vue-codemirror 代码编辑器 - 简书
2.vue-codemirror插件
相关网址
1.vue-codemirror - npm
参考文章:
Vue(27)vue-codemirror实现在线代码编译器 _ - 腾讯云开发者社区-腾讯云
vue案例代码:
<!--这是文档界面-->
<template><div class="containner"><!-- 左边导航区 --><div class="nav"><!-- 头部搜索区 --><div class="nav_top"><div class="input"><el-input v-model="inputSeach" placeholder="请输入内容"></el-input></div><div class="img_seach"><imgsrc="../../assets/img/seach.png"@click="inputSeachClick"alt=""/></div></div><!-- 底部导航区 --><div class="nav_main"><el-menu:default-active="$route.hash"class="el-menu-vertical-demo"background-color="#181a1b"text-color="#fff"@open="showPage"active-text-color="#ffd04b"@select="handleSelect"router><el-submenu index="1"><template slot="title"><i class="el-icon-location"></i><span>入门示例</span></template><el-menu-item index="#1">场景初始化</el-menu-item><el-menu-item index="#2">数据挂接</el-menu-item><el-menu-item index="#3">组件高亮</el-menu-item><el-menu-item index="#4">光源控制</el-menu-item><el-menu-item index="#5">交互控制</el-menu-item><el-menu-item index="#6">测量相关</el-menu-item></el-submenu><!-- 导航2 --><el-submenu index="2"><template slot="title"><i class="el-icon-location"></i><span>待开发</span></template><el-menu-item index="#0">选项1</el-menu-item></el-submenu></el-menu></div></div><!-- 右边 --><div class="wrap"><!-- 代码区域编辑器 --><split-panesplit="vertical"@resize="resize":min-percent="5":default-percent="25"><template slot="paneL"><split-pane split="horizontal" :min-percent="5"><template slot="paneL"><div><!-- 编辑器头部按钮 --><div class="btns"><el-button type="primary" size="mini" @click="submitHtml">运行</el-button><el-button type="primary" size="mini" @click="submitBack">重置</el-button></div><!-- 编辑器 --><div class="edit"><codemirrorclass="code"v-model="code":options="cmOptions"></codemirror></div><!-- <pre class="pre">{{ code }}</pre> --></div></template><template slot="paneR"><div class="doc"><component :is="currentView"></component></div></template></split-pane></template><!-- 内嵌网页区域 --><template slot="paneR"><div id="iframewrapper"></div></template></split-pane></div></div>
</template><script>
// import 《组件名称》 from '《组件路径》';
// 引入的插件部分
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/keymap/sublime' // sublime编辑器效果
import 'codemirror/theme/dracula.css'// 配置里面也需要theme设置为monokai
import 'codemirror/mode/vue/vue.js' // 配置里面也需要mode设置为vue
import 'codemirror/addon/selection/active-line' // 光标行背景高亮,配置里面也需要styleActiveLine设置为true
import dedent from 'dedent'// 文档引入部分
import index_1_2 from '../apiShow/index_1_2.vue'
import index_1_3 from '../apiShow/index_1_3.vue'
import { log } from 'three'export default {// import引入的组件需要注入到对象中才能使用components: { codemirror, index_1_2, index_1_3 },data () {// 这里存放数据return {code: '',cmOptions: {tabSize: 4, // tab的空格个数theme: 'dracula', // 主题样式lineWrapping: true, // 是否自动换行styleActiveLine: true, // line选择是是否加亮matchBrackets: true, // 括号匹配mode: 'vue', // 实现javascript代码高亮readOnly: false, // 只读scrollbarStyle: 'native',},// 模糊搜索内容inputSeach: '',// 文档引入index_1_2: index_1_2,index_1_3: index_1_3,}},// 监听属性 类似于data概念computed: {currentView: function () {// if (this.$route.hash === '#1') {// return this.index_1_2// }if (this.$route.hash === '#2') {return this.index_1_3} else {return this.index_1_2}return false}},// 监控data中的数据变化watch: {},// 方法集合methods: {resize () { },// 保存submitHtml () {let text = this.codeconst patternHtml = /<html[^>]*>((.|[\n\r])*)<\/html>/imconst patternHead = /<head[^>]*>((.|[\n\r])*)<\/head>/imconst arrayMatchesHead = patternHead.exec(text)const patternBody = /<body[^>]*>((.|[\n\r])*)<\/body>/imconst arrayMatchesBody = patternBody.exec(text)if (arrayMatchesHead) {text = text.replace('<head>', '<head>')} else if (patternHtml) {text = text.replace('<html>', '<head>' + '</head>')} else if (arrayMatchesBody) {text = text.replace('<body>', '<body>')}// 动态创建iframeconst ifr = document.createElement('iframe')ifr.setAttribute('frameborder', '0')ifr.setAttribute('id', 'iframeResult')ifr.setAttribute('height', '100%')ifr.setAttribute('width', '100%')// 把创建的iframe追加到页面中document.getElementById('iframewrapper').innerHTML = ''document.getElementById('iframewrapper').appendChild(ifr)// 创建标签const style = document.createElement('style')style.innerHTML = '::-webkit-scrollbar {width: 2px;background-color: red;color: red;}'ifr.contentWindow.document.getElementsByTagName('head')[0].appendChild(style)const ifrw = (ifr.contentWindow) ? ifr.contentWindow : (ifr.contentDocument.document) ? ifr.contentDocument.document : ifr.contentDocument// 打开一个新的文档ifrw.document.open()// 编写文档ifrw.document.write(text)// 关闭文档操作ifrw.document.close()},submitCode (item) {if (item == 1) {this.code = dedent`
<!DOCTYPE html>
<html lang="en"></html>`} else if (item == 2) {this.code = dedent`
<!DOCTYPE html>
<html lang="en">
<body>
${'<\/script>'}
</body></html>`} else if (item == 3) {this.code = dedent`
<!DOCTYPE html>
<html lang="en"><head></head><body>${'<\/script>'}
</body></html>
`} else if (item == 4) {this.code = dedent`
<!DOCTYPE html>
<html lang="en"><head>
</head><body><script type="module">${'<\/script>'}
</body></html>
`} else if (item == 5) {this.code = dedent`
<!DOCTYPE html>
<html lang="en"></html>
`} else if (item == 6) {this.code = dedent`
<!DOCTYPE html>
<html lang="en"></html>
`} else {this.code = null;}this.submitHtml()},// 刷新submitBack () {// 根据刷新的地址请求html文本// 点击刷新,拿到当前hash,并去除井号const path = this.$route.hash.slice(1)this.submitCode(path);},// 模糊搜索inputSeachClick () {// 无输入停止if (this.inputSeach.length === 0) {return false}},// 打开就拿到key值showPage (key) {},getMutilines (data) {let content = new String(data);let start = content.indexOf('/*') + 3;let stop = content.lastIndexOf('*/');return content.substring(start, stop);},// 当选中的选项不同时,展示不同的内容,key子选项卡index的值,keyPath [复选项卡的index,子选项卡的index]handleSelect (key, keyPath) {// 如果key的值为空或者未定义 停止向下执行if (key === 'undefined' || key === '') return falsethis.submitCode(key.slice(1));}},getCode (id) {},// 生命周期 - 创建完成(可以访问当前this实例)created () {// 监听hash的变化const _this = thiswindow.addEventListener('hashchange', function () {_this.submitBack()}, false)},// 生命周期 - 挂载完成(可以访问DOM元素)mounted () {// 当页面渲染完成后加载this.$nextTick(function () {// 点击时,刷新展示最开始展示的文档this.submitBack()})},beforeCreate () { }, // 生命周期 - 创建之前beforeMount () { }, // 生命周期 - 挂载之前beforeUpdate () { }, // 生命周期 - 更新之前updated () { }, // 生命周期 - 更新之后beforeDestroy () { }, // 生命周期 - 销毁之前destroyed () { }, // 生命周期 - 销毁完成activated () { } // 如果页面有keep-alive缓存功能,这个函数会触发
}
</script>
<style lang='less' scoped>
//@import url(); 引入公共css类
.containner {display: flex;box-sizing: border-box;position: absolute;padding-top: 77px;width: 100%;height: 100%;min-width: 1600px;background-color: #181a1b;// border: 1px solid white;.nav {height: 100%;min-width: 240px;background-color: #181a1b;.el-menu {border: none;}/deep/.el-submenu__title {border-radius: 10px;margin: 10px;// border-bottom: 1px solid rgba(204, 204, 204, 0.7);}.nav_top {height: 30px;width: 85%;background-color: #3a3b3c;border-radius: 10px;display: flex;margin: 0 auto;margin-top: 2px;.input {width: 80%;z-index: 9999;/deep/.el-input__inner {height: 30px;padding: 0 10px;color: rgb(235, 235, 235);border: none;background-color: rgba(143, 143, 143, 0);}}.img_seach {flex: 1;display: flex;justify-content: center;align-items: center;z-index: 9999;img {width: 18px;height: 18px;display: block;}}}}/deep/.el-input__inner {border-radius: 0% !important;}.el-menu-item.is-active {background-color: #8d8d8d1a !important;border-radius: 20px;}// 编辑器部分.wrap {box-sizing: border-box;height: 100%;flex: 1;.top {height: 25px;width: 100%;}/deep/.splitter-pane-resizer {background-color: #959595;// opacity: 0.5;}.doc {box-sizing: border-box;width: 100%;height: 100%;// background-color: #181a1b6c;background: linear-gradient(to left,rgba(0, 0, 0, 0.247),#181a1bd3,#181a1b) !important;backdrop-filter: saturate(180%) blur(20px);color: rgb(56, 56, 56);padding: 15px;// 火狐隐藏滚动条overflow-y: scroll;scrollbar-color: transparent transparent;scrollbar-track-color: transparent;-ms-scrollbar-track-color: transparent;}}/deep/ .CodeMirror-code {margin: 10px;margin-top: 20px;margin-bottom: 500px;margin-left: 0;}/deep/ .el-menu-item {margin: 10px;border-radius: 20px;}/deep/.CodeMirror-lines {padding-left: 10px;}.btns {box-sizing: border-box;width: 100%;height: 36px;display: flex;justify-content: center;// background-color: #fff;// border-left: 1px solid #ccc;.el-button {width: 100%;height: 30px;border-radius: 10px;padding: 0;margin: 2px 20px;text-align: center;line-height: 20px;z-index: 9999;}.el-button--primary {background-color: #242526;border-color: #242526;}}.el-button--primary:hover {background-color: #4d4f52;border-color: #4d4f52;}.edit {height: 100%;box-sizing: border-box;.code {box-sizing: border-box;height: 800px;width: 100%;}}
}#iframewrapper {width: 98%;height: 99%;// min-width: 1300px;margin: 10px;margin-top: 0;box-sizing: border-box;border-radius: 20px;// border: 1px solid rgba(204, 204, 204, 0.7);background-color: #242526;
}
</style>// 全局样式
<style>
.CodeMirror {box-sizing: border-box;height: 100%;overflow-y: scroll;margin-top: 10px;margin-left: 10px;margin-right: 10px;border-radius: 10px;scrollbar-color: transparent transparent;scrollbar-track-color: transparent;-ms-scrollbar-track-color: transparent;
}
</style>