web在线编辑器(vue版)


前言

提示:这里可以添加本文要记录的大概内容:


提示:以下是本篇文章正文内容,下面案例可供参考

一、monaco-editor

“monaco-editor”: “^0.30.1”

点击进入api地址

1、编辑器源码

<template>
  <div ref="monacoEditor" id="monacoEditor" :style="style"></div>
</template>

<script setup>
import { nextTick, onMounted, ref, watch, onBeforeUnmount } from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
  modelValue: {},
  style: {},
  readOnly: {},
  language: {},
  content: {},
});

const emit = defineEmits(["update:modelValue"]);

const monacoEditor = ref();
let editor;

const init = () => {
  /**
   * @param wordWrap 自动换行,注意大小写
   */
  editor = monaco.editor.create(monacoEditor.value, {
    automaticLayout: true,
    value: props.modelValue,
    readOnly: props.readOnly,
    theme: "vs-dark",
    language: props.language,
    wordWrap: "on",
    wrappingIndent: "indent",
  });

  // 监听值的变化
  editor.onDidChangeModelContent(() => {
    emit("update:modelValue", editor.getValue());
  });
};
onMounted(() => {
  init();
});

watch(
  () => props.language,
  (nv, ov) => {
    monaco.editor.setModelLanguage(editor.getModel(), nv);
  }
);

function updateValue() {
  setTimeout(() => {
    editor.setValue(props.modelValue);
  }, 200);
}

watch(
  () => props.language,
  (newValue) => {
    monaco.editor.setModelLanguage(editor.getModel(), newValue);
  }
);
defineExpose({ updateValue });
</script>

<style></style>

<config-edit
     v-model="tempFlow.textareashow"
    :language="tempFlow.language"
     :readOnly="false"
          style="height: 100%; width: 100%"
></config-edit>

2、对比diff

在这里插入图片描述

ps:在比较过程中,支持在右侧的进行文件编辑。可通过界面顶部操作栏,查看当前修改个数(实时更改)。

当前修改处,可通过点击下一个,上一个跳转。当用户选择某行,当前修改处也会发生变化,

需要注意的是,如果当前选中行,不在修改的范围内,则显示0,点击跳转按钮,将跳转到离当前最近的一个修改,并且标识当前处于第几个修改

<template>
  <div class="monaco-diff-box">
    <div class="layout-start operate">
      <div style="width: 50%">{{ leftText }}</div>
      <div style="width: 10%">{{ rightText }}</div>
      <div class="layout-end" style="width: 40%">
        <span
          >发现&nbsp;<span class="config-diff-count">{{ diffCount }}</span
          >&nbsp;处修改</span
        >
        <span
          >,当前第&nbsp;<span class="config-diff-count">{{
            curDiffCount || 0
          }}</span
          >&nbsp;&nbsp;&nbsp;</span
        >
        <span
          title="上一个"
          v-debounce-click="
            () => {
              exeCommand('goPrevDiff');
            }
          "
          class="diffJump"
        >
          <el-icon><CaretLeft /></el-icon>
          上一个
        </span>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <span
          title="下一个"
          v-debounce-click="
            () => {
              exeCommand('goNextDiff');
            }
          "
          class="diffJump"
        >
          下一个
          <el-icon><CaretRight /></el-icon>
        </span>
      </div>
    </div>
    <div ref="monacoDiff" id="monacoDiff" :style="style"></div>
  </div>
</template>

<script setup>
import {
  nextTick,
  onMounted,
  ref,
  watch,
  onBeforeUnmount,
  reactive,
  computed,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
  newCode: {},
  oldCode: {},
  params: {},
  language: {},
});

let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");

const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
  return lineChangesDataList.value.length;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(1);
var editor = null;
var decorations = null;
var navi;
var lineChangesDataMap = new Map();

const init = () => {
  editor = monaco.editor.createDiffEditor(monacoDiff.value, {
    automaticLayout: true,
    readOnly: false,
    wordWrap: "on",
    theme: "vs",
    wrappingIndent: "indent",
    bracketPairColorization: true,
    diffCodeLens: true,
    experimental: {
      collapseUnchangedRegions: true, // 折叠未更改区域
    },
    enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
  });

  let originalModel = monaco.editor.createModel(props.oldCode, props.language);
  let modifiedModel = monaco.editor.createModel(props.newCode, props.language);

  editor.setModel({
    original: originalModel,
    modified: modifiedModel,
  });

  // decorations = editor.createDecorationsCollection([
  //   {
  //     range: new monaco.Range(3, 1, 3, 1),
  //     options: {
  //       isWholeLine: true,
  //       className: "myContentClass",
  //       glyphMarginClassName: "myGlyphMarginClass",
  //     },
  //   },
  // ]);

  navi = monaco.editor.createDiffNavigator(editor, {
    followsCaret: true,
    ignoreCharChanges: true,
  });
};
onMounted(() => {
  init();
  setTimeout(() => {
    editor.onDidUpdateDiff((val) => {
      lineChangesDataList.value = editor.getLineChanges();

      lineChangesDataList.value.forEach((item, index) => {
        if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
          lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
        } else {
          for (
            let i = item.modifiedStartLineNumber;
            i <= item.modifiedEndLineNumber;
            i++
          ) {
            lineChangesDataMap.set(i, index + 1);
          }
        }
      });
      curDiffCount.value = lineChangesDataMap.get(
        editor.getPosition().lineNumber
      );
    });
    lineChangesDataList.value = editor.getLineChanges();
    lineChangesDataList.value.forEach((item, index) => {
      if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
        lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
      } else {
        for (
          let i = item.modifiedStartLineNumber;
          i <= item.modifiedEndLineNumber;
          i++
        ) {
          lineChangesDataMap.set(i, index + 1);
        }
      }
    });

    editor.getModifiedEditor().onMouseDown((e) => {
      lineChangesDataList.value = editor.getLineChanges();

      lineChangesDataList.value.forEach((item, index) => {
        if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
          lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
        } else {
          for (
            let i = item.modifiedStartLineNumber;
            i <= item.modifiedEndLineNumber;
            i++
          ) {
            lineChangesDataMap.set(i, index + 1);
          }
        }
      });
      curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
    });

    editor.getOriginalEditor().onMouseDown((e) => {
      lineChangesDataList.value = editor.getLineChanges();

      lineChangesDataList.value.forEach((item, index) => {
        if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
          lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
        } else {
          for (
            let i = item.modifiedStartLineNumber;
            i <= item.modifiedEndLineNumber;
            i++
          ) {
            lineChangesDataMap.set(i, index + 1);
          }
        }
      });
      curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
    });
  }, 200);
});

