image.png
1.添加的组件必须全局注册,此处以一个自定义封装的代码编辑器为demo
// 自定义代码编辑器组件
//src\components\monacoEditor\index.vue
<template>
<div class="full flex flex-column">
<div class="toolbar flex align-center">
<el-tooltip class="item" effect="dark" content="格式化" placement="top-start">
<span class="format" @click.stop="handleFormat"> { } </span>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="全屏" placement="top-start">
<span class="screen" @click.stop="handleScreen">
<i class="el-icon-full-screen"></i>
</span>
</el-tooltip>
</div>
<div class="flex-1 mt10 main" ref="main"></div>
</div>
</template>
<script>
import loadMonaco from '@/utils/edit/loadMonaco';
import beautify from 'js-beautify';
export default {
model: {
prop: 'value',
event: 'onchange'
},
props: {
value: {
type: String,
default: ''
},
config: [Object]
},
data() {
return {
code: '',
monacoEditor: null,
fullscreen: false,
configType: {
html: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'separate',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: false,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
},
css: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'separate',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: false,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
},
js: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'normal',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: true,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
}
}
};
},
mounted() {
this.init();
},
watch: {
value(val) {
if (this.monacoEditor.getValue() !== val) {
this.monacoEditor.setValue(val);
}
}
},
methods: {
init() {
loadMonaco(monaco => {
this.monacoEditor = monaco.editor.create(this.$refs.main, {
theme: 'vs-dark', // 主题
value: this.value, // 默认显示的值
language: this.config.language,
folding: true, // 是否折叠
minimap: {
enabled: false // 是否启用预览图
}, // 预览图设置
foldingHighlight: true, // 折叠等高线
foldingStrategy: 'indentation', // 折叠方式 auto | indentation
showFoldingControls: 'always', // 是否一直显示折叠 always | mouseover
disableLayerHinting: true, // 等宽优化
emptySelectionClipboard: false, // 空选择剪切板
selectionClipboard: false, // 选择剪切板
automaticLayout: true, // 自动布局
codeLens: true, // 代码镜头
scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
colorDecorators: true, // 颜色装饰器
accessibilitySupport: 'on', // 辅助功能支持 "auto" | "off" | "on"
lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
lineNumbersMinChars: 5, // 行号最小字符 number
enableSplitViewResizing: false,
overviewRulerBorder: false, // 是否应围绕概览标尺绘制边框
renderLineHighlight: 'gutter', // 当前行突出显示方式
readOnly: false //是否只读 取值 true | false
});
this.monacoEditor.onDidChangeModelContent(e => {
let val = this.monacoEditor.getValue();
this.$emit('onchange', val);
this.$emit('getValue', val);
});
});
},
handleScreen() {
let element = this.$refs.main;
if (this.fullscreen) {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
} else {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullScreen) {
element.webkitRequestFullScreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}
this.fullscreen = !this.fullscreen;
},
handleFormat() {
const code = this.monacoEditor.getValue();
let type = undefined;
let newCode = undefined;
if (this.config.language === 'javascript') {
type = this.configType.js;
newCode = beautify.js(code, type);
} else if (this.config.language === 'css') {
type = this.configType.css;
newCode = beautify.css(code, type);
} else {
type = this.configType.html;
newCode = beautify.html(code, type);
}
this.monacoEditor.setValue(newCode);
}
},
beforeDestroy() {
this.monacoEditor?.dispose();
}
};
</script>
<style lang="scss" scoped>
.full {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.screen,
.format {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
cursor: pointer;
font-size: 20px;
}
.format {
float: left;
font-size: 18px;
margin: 7px 10px 0;
}
.el-icon-full-screen {
float: left;
margin-top: 5px;
}
.main {
min-height: 0;
&::v-deep {
.monaco-editor {
height: 100% !important;
}
}
}
.toolbar {
position: absolute;
right: 15px;
top: 10px;
color: #fff;
z-index: 2;
}
</style>
main.js
// 全局注册富文本组件
import monacoEditor from '@/components/monacoEditor';
Vue.component('monacoEditor', monacoEditor);
2.在表单生成器的组件config内添加这个组件
src\components\generator\config.js
{
__config__: {
label: '代码编辑器',
showLabel: true,
changeTag: true,
labelWidth: null,
tag: 'monacoEditor',
tagIcon: 'button',
span: 24,
layout: 'colFormItem',
document: '#'
},
__slot__: {
default: ''
},
style: { height: '300px' },
config: { language: "javascript"},
on: {
'getValue':'setEditorValue'
}
}
注意:这里添加了3个属性,style,config,on
- style用于调整编辑器默认高度
- config是组件的其余配置,详细选项见富文本编辑器文档,此处仅设置编辑器语言为js
- on是该编辑器自定义事件的监听
对比编辑器正常使用方式如下
<editor :config="editorConfig" :value="form.eventCode" @getValue="getValue" />
此处的value表单生成器会默认赋值
3.为表单生成器添加事件监听与处理。
正常开发流程我们组件输入的时候会触发组件内的 this.$emit('getValue', val);
引用组件的父组件需要响应子组件上的@getValue方法,调用自身的getValue方法处理里面的逻辑
转换成代码生成器内的流程就是
编辑器组件输入内容=>触发this.$emit('getValue', val)=> 子组件监听getValue事件=>父组件处理getValue事件setEditorValue
子组件监听getValue事件
//src\components\render\render.js
function emitEvents(confClone) {
['on', 'nativeOn'].forEach(attr => {
const eventKeyList = Object.keys(confClone[attr] || {})
eventKeyList.forEach(key => {
const val = confClone[attr][key]
if (typeof val === 'string') {
// 代码编辑器自定义事件注册
// 将getValue的事件指向我们定义的setEditorValue去
// confClone['on']['getValue'] = event => this.$emit('setEditorValue', event)
confClone[attr][key] = event => this.$emit(val, event)
}
})
})
}
//src\views\tool\build\pageview\formParse\Parser.vue
function buildListeners(scheme) {
const config = scheme.__config__;
console.log('scheme', scheme);
let listeners = {};
// 自定义事件插入
let column = this.compData.cols.find(item => item.columName === scheme.columName);
let _event = null;
if (column && column.__event__) {
_event = deepClone(column.__event__);
Object.keys(_event).forEach(key => {
listeners[key] = event => _event[key].call(this, event);
});
}
['on', 'nativeOn'].forEach(attr => {
const eventKeyList = Object.keys(scheme[attr] || {});
eventKeyList.forEach(key => {
const val = scheme[attr][key];
if (typeof val === 'string') {
// 代码编辑器自定义事件注册
// listeners['setEditorValue'] = this['setEditorValue'].bind(this);
listeners[val] = this[val].bind(this, scheme);
}
});
});
// 响应 render.js 中的 vModel $emit('input', val)
listeners.input = event => {
setValue.call(this, event, config, scheme);
if (_event && _event.input) _event.input.call(this);
};
console.log('listeners', listeners);
return listeners;
}
// 调用了这个自定义方法
setEditorValue(conf,e) {
console.log('getValue',conf, e, this);;
this.formData[conf.__vModel__] = e
}
//此时相当于渲染了了一个这样的组件
<editor :config="editorConfig" @getValue="setEditorValue" />