monacoEditor在vue中使用
<template>
<!-- 编辑设备配置文件 -->
<!-- monacoEditor 编辑器 -->
<div class="edit_devices_profile">
<base-drawer
ref="baseDrawer"
width="100%"
:title="titleName"
@closeDialog="onCloseDialog"
:submitText="customSubmitText"
@submitForm="onSubmitDrawer"
>
<el-button
type="primary"
class="button left-button"
@click="
showDeviceList = true;
openList();
"
>选择矿卡</el-button
><br />
<div class="selected-devices">
已选择矿卡:<span>{{ localTruckNames.join(', ')||'无' }}</span>
</div>
<!-- 配置格式单选框 -->
<el-radio-group
v-model="selectedFormat"
class="config-format-radio-group"
>
<el-radio label="toml">TOML</el-radio>
<el-radio label="xml">XML</el-radio>
<el-radio label="TEXT">TEXT</el-radio>
</el-radio-group>
<div ref="monacoContainer" id="monacoContainer"></div>
<list-receiver
ref="listReceiver"
@selectedTrucksChanged="updateSelectedTruckNames"
></list-receiver>
</base-drawer>
<!-- codemirror 对比编辑器 -->
<base-dialog
ref="baseDialog"
title="内容比较"
width="1300px"
dialogStyle="mirrorDialog"
@submitForm="onSubmitDialog"
>
<div class="mirrorTitle">
<span class="title">当前值</span>
<span class="title">原始值</span>
</div>
<div id="mirrorEditor"></div>
</base-dialog>
</div>
</template>
<script>
import * as monaco from "monaco-editor";
import CodeMirror from "codemirror";
import "codemirror/theme/neat.css";
import "codemirror/lib/codemirror.css";
import "codemirror/addon/merge/merge.js";
import "codemirror/addon/merge/merge.css";
import DiffMatchPatch from "diff-match-patch";
const ListReceiver = () => import("../../components/devices/ListReceiver");
window.diff_match_patch = DiffMatchPatch;
window.DIFF_DELETE = -1;
window.DIFF_INSERT = 1;
window.DIFF_EQUAL = 0;
export default {
name: "EditDevicesProfile",
props: {
selectedTruckNames: {
type: Array,
default: () => [],
},
},
components: {
ListReceiver,
},
data() {
return {
truckName: "",
selectedDeviceNames: [],
localTruckNames: [...this.selectedTruckNames],
selectedDevices: [],
deviceList: [],
selectedFormat: "toml",
customSubmitText: "发布",
titleName: "", // 标题
monacoEditor: null, // 编辑器
newMonacoValue: null, // monacoEditor修改后的数据
form: {
id: null, // 行id
editorValue: null, // 最终发布时的数据
deviceList: [], // 矿卡id // TODO:多选后的矿卡
},
objTest: {
// 测试数据 TODO: 接口完成后删除
id: "19",
dataId: "1101",
group: "DEFAULT_GROUP",
// "content": "\r\n[[hmi]]\r\nname = \"hmi\"\r\nenable = true\r\nlib_path = \"libhmi.so\"\r\nclass_name = \"HmiComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[scenario]]\r\nname = \"scenario\"\r\nenable = true\r\nlib_path = \"libscenarios.so\"\r\nclass_name = \"ScenarioComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[map]]\r\nname = \"map\"\r\nenable = true\r\nlib_path = \"libhdmap.so\"\r\nclass_name = \"MapComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[hello_pub]]\r\nname = \"helloPub\"\r\nenable = true\r\nlib_path = \"libhelloPub_comp.so\"\r\nclass_name = \"HelloComponentPub\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[hello_sub]]\r\nname = \"helloSub\"\r\nenable = true\r\nlib_path = \"libhelloSub_comp.so\"\r\nclass_name = \"HelloComponentSub\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[sub_test]]\r\nname = \"sub_test\"\r\nenable = true\r\nlib_path = \"libPnc_test.so\"\r\nclass_name = \"Pnc_test\"\r\ntype = \"timer\"\r\ninterval = 100\r\n \r\n[[hello_comp]]\r\nname = \"helloComp\"\r\nenable = true\r\nlib_path = \"libhello_comp.so\"\r\nclass_name = \"HelloComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[lidar_comp]]\r\nname = \"LidarComp\"\r\nenable = true\r\nlib_path = \"liblidar.so\"\r\nclass_name = \"LidarComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[lidar_pub]]\r\nname = \"LidarComp\"\r\nenable = true\r\nlib_path = \"liblidar.so\"\r\nclass_name = \"LidarComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[lidar_sub]]\r\nname = \"LidarComp\"\r\nenable = true\r\nlib_path = \"liblidar.so\"\r\nclass_name = \"LidarComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[sensor]]\r\nname = \"gps\"\r\nenable = true\r\nlib_path = \"libgps.so\"\r\nclass_name = \"GpsComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[test_log]]\r\nname = \"helloComp\"\r\nenable = true\r\nlib_path = \"libtestglog_comp.so\"\r\nclass_name = \"TestComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[cloud]]\r\nname = \"cloud\"\r\nenable = true\r\nlib_path = \"libcloud.so\"\r\nclass_name = \"CloudComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[tnt]]\r\nname = \"cloud\"\r\nenable = true\r\nlib_path = \"libcloud.so\"\r\nclass_name = \"CloudComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[tnt]]\r\nname = \"hmi\"\r\nenable = true\r\nlib_path = \"libhmi.so\"\r\nclass_name = \"HmiComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[tnt]]\r\nname = \"scenario\"\r\nenable = true\r\nlib_path = \"libscenarios.so\"\r\nclass_name = \"ScenarioComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[tnt]]\r\nname = \"map\"\r\nenable = true\r\nlib_path = \"libhdmap.so\"\r\nclass_name = \"MapComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[rcd]]\r\nname = \"rcd\"\r\nenable = true\r\nlib_path = \"librcdclient.so\"\r\nclass_name = \"rcdClientComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[pnp]]\r\nname = \"planning\"\r\nenable = true\r\nlib_path = \"libplanning.so\"\r\nclass_name = \"PlanningComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[pnp]]\r\nname = \"prediction\"\r\nenable = true\r\nlib_path = \"libprediction.so\"\r\nclass_name = \"PredictionComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[cnc]]\r\nname = \"control\"\r\nenable = true\r\nlib_path = \"libcontrol.so\"\r\nclass_name = \"ControlComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[cnc]]\r\nname = \"can_adaptor\"\r\nenable = true\r\nlib_path = \"libcan_adaptor.so\"\r\nclass_name = \"CanAdaptorComponent\"\r\ntype = \"timer\"\r\ninterval = 50\r\n\r\n[[pnc]]\r\nname = \"planning\"\r\nenable = true\r\nlib_path = \"libplanning.so\"\r\nclass_name = \"PlanningComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[pnc]]\r\nname = \"control\"\r\nenable = true\r\nlib_path = \"libcontrol.so\"\r\nclass_name = \"ControlComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[pnc]]\r\nname = \"can_adaptor\"\r\nenable = true\r\nlib_path = \"libcan_adaptor.so\"\r\nclass_name = \"CanAdaptorComponent\"\r\ntype = \"timer\"\r\ninterval = 50\r\n\r\n\r\n[[pnc]]\r\nname = \"prediction\"\r\nenable = true\r\nlib_path = \"libprediction.so\"\r\nclass_name = \"PredictionComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n\r\n[[prediction]]\r\nname = \"prediction\"\r\nenable = true\r\nlib_path = \"libprediction.so\"\r\nclass_name = \"PredictionComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[can_adaptor]]\r\nname = \"can_adaptor\"\r\nenable = true\r\nlib_path = \"libcan_adaptor.so\"\r\nclass_name = \"CanAdaptorComponent\"\r\ntype = \"timer\"\r\ninterval = 50\r\n\r\n[[can]]\r\nname = \"can_adaptor\"\r\nenable = true\r\nlib_path = \"libcan_adaptor.so\"\r\nclass_name = \"CanAdaptorComponent\"\r\ntype = \"timer\"\r\ninterval = 50\r\n\r\n[[sim]]\r\nname = \"sim_platform\"\r\nenable = true\r\nlib_path = \"libsim_platform.so\"\r\nclass_name = \"BusinessManagerComponent\"\r\ntype = \"timer\"\r\ninterval = 50\r\n\r\n[[radar]]\r\nname = \"radar\"\r\nenable = true\r\nlib_path = \"libradar.so\"\r\nclass_name = \"RadarComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[csv_logger]]\r\nname = \"csv_logger\"\r\nenable = true\r\nlib_path = \"libcsv_logger.so\"\r\nclass_name = \"CsvLoggerComponent\"\r\ntype = \"timer\"\r\ninterval = 100\r\n\r\n[[lidar_back]]\r\nname = \"lidar_back\"\r\nenable = true\r\nlib_path = \"liblidar.so\"\r\nclass_name = \"LidarComponent\"\r\ntype = \"timer\"\r\ninterval = 1000\r\n\r\n[[diagnose]]\r\nname = \"diagnose\"\r\nenable = true\r\nlib_path = \"libdiagnose_service.so\"\r\nclass_name = \"DiagnoseServiceNode\"\r\ntype = \"timer\"\r\ninterval = 1000\r\n\r\n",
content:
'## general\r\npath_interval = 0.2\r\nplan_path_length = 80.0 # 规划路径长度,单位:m\r\npreview_dist = 15.0\r\nvehicle_width_expand_dist = 0.5 # 车辆宽度安全距离\r\nvehicle_length_expand_dist = 3.5 # 车辆长度安全距离\r\nenable_bussiness = false\r\nenable_diagnose = true # 故障诊断开关\r\nrecord_data_types = ["normal_data","planning_path","obstacles","vehicle_pose"] # 记录csv的数据内容\r\n # 常规周期性数据: "normal_data"\r\n # 规划路径: "planning_path"\r\n # 障碍物: "obstacles"\r\n # 车辆位姿: "vehicle_pose"\r\n\r\n\r\n## cloud-vehicle interaction\r\navoidance_apply_distance = 100.0\r\nmax_avoidance_length = 30.0\r\nmin_avoidance_index =10\r\n\r\n## parking\r\nparking_velocity = 5.0 # 泊车速度,单位:km/h\r\nmax_parking_search_time = 5.0 # 泊车规划最大时间,单位:second\r\nif_smooth = false\r\nplan_area_type = 0 # 泊车规划区域\r\nmax_search_num = 15\r\ndetection_offset = 1\r\n\r\n## drive_in\r\ndrive_in_strategy = ["HybridAStarRS","ReedShepp"]\r\nmin_forward_dist = 7.0 # 停车点最小前移距离,单位:m\r\nmax_forward_dist = 100.0 # 停车点最大前移距离,单位:m\r\nstep_forward_dist = 10.0 # 停车点前移步长,单位:m\r\nextend_straight_dist = 1.0 # 向前延长距离,单位:m\r\nextend_straight_dist_for_waiting_mode = 0.4 # 进入等待位模式后,向前延长距离,单位:m\r\nis_waiting_mode = false # 等待位模式\r\n\r\n# drive_out\r\ndrive_out_strategy = ["Dubins"]\r\nif_stitch_to_main_path = true\r\nmax_sample_dist = 100.0',
md5: "319430c2ca8749768b254ced462b9652",
encryptedDataKey: "",
tenant: "dev",
appName: "",
type: "toml",
createTime: 1735117388453,
modifyTime: 1735117388453,
createUser: "nacos",
createIp: "172.16.41.17",
desc: "测试toml文件",
use: null,
effect: null,
schema: null,
configTags: null,
},
};
},
mounted() {
this.fetchDeviceList();
this.initMonaco(); // 初始化编辑器语言及主题
},
watch: {
selectedTruckNames(newVal) {
this.localTruckNames = [...newVal]; // 监听props变化,更新本地副本
}
},
methods: {
openList(data) {
this.$refs.listReceiver.toggleDialog(true);
},
fetchDeviceList() {
// 假设有一个 API 调用获取矿卡列表
},
updateSelectedTruckNames(selectedTrucks) {
this.localTruckNames = selectedTrucks; // 修改本地变量
this.$emit('update:selectedTruckNames', selectedTrucks);
},
//
// 初始化编辑器
initMonaco() {
// 注册 TOML 语言
monaco.languages.register({
id: "toml",
extensions: [".toml"],
aliases: ["TOML"],
mimetypes: ["text/toml"],
});
// 设置属性高亮
monaco.languages.setMonarchTokensProvider("toml", {
tokenizer: {
root: [
[/^\[.*\]/, "delimiter"],
[/^[a-zA-Z_]\w*/, "identifier"],
[/=\s*/, "operator"],
[/".*?"/, "string"],
[/'.*?'/, "string"],
[/\s*#\s*(.*)$/, "comment"],
[/true|false/, "boolean"],
[/\d+\.\d+|\d+/, "number"],
],
},
});
// 设置属性颜色
monaco.editor.defineTheme("myTheme", {
base: "vs-dark",
inherit: true,
rules: [
{ token: "delimiter", foreground: "eee8aa" },
{ token: "identifier", foreground: "74b0df" },
{ token: "operator", foreground: "d4d4d4" },
{ token: "string", foreground: "ce9178" },
{ token: "comment", foreground: "608b4e" },
{ token: "boolean", foreground: "3dc9b0" },
{ token: "number", foreground: "b5cea8" },
],
colors: {
"editor.background": "#1e1e1e",
// "editor.foreground": "#d4d4d4",
// "editorCursor.foreground": "#d4d4d4",
// "editor.lineHighlightBackground": "#3c3c3c",
// "editorLineNumber.foreground": "#d4d4d4",
// "editor.selectionBackground": "#3c3c3c",
},
});
},
// 创建编辑器
createMonaco(data) {
this.monacoEditor = monaco.editor.create(this.$refs.monacoContainer, {
theme: "vs-dark", // 主题
value: this.objTest.content, // TODO: 列表传过来的值
language: this.objTest.type,
});
// 编辑器改变主题
monaco.editor.setTheme("myTheme");
let _this = this;
// 内容改变
this.monacoEditor.onDidChangeModelContent(function (e) {
_this.editorChange(e);
const changes = e.changes.map((change) => {
return {
range: change.range,
text: change.text,
};
});
});
},
// 编辑器改变
editorChange(event) {
this.newMonacoValue = this.monacoEditor.getValue();
// 更新表单数据
this.form.editorValue = this.newMonacoValue;
console.log('monaco编辑器改变',this.form.editorValue);
},
// 接收矿卡详情
showDetails(data) {
this.titleName = `配置文件 - ${data.fileNameDisplay}`;
this.drawerShow();
this.$nextTick(() => {
this.createMonaco(data);
});
},
// 关闭弹窗
onCloseDialog() {
console.log("关闭弹窗");
},
// 确认弹窗
async onSubmitDrawer(ev) {
console.log("弹窗确定", ev);
await this.$refs.baseDialog.toggleDialog();
this.showMirrorEditor();
},
// 展示/隐藏弹窗
drawerShow() {
this.$refs.baseDrawer.toggleDialog();
},
// codemirror
showMirrorEditor() {
// 创建 CodeMirror MergeView 实例
const mergeView = CodeMirror.MergeView(
document.getElementById("mirrorEditor"),
{
theme: "neat", // 主题
value: this.newMonacoValue || this.objTest.content, // 左侧内容
origRight: null,
orig: this.objTest.content, // 右侧内容
lineNumbers: true, // 显示行号
mode: this.objTest.type,
highlightDifferences: true, // 高亮
connect: "align",
readOnly: false, // 只读
scrollLock: true, // 锁定
}
);
// 监听改变
// 获取左侧编辑器实例
const leftEditor = mergeView.editor();
// 添加监听器以监听左侧编辑器内容的变化
leftEditor.on("change", (instance, change) => {
// 获取当前左侧编辑器的值
this.form.editorValue = instance.getValue();
const changeDetails = {
from: change.from,
to: change.to,
text: change.text,
};
});
},
onSubmitDialog() {
console.log("最终提交");
// 提交编辑器内容
this.submitEditorContent();
},
selectDevice(device) {
this.selectedDevices.push(device); // 假设存在一个数组存储选中的矿卡对象
this.selectedDeviceNames.push(device.name); // 将矿卡名称添加到显示数组中
this.showDeviceList = false; // 关闭矿卡选择列表
},
submitEditorContent() {
console.log("提交给后端的内容:");
//关闭弹窗
this.$refs.baseDialog.toggleDialog();
console.log(this.form);
},
},
beforeDestroy() {
if (this.monacoEditor) {
this.monacoEditor.dispose();
}
},
};
</script>
<style lang="scss">
.selected-devices {
margin: 15px 0;
color: #ffffff;
font-size: 14px;
}
.config-format-radio-group {
margin-bottom: 10px;
// margin-top:10px;
}
#monacoContainer {
width: 100%;
height: calc(100% - 30px);
margin-top: 10px;
overflow: hidden;
body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
pre,
code,
form,
fieldset,
legend,
input,
textarea,
p,
blockquote,
th,
td,
button {
font-family: Consolas, "Courier New", monospace;
}
}
.mirrorTitle {
height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: $whiteOpacity7;
text-align: center;
.title {
width: 47%;
}
}
.drawer-body-content-body {
overflow: hidden;
}
#mirrorEditor {
width: 100%;
height: calc(100% - 30px);
// overflow: hidden;
.CodeMirror-merge {
height: 100%;
.CodeMirror-merge-pane {
height: 100%;
.CodeMirror {
height: 100%;
}
}
}
}
.mirrorDialog {
height: 700px;
.dialog-body-content {
height: 100%;
overflow: hidden;
}
.config-format-radio-group {
margin-bottom: 10px;
margin-top: 10px;
}
}
</style>编辑数据