/**
 * @desc 跳转修改处
 * @cmd string
 * goNextDiff 前进异步
 * goPrevDiff 后退一步
 */
const exeCommand = (cmd) => {
  let arr = Array.from(lineChangesDataMap.keys());
  let positionIndex;
  let lineNumber = editor.getPosition().lineNumber;
  let hasNewNum = false;

  if (arr.indexOf(lineNumber) == -1) {
    hasNewNum = true;
    arr.push(lineNumber);
    arr = arr.sort((a, b) => {
      return a - b;
    });
  }

  positionIndex = arr.indexOf(lineNumber);

  if (cmd === "goNextDiff") {
    setTimeout(() => {
      //下一步
      if (
        curDiffCount.value == lineChangesDataList.value.length ||
        (hasNewNum && arr[arr.length - 1] == lineNumber)
      ) {
        curDiffCount.value = 1;
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
      }
      navi.next();
    }, 300);
  } else if (cmd === "goPrevDiff") {
    setTimeout(() => {
      //上一步
      if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
        curDiffCount.value = lineChangesDataList.value.length;
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
      }
      nextTick(() => {
        navi.previous();
      });
    });
  }
};

/**
 * @desc 获取最新编辑数据
 */
const getValue = () => {
  return editor.getModel().modified.getValue();
}

watch(
  () => props.language,
  (nv, ov) => {
    // monaco.editor.setModelLanguage(editor.getModel(), nv);
  }
);

defineExpose({
  getValue,
});
</script>

<style lang="less">
.monaco-diff-box {
  height: 100%;
  .operate {
    height: 36px;
    .config-diff-count {
      color: #ff5722;
    }
    .diffJump {
      cursor: pointer;
    }
  }
  #monacoDiff {
    flex: 1;
    height: calc(100% - 36px);
    .monaco-editor .line-numbers.active-line-number {
      color: #ff5722;
    }
  }
}
</style>

2023/12/05
支持相同代码可折叠
这个需要升级monaco的版本
“monaco-editor”: “^0.44.0”
由于这个需要和webpack5才能兼容,所以不是webpack5的需要升级(vue-cli5)
升级manaco之后createDiffNavigator,被删除了,在这个地方化解决了createDiffNavigator被删除之后的,修改处跳转的问题

<template>
  <div class="monaco-diff-box">
    <div class="layout-start operate">
      <div style="width: 50%">{{ leftText }}</div>
      <div style="width: 10%">{{ rightText }}</div>
      <div class="layout-end" style="width: 40%">
        <span
          >发现&nbsp;<span class="config-diff-count">{{ diffCount }}</span
          >&nbsp;处修改</span
        >
        <span
          >,当前第&nbsp;<span class="config-diff-count">{{
            curDiffCount || 0
          }}</span
          >&nbsp;处&nbsp;&nbsp;</span
        >
        <span
          title="上一个"
          v-debounce-click="
            () => {
              exeCommand('goPrevDiff');
            }
          "
          class="diffJump"
        >
          <el-icon><CaretLeft /></el-icon>
          上一个
        </span>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <span
          title="下一个"
          v-debounce-click="
            () => {
              exeCommand('goNextDiff');
            }
          "
          class="diffJump"
        >
          下一个
          <el-icon><CaretRight /></el-icon>
        </span>
      </div>
    </div>
    <div ref="monacoDiff" id="monacoDiff" :style="style"></div>
  </div>
</template>

<script setup>
import {
  nextTick,
  onMounted,
  ref,
  watch,
  onBeforeUnmount,
  reactive,
  computed,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
  newCode: {},
  oldCode: {},
  params: {},
  language: {},
});

let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");

const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
  return lineChangesDataList.value.length;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(1);
var editor = null;
var lineChangesDataMap = new Map();
let monaco_languages = monaco.languages.getLanguages();

