前端——代码编辑器

1.codemirror插件

相关网址

  1. Vue 官方插件库推荐的集成实现
    • 这个实现做的比较全面,但不支持动态语法高亮的切换
  2. codemirror 支持的语言类型
  3. 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">
          <img
            src="../../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-pane
        split="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">
                  <codemirror
                    class="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.code
      const patternHtml = /<html[^>]*>((.|[\n\r])*)<\/html>/im
      const patternHead = /<head[^>]*>((.|[\n\r])*)<\/head>/im
      const arrayMatchesHead = patternHead.exec(text)
      const patternBody = /<body[^>]*>((.|[\n\r])*)<\/body>/im

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

      // 动态创建iframe
      const 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 false
      this.submitCode(key.slice(1));
    }
  },
  getCode (id) {

  },
  // 生命周期 - 创建完成(可以访问当前this实例)
  created () {
    // 监听hash的变化
    const _this = this
    window.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>

猜你喜欢

转载自blog.csdn.net/helloyangkl/article/details/128494965