console
import pretty from 'https://cdn.jsdelivr.net/npm/pretty@2.0.0/+esm'
const { log, dir, table, clear, warn, error } = console; clear();
const btnSave = document.querySelector('.btnSave');
const pop = document.querySelector('.popup');
const coder = CodeMirror.fromTextArea(document.querySelector("#coder"), {
lineNumbers: true,
lineWrapping: false,
indentWithTabs: true,
mode: "htmlmixed",
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
lint: true,
tabSize: 2,
theme: "default",
cursorHeight: 1,
autoCloseTags: true,
});
const editor = tinymce.init({
selector: "#editor",
language:'zh_CN',
promotion: false,
branding: false,
license_key: 'gpl',
plugins: 'code',
toolbar: 'coder | fontsize bold italic alignleft aligncenter alignright alignjustify lineheight | forecolor backcolor',
menubar: '',
width: '100%',
height: '100%',
valid_children : '+body[style]',
valid_elements: '+*[*]',
inline_styles: true,
keep_styles: true,
extended_valid_elements: '+*[*]',
custom_elements: '*',
invalid_elements: '',
verify_html: false,
setup: function (editor) {
editor.on('init', function (e) {
btnSave.addEventListener('click', (e) => {
editor.setContent( coder.getValue() );
pop.classList.add('hide');
editor.mode.set('design');
editor.getBody().style.opacity = 1;
});
coder.on('change', function (instance, changeObj) {
if (!pop.classList.contains('hide')) {
editor.setContent( coder.getValue() );
}
});
});
editor.ui.registry.addButton('coder', {
icon: 'sourcecode',
text: '编辑源码',
onAction: function () {
coder.setValue( getHtmlFromTinyMCE(editor) );
pop.classList.remove('hide');
editor.mode.set('readonly');
editor.getBody().style.opacity = .5;
}
});
editor.on('input', function () {
coder.setValue( getHtmlFromTinyMCE(editor) );
});
editor.on('change', function () {
coder.setValue( getHtmlFromTinyMCE(editor) );
});
}
});
function getHtmlFromTinyMCE(editor) {
const html = editor.getContent()
return pretty(html);
}
<link rel="stylesheet" href="https://cdn.staticfile.org/codemirror/6.65.7/codemirror.min.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/codemirror/6.65.7/theme/idea.min.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/codemirror/6.65.7/addon/hint/show-hint.css">
<script src="https://cdn.staticfile.org/codemirror/6.65.7/codemirror.min.js"></script>
<script src="https://cdn.staticfile.org/codemirror/6.65.7/mode/xml/xml.min.js"></script>
<script src="https://cdn.staticfile.org/codemirror/6.65.7/mode/css/css.min.js"></script>
<script src="https://cdn.staticfile.org/codemirror/6.65.7/mode/javascript/javascript.min.js"></script>
<script src="https://cdn.staticfile.org/codemirror/6.65.7/mode/htmlmixed/htmlmixed.min.js"></script>
<textarea id="editor" name="content">
<h3 v-scope @vue:mounted="mounted">TinyMCE源码格式化-最小验证模型</h3>
<ul v-effect="test($el)">
<li>解决了编辑源码时会自动清除掉`空格缩进`导致日后维护活动页源码很麻烦的问题</li>
<li>解决了编辑源码时会自动清除掉`script`和`style`标签的问题</li>
<li><s>现在 TinyMCE 和 CodeMirror 修改的内容会保持同步变化</s></li>
<li>解决 TinyMCE 编辑时,编辑器光标位置不断重置到最前面的问题</li>
<li>解决 TinyMCE 源码编辑器不支持在标签中加入特殊字符的问题,如"@vue:mounted"</li>
</ul>
<blockquote>实际开发时别再引用这里的CDN地址,国内某些地区有风墙!!!</blockquote>
<a href="https://github.com/tinymce/tinymce" target="_blank">TinyMCE</a>
<span> | </span>
<a href="https://github.com/codemirror/dev" target="_blank">CodeMirror</a>
<span> | </span>
<a href="https://github.com/jonschlinkert/pretty" target="_blank">Pretty</a>
<style>button{background:#0cf;}</style>
<script>PetiteVue.createApp({mounted(){}}).mount()</script>
</textarea>
<div class="popup hide">
<h3>编辑源码</h3>
<textarea id="coder"></textarea>
<button class="btnSave" style="margin-top:25px;">保存 & 关闭</button>
</div>
:root {
--tiny-width: 70%;
}
html, body {
height: 100%;
margin: 0;
padding: 10px;
background: #f2f5f7;
}
.hide {
visibility: hidden;
pointer-events: none;
right: calc(-1 * var(--tiny-width)) !important;
}
.CodeMirror {
flex: 1;
}
.popup {
display: flex;
flex-direction: column;
position: absolute;
width: var(--tiny-width);
min-width: 300px;
height: 100%;
top: 0;
right: 0px;
padding: 20px;
z-index: 99999;
background: #ffffffdd;
backdrop-filter: blur(5px);
transition: .3s;
box-shadow: 0 0 8px #00000033;
}