const init = () => {
  editor = monaco.editor.createDiffEditor(monacoDiff.value, {
    automaticLayout: true,
    readOnly: false,
    wordWrap: "on",
    theme: "vs",
    wrappingIndent: "indent",
    bracketPairColorization: true,
    diffCodeLens: true,
    ignoreTrimWhitespace: false, //是否忽略差异编辑器中的空格
    enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
    hideUnchangedRegions: {
      enabled: true,
    },
  });

  let id = "plaintext"; // 默认纯文本

  monaco_languages.forEach((language) => {
    language.extensions &&
      language.extensions.forEach((ex) => {
        if (props.language == ex.slice(1)) {
          id = language.id;
        }
      });
  });

  let originalModel = monaco.editor.createModel(props.oldCode, id);
  let modifiedModel = monaco.editor.createModel(props.newCode, id);

  editor.setModel({
    original: originalModel,
    modified: modifiedModel,
  });
};
onMounted(() => {
  init();
  setTimeout(() => {
    editor.onDidUpdateDiff((val) => {
      lineChangesDataList.value = editor.getLineChanges();

      lineChangesDataList.value.forEach((item, index) => {
        if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
          lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
        } else {
          for (
            let i = item.modifiedStartLineNumber;
            i <= item.modifiedEndLineNumber;
            i++
          ) {
            lineChangesDataMap.set(i, index + 1);
          }
        }
      });
      curDiffCount.value = lineChangesDataMap.get(
        editor.getPosition().lineNumber
      );
    });
    lineChangesDataList.value = editor.getLineChanges();
    editor.setPosition({
      column: 1,
      lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
    });
    editor
      .getModifiedEditor()
      .revealLineInCenter(
        lineChangesDataList.value[0].modifiedStartLineNumber,
        lineChangesDataList.value[0].modifiedEndLineNumber
      );
    lineChangesDataList.value.forEach((item, index) => {
      if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
        lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
      } else {
        for (
          let i = item.modifiedStartLineNumber;
          i <= item.modifiedEndLineNumber;
          i++
        ) {
          lineChangesDataMap.set(i, index + 1);
        }
      }
    });

    editor.getModifiedEditor().onMouseDown((e) => {
      if (e?.target?.position?.lineNumber) {
        lineChangesDataList.value = editor.getLineChanges();

        lineChangesDataList.value.forEach((item, index) => {
          if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
            lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
          } else {
            for (
              let i = item.modifiedStartLineNumber;
              i <= item.modifiedEndLineNumber;
              i++
            ) {
              lineChangesDataMap.set(i, index + 1);
            }
          }
        });
        curDiffCount.value = lineChangesDataMap.get(
          e.target.position.lineNumber
        );
      } else {
        curDiffCount.value = 0;
      }
    });

    editor.getOriginalEditor().onMouseDown((e) => {
      if (e?.target?.position?.lineNumber) {
        lineChangesDataList.value = editor.getLineChanges();

        lineChangesDataList.value.forEach((item, index) => {
          if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
            lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
          } else {
            for (
              let i = item.modifiedStartLineNumber;
              i <= item.modifiedEndLineNumber;
              i++
            ) {
              lineChangesDataMap.set(i, index + 1);
            }
          }
        });
        curDiffCount.value = lineChangesDataMap.get(
          e.target.position.lineNumber
        );
      } else {
        curDiffCount.value = 0;
      }
    });
  }, 500);
});

/**
 * @desc 跳转修改处
 * @cmd string
 * goNextDiff 前进异步
 * goPrevDiff 后退一步
 */
const exeCommand = (cmd) => {
  let arr = Array.from(lineChangesDataMap.keys());
  let positionIndex;
  let lineNumber = editor.getPosition().lineNumber;
  let hasNewNum = false;

  if (arr.indexOf(lineNumber) == -1) {
    hasNewNum = true;
    arr.push(lineNumber);
    arr = arr.sort((a, b) => {
      return a - b;
    });
  }

  positionIndex = arr.indexOf(lineNumber);

  if (cmd === "goNextDiff") {
    setTimeout(() => {
      //下一步
      if (
        curDiffCount.value == lineChangesDataList.value.length ||
        (hasNewNum && arr[arr.length - 1] == lineNumber)
      ) {
        curDiffCount.value = 1;
        editor
          .getModifiedEditor()
          .revealLineInCenter(
            lineChangesDataList.value[0].modifiedStartLineNumber,
            lineChangesDataList.value[0].modifiedEndLineNumber
          );
        editor.setPosition({
          column: 1,
          lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
        });
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
        editor
          .getModifiedEditor()
          .revealLineInCenter(arr[positionIndex + 1], arr[positionIndex + 1]);
        editor.setPosition({
          column: 1,
          lineNumber: arr[positionIndex + 1],
        });
      }
    }, 300);
  } else if (cmd === "goPrevDiff") {
    setTimeout(() => {
      //上一步
      if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
        let diffNum = lineChangesDataList.value.length;
        curDiffCount.value = diffNum;
        editor
          .getModifiedEditor()
          .revealLineInCenter(
            lineChangesDataList.value[diffNum - 1].modifiedStartLineNumber,
            lineChangesDataList.value[diffNum - 1].modifiedEndLineNumber
          );
        editor.setPosition({
          column: 1,
          lineNumber:
            lineChangesDataList.value[diffNum - 1].modifiedEndLineNumber,
        });
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
        editor
          .getModifiedEditor()
          .revealLineInCenter(arr[positionIndex - 1], arr[positionIndex - 1]);
        editor.setPosition({
          column: 1,
          lineNumber: arr[positionIndex - 1],
        });
      }
    });
  }
};

/**
 * @desc 获取最新编辑数据
 */
const getValue = () => {
  return editor.getModel().modified.getValue();
};

watch(
  () => props.language,
  (nv, ov) => {
    // monaco.editor.setModelLanguage(editor.getModel(), nv);
  }
);

defineExpose({
  getValue,
});
</script>

