2019-11-01 17:16:40 +08:00
|
|
|
let app = new Vue({
|
|
|
|
el: '#app',
|
|
|
|
data: function () {
|
|
|
|
let d = {
|
|
|
|
aboutOutput: '',
|
|
|
|
output: '',
|
|
|
|
source: '',
|
|
|
|
editor: null,
|
2019-12-05 17:03:54 +08:00
|
|
|
cssEditor: null,
|
2019-11-01 17:16:40 +08:00
|
|
|
builtinFonts: [
|
2019-12-25 21:56:35 +08:00
|
|
|
{ label: '无衬线', value: "-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif" },
|
|
|
|
{ label: '衬线', value: "Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif" }
|
2019-11-01 17:16:40 +08:00
|
|
|
],
|
|
|
|
sizeOption: [
|
2020-01-06 14:53:08 +08:00
|
|
|
{ label: '12px', value: '12px', desc: '更小' },
|
2019-11-07 14:06:59 +08:00
|
|
|
{ label: '13px', value: '13px', desc: '稍小' },
|
2019-11-03 16:05:04 +08:00
|
|
|
{ label: '14px', value: '14px', desc: '推荐' },
|
2020-01-06 14:53:08 +08:00
|
|
|
{ label: '15px', value: '15px', desc: '稍大' },
|
|
|
|
{ label: '16px', value: '16px', desc: '更大' }
|
2019-11-01 17:16:40 +08:00
|
|
|
],
|
2019-11-05 15:16:42 +08:00
|
|
|
colorOption: [
|
2019-12-25 21:56:35 +08:00
|
|
|
{ label: '经典蓝', value: 'rgba(15, 76, 129, 1)', hex: '最新流行' },
|
|
|
|
{ label: '翡翠绿', value: 'rgba(0, 152, 116, 1)', hex: '优雅清新' },
|
|
|
|
{ label: '活力橘', value: 'rgba(250, 81, 81, 1)', hex: '热情活泼' }
|
2019-11-05 15:16:42 +08:00
|
|
|
],
|
2019-12-10 20:50:53 +08:00
|
|
|
showBox: true,
|
2019-12-29 21:23:47 +08:00
|
|
|
aboutDialogVisible: false,
|
|
|
|
dialogFormVisible: false,
|
|
|
|
form: {
|
|
|
|
rows: 1,
|
|
|
|
cols: 1
|
|
|
|
}
|
2019-11-01 17:16:40 +08:00
|
|
|
};
|
|
|
|
d.currentFont = d.builtinFonts[0].value;
|
2020-01-06 14:53:08 +08:00
|
|
|
d.currentSize = d.sizeOption[2].value;
|
2019-12-25 21:56:35 +08:00
|
|
|
d.currentColor = d.colorOption[1].value;
|
2019-12-07 21:14:02 +08:00
|
|
|
d.status = '1';
|
2019-11-01 17:16:40 +08:00
|
|
|
return d;
|
|
|
|
},
|
|
|
|
mounted() {
|
2019-12-10 19:16:05 +08:00
|
|
|
this.showBox = false
|
2019-11-01 17:16:40 +08:00
|
|
|
this.editor = CodeMirror.fromTextArea(
|
|
|
|
document.getElementById('editor'),
|
|
|
|
{
|
2019-12-11 16:48:28 +08:00
|
|
|
mode: 'text/x-markdown',
|
|
|
|
theme: 'xq-light',
|
2019-11-01 17:16:40 +08:00
|
|
|
lineNumbers: false,
|
|
|
|
lineWrapping: true,
|
|
|
|
styleActiveLine: true,
|
2019-12-11 16:48:28 +08:00
|
|
|
autoCloseBrackets: true
|
2019-11-01 17:16:40 +08:00
|
|
|
}
|
|
|
|
);
|
2019-12-05 17:03:54 +08:00
|
|
|
this.cssEditor = CodeMirror.fromTextArea(
|
2019-12-10 20:50:53 +08:00
|
|
|
document.getElementById('cssEditor'), {
|
|
|
|
mode: 'css',
|
|
|
|
theme: 'style-mirror',
|
|
|
|
lineNumbers: false,
|
|
|
|
lineWrapping: true,
|
|
|
|
matchBrackets: true,
|
|
|
|
autofocus: true,
|
|
|
|
extraKeys: {
|
2019-12-25 21:56:35 +08:00
|
|
|
'Ctrl-F': function autoFormat(editor) {
|
|
|
|
const totalLines = editor.lineCount();
|
2019-12-10 20:50:53 +08:00
|
|
|
editor.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines });
|
2019-12-10 19:16:05 +08:00
|
|
|
}
|
2019-12-10 20:50:53 +08:00
|
|
|
},
|
|
|
|
}
|
2019-12-05 17:03:54 +08:00
|
|
|
);
|
2019-12-10 19:16:05 +08:00
|
|
|
// 自动提示
|
2019-12-25 21:56:35 +08:00
|
|
|
this.cssEditor.on('keyup', (cm, e) => {
|
2019-12-10 20:50:53 +08:00
|
|
|
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 189) {
|
|
|
|
cm.showHint(e);
|
|
|
|
}
|
|
|
|
});
|
2019-12-25 21:56:35 +08:00
|
|
|
this.editor.on('change', (cm, e) => {
|
2019-11-10 15:52:46 +08:00
|
|
|
this.refresh();
|
2019-12-08 21:45:33 +08:00
|
|
|
this.saveEditorContent(this.editor, '__editor_content');
|
2019-11-01 17:16:40 +08:00
|
|
|
});
|
2019-12-23 22:34:38 +08:00
|
|
|
|
|
|
|
// 粘贴上传图片并插入
|
2019-12-25 21:56:35 +08:00
|
|
|
this.editor.on('paste', (cm, e) => {
|
2019-12-23 22:34:38 +08:00
|
|
|
if (!(e.clipboardData && e.clipboardData.items)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) {
|
|
|
|
let item = e.clipboardData.items[i];
|
|
|
|
if (item.kind === 'file') {
|
|
|
|
const pasteFile = item.getAsFile();
|
2019-12-31 11:35:33 +08:00
|
|
|
if (!(this.checkType(pasteFile) && this.checkImageSize(pasteFile))) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-23 22:34:38 +08:00
|
|
|
let data = new FormData();
|
2019-12-25 21:56:35 +08:00
|
|
|
data.append('file', pasteFile);
|
2019-12-23 22:34:38 +08:00
|
|
|
axios.post('https://imgkr.com/api/files/upload', data, {
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'multipart/form-data'
|
|
|
|
}
|
|
|
|
}).then(resp => {
|
|
|
|
this.uploaded(resp.data)
|
2019-12-25 21:56:35 +08:00
|
|
|
}).catch(err => { })
|
2019-12-23 22:34:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2019-12-05 17:03:54 +08:00
|
|
|
this.cssEditor.on('update', (instance) => {
|
2019-12-10 20:50:53 +08:00
|
|
|
this.cssChanged();
|
|
|
|
this.saveEditorContent(this.cssEditor, '__css_content');
|
2019-11-01 17:16:40 +08:00
|
|
|
});
|
|
|
|
this.wxRenderer = new WxRenderer({
|
2019-11-05 21:32:50 +08:00
|
|
|
theme: setColor(this.currentColor),
|
2019-11-01 17:16:40 +08:00
|
|
|
fonts: this.currentFont,
|
2019-12-07 21:14:02 +08:00
|
|
|
size: this.currentSize,
|
|
|
|
status: this.status
|
2019-11-01 17:16:40 +08:00
|
|
|
});
|
2019-12-05 17:03:54 +08:00
|
|
|
|
2019-12-25 21:56:35 +08:00
|
|
|
// 如果有编辑器内容被保存则读取,否则加载默认内容
|
|
|
|
this.loadLocalStorage(this.editor, '__editor_content', DEFAULT_CONTENT);
|
|
|
|
this.loadLocalStorage(this.cssEditor, '__css_content', DEFAULT_CSS_CONTENT);
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
|
|
|
methods: {
|
2019-11-10 15:52:46 +08:00
|
|
|
renderWeChat(source) {
|
2019-12-07 21:14:02 +08:00
|
|
|
let output = marked(source, { renderer: this.wxRenderer.getRenderer(this.status) });
|
|
|
|
// 去除第一行的 margin-top
|
|
|
|
output = output.replace(/(style=".*?)"/, '$1;margin-top: 0"');
|
|
|
|
if (this.status) {
|
2019-11-08 10:30:32 +08:00
|
|
|
// 引用脚注
|
2019-11-01 17:16:40 +08:00
|
|
|
output += this.wxRenderer.buildFootnotes();
|
|
|
|
// 附加的一些 style
|
|
|
|
output += this.wxRenderer.buildAddition();
|
|
|
|
}
|
2019-11-10 16:59:23 +08:00
|
|
|
return output;
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-11-10 15:52:46 +08:00
|
|
|
editorThemeChanged(editorTheme) {
|
2019-11-10 16:59:23 +08:00
|
|
|
this.editor.setOption('theme', editorTheme);
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-11-10 15:52:46 +08:00
|
|
|
fontChanged(fonts) {
|
2019-11-01 17:16:40 +08:00
|
|
|
this.wxRenderer.setOptions({
|
|
|
|
fonts: fonts
|
|
|
|
});
|
2020-01-06 15:40:30 +08:00
|
|
|
this.currentFont = fonts;
|
2019-11-10 16:59:23 +08:00
|
|
|
this.refresh();
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-11-10 15:52:46 +08:00
|
|
|
sizeChanged(size) {
|
2019-11-01 17:16:40 +08:00
|
|
|
this.wxRenderer.setOptions({
|
|
|
|
size: size
|
|
|
|
});
|
2020-01-06 14:53:08 +08:00
|
|
|
let theme = setFontSize(size.replace('px', ''));
|
|
|
|
this.wxRenderer.setOptions({
|
|
|
|
theme: theme
|
|
|
|
});
|
2020-01-06 15:40:30 +08:00
|
|
|
this.currentSize = size;
|
2019-11-10 16:59:23 +08:00
|
|
|
this.refresh();
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-11-10 15:52:46 +08:00
|
|
|
colorChanged(color) {
|
2020-01-06 15:40:30 +08:00
|
|
|
let theme = setFontSize(this.currentSize.replace('px', ''));
|
|
|
|
theme = setColorWithCustomTemplate(theme, color);
|
2019-11-01 17:16:40 +08:00
|
|
|
this.wxRenderer.setOptions({
|
2019-11-05 21:32:50 +08:00
|
|
|
theme: theme
|
2019-11-10 16:59:23 +08:00
|
|
|
});
|
2020-01-06 15:40:30 +08:00
|
|
|
this.currentColor = color;
|
2019-11-10 16:59:23 +08:00
|
|
|
this.refresh();
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-12-05 17:03:54 +08:00
|
|
|
cssChanged() {
|
2019-12-24 22:53:53 +08:00
|
|
|
let json = css2json(this.cssEditor.getValue(0));
|
2020-01-06 15:40:30 +08:00
|
|
|
let theme = setFontSize(this.currentSize.replace('px', ''));
|
|
|
|
theme = customCssWithTemplate(json, this.currentColor, theme);
|
2019-11-01 17:16:40 +08:00
|
|
|
this.wxRenderer.setOptions({
|
2019-11-05 21:32:50 +08:00
|
|
|
theme: theme
|
2019-11-10 16:59:23 +08:00
|
|
|
});
|
|
|
|
this.refresh();
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-12-31 11:35:33 +08:00
|
|
|
// 图片上传前的处理
|
|
|
|
beforeUpload(file) {
|
|
|
|
return this.checkType(file) && this.checkImageSize(file);
|
|
|
|
},
|
|
|
|
// 检查文件类型
|
|
|
|
checkType(file) {
|
|
|
|
if (!/\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) {
|
|
|
|
this.$message({
|
|
|
|
showClose: true,
|
|
|
|
message: '请上传 JPG/PNG/GIF 格式的图片',
|
|
|
|
type: 'error'
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
// 检查图片大小
|
|
|
|
checkImageSize(file) {
|
|
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
|
|
this.$message({
|
|
|
|
showClose: true,
|
|
|
|
message: '由于公众号限制,图片大小不能超过 5.0M',
|
|
|
|
type: 'error'
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2019-11-30 20:36:32 +08:00
|
|
|
// 图片上传结束
|
|
|
|
uploaded(response, file, fileList) {
|
|
|
|
if (response.success) {
|
2019-11-30 21:07:27 +08:00
|
|
|
// 上传成功,获取光标
|
2019-11-30 20:36:32 +08:00
|
|
|
const cursor = this.editor.getCursor();
|
2019-12-11 21:01:02 +08:00
|
|
|
const imageUrl = response.data
|
2019-11-30 20:36:32 +08:00
|
|
|
const markdownImage = `![](${imageUrl})`
|
2019-11-30 21:07:27 +08:00
|
|
|
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
|
2019-11-30 20:36:32 +08:00
|
|
|
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
|
|
|
|
this.$message({
|
|
|
|
showClose: true,
|
|
|
|
message: '图片插入成功',
|
|
|
|
type: 'success'
|
|
|
|
});
|
2019-12-01 10:19:15 +08:00
|
|
|
this.refresh();
|
2019-11-30 20:36:32 +08:00
|
|
|
} else {
|
|
|
|
// 上传失败
|
|
|
|
this.$message({
|
|
|
|
showClose: true,
|
|
|
|
message: response.message,
|
|
|
|
type: 'error'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2019-11-01 17:16:40 +08:00
|
|
|
// 刷新右侧预览
|
2019-11-10 15:52:46 +08:00
|
|
|
refresh() {
|
2019-11-10 16:59:23 +08:00
|
|
|
this.output = this.renderWeChat(this.editor.getValue(0));
|
2019-11-01 17:16:40 +08:00
|
|
|
},
|
2019-12-07 16:46:30 +08:00
|
|
|
// 重置页面
|
|
|
|
reset() {
|
|
|
|
this.$confirm('此操作将丢失本地缓存的文本和自定义样式,是否继续?', '提示', {
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
cancelButtonText: '取消',
|
2019-12-28 16:25:30 +08:00
|
|
|
confirmButtonClass: 'el-button--success',
|
|
|
|
cancelButtonClass: 'el-button--success is-plain',
|
2019-12-07 16:46:30 +08:00
|
|
|
type: 'warning',
|
|
|
|
center: true
|
|
|
|
}).then(() => {
|
2019-12-11 15:50:04 +08:00
|
|
|
localStorage.clear()
|
2019-12-11 16:28:20 +08:00
|
|
|
this.editor.setValue(DEFAULT_CONTENT);
|
2019-12-10 19:16:05 +08:00
|
|
|
this.cssEditor.setValue(DEFAULT_CSS_CONTENT);
|
2019-12-07 16:46:30 +08:00
|
|
|
this.editor.focus();
|
2019-12-11 15:50:04 +08:00
|
|
|
this.cssChanged()
|
2019-12-07 16:46:30 +08:00
|
|
|
}).catch(() => {
|
|
|
|
this.editor.focus();
|
|
|
|
});
|
|
|
|
},
|
2019-12-29 21:23:47 +08:00
|
|
|
// 插入表格
|
|
|
|
insertTable() {
|
|
|
|
const cursor = this.editor.getCursor();
|
|
|
|
const rows = parseInt(this.form.rows);
|
|
|
|
const cols = parseInt(this.form.cols);
|
|
|
|
if (isNaN(rows) || isNaN(cols) || rows < 1 || cols < 1) {
|
|
|
|
this.$message({
|
|
|
|
showClose: true,
|
|
|
|
message: '输入的行/列数无效,请重新输入',
|
|
|
|
type: 'error'
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let table = '';
|
|
|
|
for (let i = 0; i < rows + 2; ++i) {
|
|
|
|
for (let j = 0; j < cols + 1; ++j) {
|
|
|
|
table += (j === 0 ? '|' : (i !== 1 ? ' |' : ' --- |'));
|
|
|
|
}
|
|
|
|
table += '\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
this.editor.replaceSelection(`\n${table}\n`, cursor);
|
|
|
|
this.dialogFormVisible = false
|
|
|
|
this.refresh();
|
|
|
|
},
|
2019-12-07 21:14:02 +08:00
|
|
|
statusChanged() {
|
|
|
|
this.refresh();
|
|
|
|
},
|
2019-12-25 21:56:35 +08:00
|
|
|
// 将编辑器内容保存到 LocalStorage
|
2019-12-08 21:45:33 +08:00
|
|
|
saveEditorContent(editor, name) {
|
|
|
|
const content = editor.getValue(0);
|
2019-11-10 15:52:46 +08:00
|
|
|
if (content) {
|
2019-12-08 21:45:33 +08:00
|
|
|
localStorage.setItem(name, content);
|
2019-11-01 17:16:40 +08:00
|
|
|
} else {
|
2019-12-08 21:45:33 +08:00
|
|
|
localStorage.removeItem(name);
|
2019-11-01 17:16:40 +08:00
|
|
|
}
|
|
|
|
},
|
2019-12-25 21:56:35 +08:00
|
|
|
loadLocalStorage(editor, name, content) {
|
|
|
|
if (localStorage.getItem(name)) {
|
|
|
|
editor.setValue(localStorage.getItem(name));
|
|
|
|
} else {
|
|
|
|
editor.setValue(content);
|
|
|
|
}
|
|
|
|
},
|
2019-12-19 21:04:13 +08:00
|
|
|
// 下载编辑器内容到本地
|
|
|
|
downloadEditorContent() {
|
|
|
|
let downLink = document.createElement('a');
|
|
|
|
downLink.download = 'content.md';
|
|
|
|
downLink.style.display = 'none';
|
|
|
|
let blob = new Blob([this.editor.getValue(0)]);
|
|
|
|
downLink.href = URL.createObjectURL(blob);
|
|
|
|
document.body.appendChild(downLink);
|
|
|
|
downLink.click();
|
|
|
|
document.body.removeChild(downLink);
|
|
|
|
},
|
|
|
|
// 自定义CSS样式
|
2019-12-11 15:50:04 +08:00
|
|
|
async customStyle() {
|
2019-12-05 17:03:54 +08:00
|
|
|
this.showBox = !this.showBox;
|
2019-12-25 21:56:35 +08:00
|
|
|
let flag = await localStorage.getItem('__css_content')
|
2019-12-11 15:50:04 +08:00
|
|
|
if (!flag) {
|
|
|
|
this.cssEditor.setValue(DEFAULT_CSS_CONTENT);
|
|
|
|
}
|
2019-12-07 17:07:05 +08:00
|
|
|
},
|
2019-12-19 21:04:13 +08:00
|
|
|
// 复制渲染后的内容到剪贴板
|
2019-11-10 15:52:46 +08:00
|
|
|
copy() {
|
2019-12-25 21:56:35 +08:00
|
|
|
let clipboardDiv = document.getElementById('output');
|
|
|
|
clipboardDiv.focus();
|
|
|
|
window.getSelection().removeAllRanges();
|
|
|
|
let range = document.createRange();
|
|
|
|
range.setStartBefore(clipboardDiv.firstChild);
|
|
|
|
range.setEndAfter(clipboardDiv.lastChild);
|
|
|
|
window.getSelection().addRange(range);
|
2019-12-29 15:50:03 +08:00
|
|
|
document.execCommand('copy')
|
2019-12-25 21:56:35 +08:00
|
|
|
// 输出提示
|
|
|
|
this.$notify({
|
|
|
|
showClose: true,
|
|
|
|
message: '已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴',
|
|
|
|
offset: 80,
|
|
|
|
duration: 1600,
|
|
|
|
type: 'success'
|
|
|
|
});
|
2019-11-01 17:16:40 +08:00
|
|
|
}
|
|
|
|
},
|
2019-11-10 15:52:46 +08:00
|
|
|
updated() {
|
|
|
|
this.$nextTick(() => {
|
|
|
|
prettyPrint();
|
2019-11-01 17:16:40 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|