import { Component, Model, Prop, Vue, Watch } from 'vue-property-decorator'; import * as monaco from 'monaco-editor'; import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'; import 'monaco-editor/esm/vs/editor/contrib/find/findController.js'; import { CreateElement } from 'vue'; interface IToolbarItem { key: string; //标识 text: string; //文本值 component: any; //组件 visible: boolean; //显示状态 } @Component export class AppCodeEditor extends Vue { /** * 绑定值 * * @type {string} * @memberof AppCodeEditor */ @Model('change') value!: string; @Watch('value') onValueWatch() { this.codeEditor.setValue(this.value); } /** * 主题 * * @type {string} * @memberof AppCodeEditor */ @Prop({ type: String, default: 'vs-light' }) theme!: 'vs-light' | 'vs-dark'; /** * 语言 * * @type {string} * @memberof AppCodeEditor */ @Prop({ type: String, default: 'typescript' }) language!: 'json' | 'javascript' | 'typescript' | 'css' | 'less' | 'sass' | 'java' | 'yaml'; /** * 只读模式 * * @type {boolean} * @memberof AppCodeEditor */ @Prop({ type: Boolean, default: false }) readOnly!: boolean; /** * 是否显示小地图 * * @type {boolean} * @memberof AppCodeEditor */ @Prop({type: Boolean, default: true}) showMinimap!: boolean; /** * 是否显示行号 * * @type {boolean} * @memberof AppCodeEditor */ @Prop({type: Boolean, default: true}) showLineNum!: boolean; /** * 是否显示语言区 * * @type {boolean} * @memberof AppCodeEditor */ @Prop({type: Boolean, default: true}) showLanguage!: boolean; /** * 是否能够更换语言 * * @type {boolean} * @memberof AppCodeEditor */ @Prop({ type: Boolean, default: true }) changeLanguage!: boolean; /** * 当前使用语言 * * @type {string} * @memberof CodeEditor */ presentLanguage: string = ''; /** * 语言列表 * * @type {string[]} * @memberof AppCodeEditor */ languages: string[] = ['json', 'javascript', 'typescript', 'css', 'less', 'sass', 'java', 'sql', 'yaml']; /** * 代码编辑器对象 * * @type {*} * @memberof AppCodeEditor */ codeEditor: any; /** * 左侧工具栏项 * * @type {IToolbarItem[]} * @memberof AppCodeEditor */ leftToolbarItems: IToolbarItem[] = []; /** * 右侧工具栏项 * * @type {any[]} * @memberof AppCodeEditor */ rightToolbarItems: IToolbarItem[] = [ { key: 'fullScreen', text: '全屏', component: () => this.renderIcon('md-resize'), visible: true, }, { key: 'quitFullScreen', text: '退出全屏', component: () => this.renderIcon('md-contract'), visible: false, }, ]; /** * 绘制图标 * * @param {string} type * @return {*} * @memberof AppCodeEditor */ renderIcon(type: string) { return <icon type={type}></icon>; } /** * Vue生命周期,实例创建完成 * * @memberof AppCodeEditor */ created() { this.presentLanguage = this.language; this.registerEvent = this.registerEvent.bind(this); } /** * Vue生命周期,实例挂载完毕 * * @memberof AppCodeEditor */ mounted() { this.initCodeEditor(); } /** * 初始化编辑器 * * @memberof AppCodeEditor */ initCodeEditor() { const codeEditorRef = this.$refs.codeEditor; if (codeEditorRef) { this.codeEditor = monaco.editor.create(this.$refs.codeEditor as any, { value: this.value, theme: this.theme, language: this.presentLanguage, readOnly: this.readOnly, lineNumbers: this.showLineNum ? 'on' : 'off', minimap: { enabled: this.showMinimap }, }); this.registerEvent(); window.addEventListener('resize', this.resize); window.addEventListener('fullscreenchange', this.fullscreenchange); } } /** *注册事件 * * @memberof AppCodeEditor */ registerEvent() { if (!this.readOnly) { this.codeEditor.onDidBlurEditorText( //数据发生改变 (event: any) => { this.$emit('change', this.codeEditor.getValue()); this.$emit('blur', this.codeEditor.getValue()); }, ); this.codeEditor.onDidFocusEditorText( //数据发生改变 (event: any) => { this.$emit('focus', this.codeEditor.getValue()); }, ); } } /** * 重置编辑器大小 * * @memberof AppCodeEditor */ resize() { this.codeEditor.layout(); } /** * 全屏状态切换触发 * * @memberof AppCodeEditor */ fullscreenchange() { if (document.fullscreenElement) { const toolbarItem: any = this.rightToolbarItems.find(toolbarItem => toolbarItem.key === 'fullScreen'); toolbarItem.visible = false; const item: any = this.rightToolbarItems.find(toolbarItem => toolbarItem.key === 'quitFullScreen'); item.visible = true; } else { const toolbarItem: any = this.rightToolbarItems.find(toolbarItem => toolbarItem.key === 'quitFullScreen'); toolbarItem.visible = false; const item: any = this.rightToolbarItems.find(toolbarItem => toolbarItem.key === 'fullScreen'); item.visible = true; } } /** * 工具栏点击项 * * @param {IToolbarItem} toolbarItem * @memberof AppCodeEditor */ toolBarClick(toolbarItem: IToolbarItem) { if (toolbarItem.key === 'fullScreen') { const editorContainer = document.getElementsByClassName('app-code-editor')[0]; editorContainer.requestFullscreen(); } if (toolbarItem.key === 'quitFullScreen') { document.exitFullscreen(); } this.$forceUpdate(); } /** * 切换语言 * * @param {string} item * @memberof AppCodeEditor */ onLanguageChange(item: string) { this.codeEditor.dispose(); this.initCodeEditor(); this.codeEditor.trigger('anyString', 'editor.action.formatDocument'); this.$forceUpdate(); } /** * Vue实例销毁前 * * @memberof AppCodeEditor */ beforeDestroy() { window.removeEventListener('resize', this.resize); } /** * 绘制函数 * * @return {*} * @memberof AppCodeEditor */ render(h: CreateElement) { return ( <div class='app-code-editor'> <div class={{ 'app-code-editor__header': true, [this.theme]: true, 'app-code-editor__header--hidden': !this.showLanguage }}> <div class='app-code-editor__header__left__toolbar'> <i-select v-model={this.presentLanguage} disabled={!this.changeLanguage} on-on-change={this.onLanguageChange} > {this.languages.map((language: string) => { return ( <i-option value={language} key={language}> {language} </i-option> ); })} </i-select> </div> <div class='app-code-editor__header__right__toolbar'> {this.rightToolbarItems.map((toolbarItem: IToolbarItem) => { return toolbarItem.visible ? ( <div class='toolbar-item' title={toolbarItem.text} on-click={() => this.toolBarClick(toolbarItem)} > {toolbarItem.component()} </div> ) : null; })} </div> </div> <div class='app-code-editor__content' ref='codeEditor'></div> </div> ); } } // 默认导出 export default AppCodeEditor;