<style lang="less">
.monaco-diff-box {
  height: 100%;
  .operate {
    height: 36px;
    .config-diff-count {
      color: #ff5722;
    }
    .diffJump {
      cursor: pointer;
    }
  }
  #monacoDiff {
    flex: 1;
    height: calc(100% - 36px);
    .monaco-editor .line-numbers.active-line-number {
      color: #ff5722;
    }
  }
}
</style>

2024/04/12
修复格式校验,修改格式为正常之后,还是又报错标记返回

<template>
  <div class="monaco-diff-box">
    <div class="layout-start operate">
      <div style="width: 50%">{{ leftText }}</div>
      <div style="width: 10%">{{ rightText }}</div>
      <div class="layout-end" style="width: 40%">
        <span
          >发现&nbsp;<span class="config-diff-count">{{ diffCount }}</span
          >&nbsp;处修改</span
        >
        <span
          >,当前第&nbsp;<span class="config-diff-count">{{
            curDiffCount || 0
          }}</span
          >&nbsp;&nbsp;&nbsp;</span
        >
        <span
          title="上一个"
          v-debounce-click="
            () => {
              exeCommand('goPrevDiff');
            }
          "
          class="diffJump"
        >
          <el-icon><CaretLeft /></el-icon>
          上一个
        </span>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <span
          title="下一个"
          v-debounce-click="
            () => {
              exeCommand('goNextDiff');
            }
          "
          class="diffJump"
        >
          下一个
          <el-icon><CaretRight /></el-icon>
        </span>
      </div>
    </div>
    <div ref="monacoDiff" id="monacoDiff" :style="style"></div>
  </div>
</template>

<script setup>
import {
  nextTick,
  onMounted,
  ref,
  watch,
  onBeforeUnmount,
  reactive,
  computed,
  onUnmounted,
} from "vue";
import * as monaco from "monaco-editor";
const props = defineProps({
  newCode: {},
  oldCode: {},
  params: {},
  language: {},
});

let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");

let originalModel = "";
let modifiedModel = "";

const monacoDiff = ref();
const lineChangesDataList = ref([]);
// 当前一共有多少处修改
let diffCount = computed(() => {
  return lineChangesDataList.value?.length || 0;
});
// 当前cursor指示的几处修改
let curDiffCount = ref(0);
var editor = null;
var lineChangesDataMap = new Map();
let monaco_languages = monaco.languages.getLanguages();
let id = "plaintext"; // 默认纯文本

