HtMonacoEditor.vue 6.74 KB
<template>
  <div class="code-editor-container" :style="{height: height}">
    <div v-if="showAll">
      <span
        :title="'全屏'"
        class="code-editor-fullscreen-icon"
        @click="fullVisible = true"
      >
        <i class="top-link-icon icon-quanpingbianji"></i>
      </span>
    </div>
    <div class="my-editor" ref="container"></div>

    <input
      v-show="false"
      :name="fieldName"
      v-model="value"
      v-validate="{required: required}"
    />
    <span v-if="hasErrors" class="field-tail__wrap"
      ><small class="el-form-item__error"> 必填 </small></span
    >
    <ht-sidebar-dialog
      width="100%"
      height="100%"
      :visible.sync="fullVisible"
      class="code-editor"
      append-to-body
    >
      <div class="code-editor-container code-editor-container-full">
        <div>
          <span
            :title="'退出全屏'"
            class="code-editor-fullscreen-icon"
            style="right: 20px"
            @click="fullVisible = false"
          >
            <i class="top-link-icon icon-restore"></i>
          </span>
        </div>
        <div class="my-editor1" ref="containerFull"></div>
      </div>
    </ht-sidebar-dialog>
  </div>
</template>

<script>
import * as monaco from 'monaco-editor'

export default {
  name: 'codeEditor',
  props: {
    value: {
      type: String,
      default: ''
    },
    language: {
      type: String,
      default: 'javascript' // shell、sql、python, javascript , java , json
    },
    readOnly: {
      type: Boolean,
      default: false
    },
    required: {
      type: Boolean,
      default: false
    },
    height: {
      type: String,
      default: '100%'
    },
    fieldName: {
      type: String,
      default: '脚本编辑器'
    },
    showAll: {
      type: Boolean,
      default: true
    },
    contextmenu: {
      //显示右键菜单
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      fullVisible: false,
      instanceMap: {}
    }
  },
  computed: {
    hasErrors: function() {
      if (
        this.required &&
        !this.value &&
        this.value !== 0 &&
        this.errors.items &&
        this.errors.items.filter(l => l.field == this.fieldName).length > 0
      ) {
        return true
      } else {
        return false
      }
    }
  },
  watch: {
    value: function(nVal, oVal) {
      this.setValue(this.value)
    },
    fullVisible: function(nVal, oVal) {
      this.$nextTick(() => {
        this.init()
      })
    }
  },
  mounted() {
    this.init()
  },
  created() {
    this.$validator = this.$root.$validator
  },
  methods: {
    init() {
      if (!this.getInstance()) {
        this.createInstance()
      } else {
        this.setValue(this.value)
      }
    },
    resized() {
      this.$nextTick(() => {
        this.getInstance() && this.getInstance().layout()
      })
    },
    getInstanceName() {
      return this.fullVisible ? 'containerFull' : 'container'
    },
    getInstance() {
      const containerName = this.getInstanceName()
      if (!this.instanceMap[containerName]) {
        return null
      }
      return this.instanceMap[containerName]
    },
    createInstance() {
      let this_ = this
      const containerName = this.getInstanceName()

      monaco.editor.defineTheme('my-vs-theme',{
        base:'vs',
        inherit:true,
        rules:[{background:'#f3f3f3'}],
        colors:{
          // 相关颜色属性配置
          'editor.background':'#f3f3f3', //编辑器的背景色
          // 'editor.foreground':'#ffffff',// 编辑器的前景色
          // "editor.lineHighlightBackground': "#0e0FF2" // 当前行的背景色
          'editor.selectionBackground': "#a6a6a6" // 选中文本的背景色
        }
      })
      // 初始化编辑器实例
      let monacoInstance = monaco.editor.create(this.$refs[containerName], {
        value: this.value,
        theme: 'my-vs-theme', // vs, hc-black, or vs-dark
        autoIndex: true,
        language: this.language,
        readOnly: this.readOnly,

        contextmenu: this.contextmenu,
      })

      if (this.language == 'json') {
        setTimeout(() => {
          monacoInstance.trigger('anyString', 'editor.action.formatDocument')
          monacoInstance.setValue(monacoInstance.getValue())
        }, 200);
      }


      //监测窗口变化
      window.addEventListener('resize', function() {
        this_.resized()
      })
      // 监听编辑器content变化事件
      monacoInstance.onDidChangeModelContent(() => {
        this.$emit('input', monacoInstance.getValue())
      })
      this.instanceMap[containerName] = monacoInstance
    },
    setValue(val) {
      if (!this.getInstance()) {
        return
      }
      const oldVal = this.getInstance().getValue()
      // 要更新的值和当前编辑器一致,则不再执行更新动作。只有当其他地方修改了编辑器的model值时,才更新
      if (val == oldVal) {
        return
      }
      let position = this.getInstance().getPosition()
      this.getInstance().setValue(val)
      // 原来的编辑器没有值,则不计算新内容的坐标和鼠标聚焦。因为不知道新内容的行数
      if (!oldVal || oldVal == ' ') {
        return
      }
      position.column = position.column + val.length - oldVal.length
      this.getInstance().setPosition(position)
    },
    insertValue(val) {
      // manacoEditor 为 实例对象
      // 1 获取光标位置
      let position = this.getInstance().getPosition()
      // 2 插入
      this.getInstance().executeEdits('', [
        {
          range: new monaco.Range(
            position.lineNumber,
            position.column,
            position.lineNumber,
            position.column
          ),
          text: val
        }
      ])
      // 3 设置新的光标位置
      position.column = position.column + val.length
      this.getInstance().setPosition(position)
      // 4 聚焦
      setTimeout(() => {
        this.getInstance().focus()
      }, 500)
    }
  }
}
</script>

<style lang="scss" scoped>
.code-editor-container-full {
  height: 100%;
}
.code-editor-container {
  width: 100%;
  position: relative;
  border: 1px solid #dcdcdb;
  .my-editor,
  .my-editor1 {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }
  .monaco-editor .scroll-decoration {
    box-shadow: none;
    // background: rgba(243,243,243,0.39);
  }


}

.code-editor /deep/ .el-dialog__header {
  display: none;
}

.code-editor /deep/ .el-dialog__body {
  padding: 5px;
  height: calc(100% - 10px);
}

.code-editor-fullscreen-icon {
  border: 1px solid #dcdee0;
  display: inline-block;
  width: 30px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  font-size: 20px;
  color: #666666;
  cursor: pointer;
  position: absolute;
  top: 10px;
  right: 20px;
  z-index: 9999;
}
</style>