const init = async () => {
  editor = await monaco.editor.createDiffEditor(monacoDiff.value, {
    automaticLayout: true,
    readOnly: false,
    wordWrap: "on",
    theme: "vs",
    wrappingIndent: "indent",
    bracketPairColorization: true,
    diffCodeLens: true,
    ignoreTrimWhitespace: false, //是否忽略差异编辑器中的空格
    enableSplitViewResizing: false, // 允许用户调整差异编辑器拆分视图的大小。默认为 true。
    hideUnchangedRegions: {
      enabled: true,
    },
    diffWordWrap: "off",
  });

  monaco_languages.forEach((language) => {
    language.extensions &&
      language.extensions.forEach((ex) => {
        if (props.language == ex.slice(1)) {
          id = language.id;
        }
      });
  });

  // Set up the XML language
  monaco.languages.register({ id: "xml" });
  monaco.languages.setLanguageConfiguration("xml", {
    comments: {
      lineComment: "--",
      blockComment: ["--[[", "]]"],
    },
  });

  // Define the error checking function for XML
  const checkXMLSyntax = (code) => {
    let errorMessage, xmlDoc;
    // code for IE
    if (window.ActiveXObject) {
      xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
      xmlDoc.async = "false";
      xmlDoc.loadXML(xmlContent);

      if (xmlDoc.parseError.errorCode != 0) {
        errorMessage = "错误code: " + xmlDoc.parseError.errorCode + "\n";
        errorMessage = errorMessage + "错误原因: " + xmlDoc.parseError.reason;
        errorMessage = errorMessage + "错误位置: " + xmlDoc.parseError.line;
        return [
          {
            severity: monaco.MarkerSeverity.Error,
            message: errorMessage,
            startLineNumber: xmlDoc.parseError.line - 1,
            startColumn: 0,
            endLineNumber: xmlDoc.parseError.line - 1,
            endColumn: 1,
          },
        ];
      } else {
        return [];
      }
    } else if (document.implementation.createDocument) {
      const parser = new DOMParser();
      xmlDoc = parser.parseFromString(code, "text/xml");
      const error = xmlDoc.getElementsByTagName("parsererror");

      if (error.length > 0) {
        errorMessage = xmlDoc
          .getElementsByTagName("parsererror")[0]
          .getElementsByTagName("div")[0].innerText;
        let tmp = [];
        let keyRexExp = new RegExp("(line 0)", "gi");
        errorMessage.split(":").forEach(function (msg, index) {
          if (index == errorMessage.split(":").length - 1) {
            msg = msg.replace(keyRexExp, "");
            tmp.push(msg);
          } else {
            tmp.push(msg);
          }
        });

        errorMessage = tmp.join(":");
        let match = errorMessage.match(/line (\d+) at column (\d+)/);
        if (match) {
          let lineNumber = +match[1];
          let columnNumber = +match[2];
          return [
            {
              severity: monaco.MarkerSeverity.Error,
              message: errorMessage,
              startLineNumber: lineNumber,
              startColumn: columnNumber,
              endLineNumber: lineNumber,
              endColumn: columnNumber,
            },
          ];
        } else {
          return [];
        }
      } else {
        return [];
      }
    }
  };

  // Set up the YAML language
  monaco.languages.register({ id: "yaml" });
  monaco.languages.setLanguageConfiguration("yaml", {
    comments: {
      lineComment: "--",
      blockComment: ["--[[", "]]"],
    },
  });

  // Define the error checking function for YAML
  const checkYAMLSyntax = (code) => {
    try {
      yaml.load(code);
      if (yaml == null) {
      }
      return [];
    } catch (error) {
      return [
        {
          severity: monaco.MarkerSeverity.Error,
          message: error.message,
          startLineNumber: error.mark.line,
          startColumn: error.mark.column,
          endLineNumber: error.mark.line,
          endColumn: error.mark.column + 1,
        },
      ];
    }
  };

  // Set up the Lua language
  monaco.languages.register({ id: "lua" });
  monaco.languages.setLanguageConfiguration("lua", {
    comments: {
      lineComment: "--",
      blockComment: ["--[[", "]]"],
    },
  });

  // Define the error checking function
  function checkLuaSyntax(code) {
    try {
      luaparse.parse(code);
      return [];
    } catch (error) {
      return [
        {
          severity: monaco.MarkerSeverity.Error,
          message: error.message,
          startLineNumber: error.line,
          startColumn: error.column + 1,
          endLineNumber: error.line,
          endColumn: error.column + 2,
        },
      ];
    }
  }

  originalModel = await monaco.editor.createModel(props.oldCode, id);
  modifiedModel = await monaco.editor.createModel(props.newCode, id);

  editor.setModel({
    original: originalModel,
    modified: modifiedModel,
  });

  let markers = [];
  if (id === "xml") {
    markers = checkXMLSyntax(modifiedModel.getValue());
  } else if (id === "yaml") {
    markers = checkYAMLSyntax(modifiedModel.getValue());
  } else if (id === "lua") {
    markers = checkLuaSyntax(modifiedModel.getValue());
  }
  monaco.editor.setModelMarkers(modifiedModel, id, markers);

  modifiedModel.onDidChangeContent(() => {
    let markers = [];
    if (id === "xml") {
      markers = checkXMLSyntax(modifiedModel.getValue());
    } else if (id === "yaml") {
      markers = checkYAMLSyntax(modifiedModel.getValue());
    } else if (id === "lua") {
      markers = checkLuaSyntax(modifiedModel.getValue());
    }
    monaco.editor.setModelMarkers(modifiedModel, id, markers);
  });

  editor.onDidUpdateDiff((val) => {
    lineChangesDataList.value = editor.getLineChanges();
    if (lineChangesDataList.value?.length > 0) {
      lineChangesDataList.value.forEach((item, index) => {
        if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
          lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
        } else {
          for (
            let i = item.modifiedStartLineNumber;
            i <= item.modifiedEndLineNumber;
            i++
          ) {
            lineChangesDataMap.set(i, index + 1);
          }
        }
      });
      curDiffCount.value = lineChangesDataMap.get(
        editor.getPosition().lineNumber
      );
    }
  });
  lineChangesDataList.value = editor.getLineChanges();
  if (lineChangesDataList.value?.length > 0) {
    curDiffCount.value = 1;
    editor.setPosition({
      column: 1,
      lineNumber: lineChangesDataList.value[0].modifiedEndLineNumber,
    });
    editor
      .getModifiedEditor()
      .revealLineInCenter(
        lineChangesDataList.value[0].modifiedStartLineNumber,
        lineChangesDataList.value[0].modifiedEndLineNumber
      );
    lineChangesDataList.value.forEach((item, index) => {
      if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
        lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
      } else {
        for (
          let i = item.modifiedStartLineNumber;
          i <= item.modifiedEndLineNumber;
          i++
        ) {
          lineChangesDataMap.set(i, index + 1);
        }
      }
    });
  }
  editor.getModifiedEditor().onMouseDown((e) => {
    if (e?.target?.position?.lineNumber) {
      lineChangesDataList.value = editor.getLineChanges();
      if (lineChangesDataList.value?.length > 0) {
        lineChangesDataList.value.forEach((item, index) => {
          if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
            lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
          } else {
            for (
              let i = item.modifiedStartLineNumber;
              i <= item.modifiedEndLineNumber;
              i++
            ) {
              lineChangesDataMap.set(i, index + 1);
            }
          }
        });
      }
      curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
    } else {
      curDiffCount.value = 0;
    }
  });
  editor.getOriginalEditor().onMouseDown((e) => {
    if (e?.target?.position?.lineNumber) {
      lineChangesDataList.value = editor.getLineChanges();
      if (lineChangesDataList.value?.length > 0) {
        lineChangesDataList.value.forEach((item, index) => {
          if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
            lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
          } else {
            for (
              let i = item.modifiedStartLineNumber;
              i <= item.modifiedEndLineNumber;
              i++
            ) {
              lineChangesDataMap.set(i, index + 1);
            }
          }
        });
      }
      curDiffCount.value = lineChangesDataMap.get(e.target.position.lineNumber);
    } else {
      curDiffCount.value = 0;
    }
  });
};
onMounted(() => {
  init();
});

/**
 * @desc 跳转修改处
 * @cmd string
 * goNextDiff 前进异步
 * goPrevDiff 后退一步
 */
const exeCommand = (cmd) => {
  if (lineChangesDataList.value.length === 0) return;
  let arr = Array.from(lineChangesDataMap.keys());
  let positionIndex;
  let lineNumber = editor.getPosition().lineNumber;
  let hasNewNum = false;

  if (arr.indexOf(lineNumber) == -1) {
    hasNewNum = true;
    arr.push(lineNumber);
    arr = arr.sort((a, b) => {
      return a - b;
    });
  }

  positionIndex = arr.indexOf(lineNumber);

  if (cmd === "goNextDiff") {
    setTimeout(() => {
      //下一步
      if (
        curDiffCount.value == lineChangesDataList.value?.length ||
        (hasNewNum && arr[arr.length - 1] == lineNumber)
      ) {
        curDiffCount.value = 1;
        editor
          .getModifiedEditor()
          .revealLineInCenter(
            lineChangesDataList.value[0].modifiedStartLineNumber,
            lineChangesDataList.value[0].modifiedEndLineNumber
          );
        editor.setPosition({
          column: 1,
          lineNumber: lineChangesDataList.value[0]?.modifiedEndLineNumber,
        });
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex + 1]);
        editor
          .getModifiedEditor()
          .revealLineInCenter(arr[positionIndex + 1], arr[positionIndex + 1]);
        editor.setPosition({
          column: 1,
          lineNumber: arr[positionIndex + 1],
        });
      }
    }, 300);
  } else if (cmd === "goPrevDiff") {
    setTimeout(() => {
      //上一步
      if (curDiffCount.value == 1 || (hasNewNum && arr[0] == lineNumber)) {
        let diffNum = lineChangesDataList.value.length;
        curDiffCount.value = diffNum;
        editor
          .getModifiedEditor()
          .revealLineInCenter(
            lineChangesDataList.value[diffNum - 1]?.modifiedStartLineNumber,
            lineChangesDataList.value[diffNum - 1]?.modifiedEndLineNumber
          );
        editor.setPosition({
          column: 1,
          lineNumber:
            lineChangesDataList.value[diffNum - 1]?.modifiedEndLineNumber,
        });
      } else {
        curDiffCount.value = lineChangesDataMap.get(arr[positionIndex - 1]);
        editor
          .getModifiedEditor()
          .revealLineInCenter(arr[positionIndex - 1], arr[positionIndex - 1]);
        editor.setPosition({
          column: 1,
          lineNumber: arr[positionIndex - 1],
        });
      }
    });
  }
};

/**
 * @desc 获取最新编辑数据
 */
const getValue = () => {
  return editor.getModel().modified.getValue();
};

/**
 * @desc 判断当前的languageId
 */
function getLanguageId(curLanguage) {
  return id;
}

/**
 * @desc 设置编辑器的位置
 */
function setPosition(column, lineNumber) {
  editor.setPosition({
    column: column,
    lineNumber: lineNumber,
  });
  editor.getModifiedEditor().revealLineInCenter(lineNumber);

  lineChangesDataList.value = editor.getLineChanges();

  if (lineChangesDataList.value?.length > 0) {
    lineChangesDataList.value.forEach((item, index) => {
      if (item.modifiedEndLineNumber == item.modifiedStartLineNumber) {
        lineChangesDataMap.set(item.modifiedStartLineNumber, index + 1);
      } else {
        for (
          let i = item.modifiedStartLineNumber;
          i <= item.modifiedEndLineNumber;
          i++
        ) {
          lineChangesDataMap.set(i, index + 1);
        }
      }
    });
  }
  curDiffCount.value = lineChangesDataMap.get(lineNumber);
}

/**
 * @desc 获取标记的信息
 */
function getModelMarkers() {
  // setTimeout(() => {
  // console.log("editor.getModel().modified.", editor.getModel().modified.getModelMarkers())
  console.log("modifiedModel.uri", modifiedModel.uri);
  console.log(
    "monaco.editor.getModelMarkers();",
    monaco.editor.getModelMarkers(modifiedModel.uri)
  );

  console.log(
    "1111111111111",
    monaco.editor.getModelMarkers({
      resource: modifiedModel.uri,
    })
  );
  return monaco.editor.getModelMarkers({
    resource: modifiedModel.uri,
  });

  // })
}

/**
 * @desc monaco注销
 */
function dispose() {
  editor.dispose();
}

defineExpose({
  getValue,
  getLanguageId,
  setPosition,
  getModelMarkers,
  dispose,
});
/**
 * @desc 清理缓存
 */
onUnmounted(() => {});
</script>

<style lang="less">
.monaco-diff-box {
  height: 100%;
  .operate {
    height: 36px;
    .config-diff-count {
      color: #ff5722;
    }
    .diffJump {
      cursor: pointer;
    }
  }
  #monacoDiff {
    flex: 1;
    height: calc(100% - 36px);
    .monaco-editor .line-numbers.active-line-number {
      color: #ff5722;
    }
  }
}
</style>

2、体积优化

待续…

二、ace-editor?

1、源码

代码如下(示例):

<template>
  <v-ace-editor
    v-model:value="modelValue"
    @init="init"
    lang="json"
    :theme="theme"
    :options="options"
    :readonly="readonly"
    :style="style"
    />
</template>

<script setup>
import { VAceEditor } from "vue3-ace-editor";
import "ace-builds/webpack-resolver";
import "ace-builds/src-noconflict/mode-json";
import "ace-builds/src-noconflict/theme-chrome";
import "ace-builds/src-noconflict/ext-language_tools";

const props = defineProps({
    modelValue: {},
    theme: {},
    options: {},
    readonly: {},
    //自定义行内样式
    style: {},
})

</script>

<style>

</style>
<ace-editor
  v-model:value="tempFlow.textareashow"
  @init="initFail"
  lang="json"
  :theme="aceConfig.theme"
  :options="aceConfig.options"
  :readonly="aceConfig.readOnly"
  class="ace-editor"
/>

2、体积优化

待续…

3、codemirror

1、diff

在这里插入图片描述

<template>
  <div class="config-diff">
    <div class="config-diff-title">
      <div class="layout-start">
        <div style="width: 50%">{{ leftText }}</div>
        <div style="width: 10%">{{ rightText }}</div>
        <div class="layout-end" style="width: 40%">
          <span
            >发现&nbsp;<span class="config-diff-count">{{ diffCount }}</span
            >&nbsp;处修改</span
          >
          <span
            >,当前第&nbsp;<span class="config-diff-count">{{
              curDiffCount
            }}</span
            >&nbsp;</span
          >
          <span
            title="上一个"
            v-debounce-click="
              () => {
                exeCommand('goPrevDiff');
              }
            "
            class="diffJump"
          >
            <el-icon><CaretLeft /></el-icon>
            上一个
          </span>
          &nbsp;&nbsp;&nbsp;&nbsp;
          <span
            title="下一个"
            v-debounce-click="
              () => {
                exeCommand('goNextDiff');
              }
            "
            class="diffJump"
          >
            下一个
            <el-icon><CaretRight /></el-icon>
          </span>
        </div>
      </div>
    </div>
    <div class="merge-view-main">
      <div class="file-editor" id="config-diff-view" ref="configDiffView"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, getCurrentInstance, onMounted, watch } from "vue";
import _ from "lodash";
// 引入全局实例
import CodeMirror from "codemirror";
// 核心样式
import "codemirror/lib/codemirror.css";
// 引入主题后还需要在 options 中指定主题才会生效
import "codemirror/theme/idea.css";

// 需要引入具体的语法高亮库才会有对应的语法高亮效果
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/css/css.js";
import "codemirror/mode/xml/xml.js";
import "codemirror/mode/shell/shell.js";
import "codemirror/mode/sql/sql.js";

//代码补全提示
import "codemirror/addon/hint/anyword-hint.js";
import "codemirror/addon/hint/css-hint.js";
import "codemirror/addon/hint/html-hint.js";
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/sql-hint.js";
import "codemirror/addon/hint/xml-hint.js";

//代码版本差异比较
import "codemirror/addon/merge/merge.js";
import "codemirror/addon/merge/merge.css";
import DiffMatchPatch from "diff-match-patch";

window.diff_match_patch = DiffMatchPatch;
window.DIFF_DELETE = -1;
window.DIFF_INSERT = 1;
window.DIFF_EQUAL = 0;

const props = defineProps({
  newCode: {},
  oldCode: {},
  params: {},
});

const { proxy } = getCurrentInstance();

//文件后缀和codemirror模式
const FILE_POSTFIX = {
  xml: "application/xml",
  json: "application/json",
  txt: "text/html",
  lua: "text/x-lua",
  py: "text/x-python",
  js: "text/javascript",
  sh: "text/x-sh",

  other: "text/x-textile",
};

let configDiffView = ref(null);

var dv;
// 当前一共有多少处修改
let diffCount = ref(0);

// 当前cursor指示的几处修改
let curDiffCount = ref(0);

let newCode = "";
let oldCode = "";

let leftText = ref(props.params.leftText || "修改前");
let rightText = ref(props.params.rightText || "修改后");

const postfix = proxy.$commonSvc.getFilePostfix(props.params.name);
const mode = FILE_POSTFIX[postfix] || FILE_POSTFIX.other;

function initUI(target) {
  if (oldCode == null) return;

  target.innerHTML = "";
  dv = CodeMirror.MergeView(target, {
    value: newCode,
    origLeft: oldCode,
    orig: null,
    lineNumbers: true, //显示行号
    lineWrapping: true,
    readOnly: false,
    mode: mode,
    autofocus: true,
    foldGutter: true, //代码折叠
    highlightDifferences: "highlight", //有差异的地方是否高亮
    collapseIdentical: true,
    connect: null,
  });

  setTimeout(function () {
    dv.editor().refresh();
    dv.leftOriginal() && dv.leftOriginal().refresh();
  }, 500);

  var diffs = dv.leftChunks();
  diffCount.value = (diffs && diffs.length) || 0;
  curDiffCount.value = 0;
}

/**
 * @desc exeCommand 执行codemirror mergeview的命令类型
 * @param {*} cmd
 * 前进一步(goNextDiff) or
 * 后退异步(goPrevDiff)
 */
const exeCommand = (cmd) => {
  if (diffCount.value == 0) {
    return;
  } else {
    return new Promise((resolve, reject) => {
      if (dv) {
        if (cmd === "goNextDiff") {
          //下一步
          if (curDiffCount.value < diffCount.value) {
            ++curDiffCount.value;
          } else {
            curDiffCount.value = 1;
          }
        } else if (cmd === "goPrevDiff") {
          //上一步
          if (curDiffCount.value > 1) {
            curDiffCount.value--;
          } else {
            curDiffCount.value = diffCount.value;
          }
        }

        if (curDiffCount.value === 0) return;
        // 延时,等待diffView刷新后,在定位,存在一定的误差,主要在于不确定diffView刷新需要的时间
        setTimeout(function () {
          proxy.$commonSvc.highlightCodeMirror(
            configDiffView.value,
            curDiffCount.value
          );
        }, 300);
      }
    });
  }
};

/**
 * @desc getMergeConfig
 */
const getMergeConfig = () => {
  if (!dv) {
    return;
  }

  var edit = dv.edit;

  if (!edit) {
    return;
  }

  return edit.getValue();
};

onMounted(() => {
  newCode = props.newCode;
  oldCode = props.oldCode;
  var target = configDiffView.value;
  initUI(target);
});

watch(
  () => [props.newCode, props.params],
  (newValue) => {
    newCode = props.newCode;
    oldCode = props.oldCode;
    var target = configDiffView.value;
    initUI(target);
  },
  {
    deep: true,
  }
);

defineExpose({
  getMergeConfig,
});
</script>

<style lang="less" scoped>
.config-diff {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0 16px;

  .config-diff-title {
    height: 36px;

    .config-diff-count {
      color: #ff5722;
    }
  }

  .merge-view-main {
    flex: 1;

    .file-editor {
      height: 600px;

      .CodeMirror-merge,
      .CodeMirror {
        z-index: 88888;
        height: 520px !important;
      }
    }

    .light-cursor {
      background: #b390f7;
    }
  }

  @media (min-width: 760px) {
    .merge-view-main {
      .file-editor {
        .CodeMirror-merge,
        .CodeMirror {
          height: 290px !important;
        }
      }
    }
  }

  @media (min-width: 1440px) {
    .merge-view-main {
      .file-editor {
        .CodeMirror-merge,
        .CodeMirror {
          height: 520px !important;
        }
      }
    }
  }

  .dev-test {
    .cm-s-blackboard.CodeMirror,
    .CodeMirror-merge-copybuttons-right,
    svg,
    .CodeMirror-merge-2pane .CodeMirror-merge-gap {
      height: 100%;
    }
  }

  .merge-event {
    .CodeMirror-merge-copy {
      pointer-events: auto !important;
    }
  }
}
.diffJump {
  cursor: pointer;
}
</style>


  /**
   * @desc codemirror merge next和pre 高亮
   * @param codeWrap {{string|Dom Element}} 需要检查的code所属的一个dom元素,目的缩小查询范围
   *  @param lineNumber {{number}} 需要高亮的行
   */
  highlightCodeMirror(codeWrap, curDiffCount) {
    if (!codeWrap) {
      return false;
    }

    var lines_left = $(".CodeMirror-merge-left", $(codeWrap))
      .find(".CodeMirror-linebackground")
      .get();
    var lines_right = $(".CodeMirror-merge-editor", $(codeWrap))
      .find(".CodeMirror-linebackground")
      .get();
    var merge_left = $(".CodeMirror-merge-left", $(codeWrap))
      .find(".CodeMirror-merge-l-chunk")
      .get();
    var merge_right = $(".CodeMirror-merge-editor", $(codeWrap))
      .find(".CodeMirror-merge-l-chunk")
      .get();
    var lineNumber_left;
    var lineNumber_right;
    var merge_left_end = $(".CodeMirror-merge-left", $(codeWrap)).find(
      ".CodeMirror-merge-l-chunk-end"
    );
    var merge_right_end = $(".CodeMirror-merge-editor", $(codeWrap)).find(
      ".CodeMirror-merge-l-chunk-end"
    );
    var lineNumber_left_end = merge_left_end
      .eq(curDiffCount - 1)
      .parent()
      .find(".CodeMirror-linenumber")
      .text();
    var lineNumber_right_end = merge_right_end
      .eq(curDiffCount - 1)
      .parent()
      .find(".CodeMirror-linenumber")
      .text();

    /**
     * @desc 定位每次修改高亮的起始和结束位置
     */
    for (var i = lines_left.length - 1; i >= 0; i--) {
      var temp = $(lines_left[i]).parent().find(".CodeMirror-linenumber");
      var value = parseInt($(temp).text());
      var value_end = parseInt(lineNumber_left_end);

      if (value <= value_end) {
        if (
          value < value_end &&
          $(lines_left[i]).attr("class").indexOf("-end") != -1
        ) {
          break;
        }

        if ($(lines_left[i]).attr("class").indexOf("-start") != -1) {
          lineNumber_left = $(temp).text();
        }
      }
    }

    for (var i = lines_right.length - 1; i >= 0; i--) {
      var temp = $(lines_right[i]).parent().find(".CodeMirror-linenumber");
      var value = parseInt($(temp).text());
      var value_end = parseInt(lineNumber_right_end);

      if (value <= value_end) {
        if (
          value < value_end &&
          $(lines_right[i]).attr("class").indexOf("-end") != -1
        ) {
          break;
        }

        if ($(lines_right[i]).attr("class").indexOf("-start") != -1) {
          lineNumber_right = $(temp).text();
        }
      }
    }

    /**
     * @desc 注入样式
     */
    merge_left.forEach(function (item) {
      var temp = $(item).parent().find(".CodeMirror-linenumber");

      if (
        lineNumber_left &&
        parseInt($(temp).text()) >= parseInt(lineNumber_left) &&
        parseInt($(temp).text()) <= parseInt(lineNumber_left_end)
      ) {
        $(item).addClass("light-cursor");
      } else {
        $(item).removeClass("light-cursor");
      }
    });

    merge_right.forEach(function (item) {
      var temp = $(item).parent().find(".CodeMirror-linenumber");

      if (
        lineNumber_right &&
        parseInt($(temp).text()) >= parseInt(lineNumber_right) &&
        parseInt($(temp).text()) <= parseInt(lineNumber_right_end)
      ) {
        $(item).addClass("light-cursor");
      } else {
        $(item).removeClass("light-cursor");
      }
    });
  },

总结

提示:这里对文章进行总结:

记录web在线编辑器的~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值