YangQi 32fb134675
perf: separation dialog component (#187)
- 分离组件
- 调整提示信息
2022-08-17 14:05:18 +08:00

500 lines
14 KiB
Vue

<template>
<el-container class="header-container is-dark">
<div class="dropdowns">
<el-dropdown>
<span class="el-dropdown-link">
文件<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="refClick">
<i class="el-icon-upload2"></i>
导入 .md
<input hidden type="file" ref="fileInput" accept=".md" />
</el-dropdown-item>
<el-dropdown-item @click.native="$emit('download')">
<i class="el-icon-download"></i>
导出 .md
</el-dropdown-item>
<el-dropdown-item @click.native="$emit('export')">
<i class="el-icon-document"></i>
导出 .html
</el-dropdown-item>
<el-dropdown-item divided @click.native="themeChanged">
<i
class="el-icon-check"
:style="{ opacity: nightMode ? 1 : 0 }"
></i>
暗黑模式
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown>
<span class="el-dropdown-link">
格式<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
class="format-item"
@click.native="$emit('addFormat', '**')"
>
加粗
<kbd> Ctrl + B </kbd>
</el-dropdown-item>
<el-dropdown-item
class="format-item"
@click.native="$emit('addFormat', '*')"
>
斜体
<kbd> Ctrl + I </kbd>
</el-dropdown-item>
<el-dropdown-item
class="format-item"
@click.native="$emit('addFormat', '~~')"
>
删除线
<kbd> Alt + Shift + U </kbd>
</el-dropdown-item>
<el-dropdown-item
class="format-item"
@click.native="$emit('addFormat', '[', ']()')"
>
超链接
<kbd> Alt + Shift + K </kbd>
</el-dropdown-item>
<el-dropdown-item
class="format-item"
@click.native="$emit('formatContent')"
>
格式化
<kbd> Ctrl + Alt + L </kbd>
</el-dropdown-item>
<el-dropdown-item divided @click.native="statusChanged">
<i
class="el-icon-check"
:style="{ opacity: citeStatus ? 1 : 0 }"
></i>
微信外链转底部引用
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown>
<span class="el-dropdown-link">
编辑<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="$emit('show-dialog-upload-img')">
<i class="el-icon-upload"></i>
上传图片
</el-dropdown-item>
<el-dropdown-item @click.native="$emit('show-dialog-form')">
<i class="el-icon-s-grid"></i>
插入表格
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown>
<span class="el-dropdown-link">
样式<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item class="padding-left-3">
<style-option-menu
label="字体"
:options="config.builtinFonts"
:current="selectFont"
:charge="fontChanged"
></style-option-menu>
</el-dropdown-item>
<el-dropdown-item class="padding-left-3">
<style-option-menu
label="字号"
:options="config.sizeOption"
:current="selectSize"
:charge="sizeChanged"
></style-option-menu>
</el-dropdown-item>
<el-dropdown-item class="padding-left-3">
<style-option-menu
label="颜色"
:options="config.colorOption"
:current="selectColor"
:charge="colorChanged"
></style-option-menu>
</el-dropdown-item>
<el-dropdown-item class="padding-left-3">
<style-option-menu
label="代码主题"
:options="config.codeThemeOption"
:current="selectCodeTheme"
:charge="codeThemeChanged"
></style-option-menu>
</el-dropdown-item>
<el-dropdown-item
divided
class="padding-left-3"
@click.native="showPicker()"
>
自定义颜色
<el-color-picker
show-alpha
ref="colorPicker"
size="mini"
style="float: right; margin-top: 3px"
v-model="selectColor"
@change="colorChanged"
></el-color-picker>
</el-dropdown-item>
<el-dropdown-item class="padding-left-3" @click.native="customStyle">
自定义 CSS
</el-dropdown-item>
<el-dropdown-item divided @click.native="codeBlockChanged">
<i
class="el-icon-check"
:style="{ opacity: isMacCodeBlock ? 1 : 0 }"
></i>
Mac 代码块
</el-dropdown-item>
<el-dropdown-item
divided
class="padding-left-3"
@click.native="showResetConfirm = true"
>
重置
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown>
<span class="el-dropdown-link">
帮助<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="$emit('show-about-dialog')">
关于
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<el-button plain size="medium" :type="btnType" @click="copy">
复制
</el-button>
<el-button plain size="medium" :type="btnType" @click="prePost">
发布
</el-button>
<post-info-dialog
:form="form"
@post="post"
@close="form.dialogVisible = false"
>
</post-info-dialog>
<reset-dialog
:show-reset-confirm="showResetConfirm"
@confirm="confirmReset"
@close="cancelReset"
></reset-dialog>
</el-container>
</template>
<script>
import { setFontSize, setColorWithCustomTemplate } from '@/assets/scripts/util'
import { solveWeChatImage, mergeCss } from '@/assets/scripts/converter'
import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt'
import config from '@/assets/scripts/config'
import ResetDialog from './ResetDialog'
import StyleOptionMenu from './StyleOptionMenu'
import PostInfoDialog from './PostInfoDialog'
import { mapState, mapMutations } from 'vuex'
export default {
name: `editor-header`,
data() {
return {
form: {
dialogVisible: false,
title: ``,
desc: ``,
thumb: ``,
content: ``,
},
config: config,
citeStatus: false,
isMacCodeBlock: true,
showResetConfirm: false,
selectFont: ``,
selectSize: ``,
selectColor: ``,
selectCodeTheme: config.codeThemeOption[2].value,
}
},
components: {
PostInfoDialog,
StyleOptionMenu,
ResetDialog,
},
computed: {
effect() {
return this.nightMode ? `dark` : `light`
},
btnContent() {
return this.nightMode ? `浅色模式` : `暗黑模式`
},
btnType() {
return this.nightMode ? `default` : `primary`
},
...mapState({
output: (state) => state.output,
editor: (state) => state.editor,
cssEditor: (state) => state.cssEditor,
currentFont: (state) => state.currentFont,
currentSize: (state) => state.currentSize,
currentColor: (state) => state.currentColor,
codeTheme: (state) => state.codeTheme,
nightMode: (state) => state.nightMode,
currentCiteStatus: (state) => state.citeStatus,
currentIsMacCodeBlock: (state) => state.isMacCodeBlock,
}),
},
methods: {
refClick() {
this.$refs.fileInput.click()
},
showPicker() {
this.$refs.colorPicker.showPicker = true
},
prePost() {
let auto = {}
try {
auto = {
thumb: document.querySelector(`#output img`).src,
title: [1, 2, 3, 4, 5, 6]
.map((h) => document.querySelector(`#output h${h}`))
.filter((h) => h)[0].innerText,
desc: document.querySelector(`#output p`).innerText,
content: this.output,
}
} catch (error) {
console.log(`error`, error)
}
this.form = {
dialogVisible: true,
...auto,
auto,
}
},
post() {
this.form.dialogVisible = false
// 使用 window.$syncer 可以检测是否安装插件
window.syncPost({
title: this.form.title || this.form.auto.title,
desc: this.form.desc || this.form.auto.desc,
content: this.form.content || this.form.auto.content,
thumb: this.form.thumb || this.form.auto.thumb,
})
},
fontChanged(fonts) {
this.setWxRendererOptions({
fonts: fonts,
})
this.setCurrentFont(fonts)
this.selectFont = fonts
this.$emit(`refresh`)
},
sizeChanged(size) {
let theme = setFontSize(size.replace(`px`, ``))
theme = setColorWithCustomTemplate(theme, this.currentColor)
this.setWxRendererOptions({
size: size,
theme: theme,
})
this.setCurrentSize(size)
this.selectSize = size
this.$emit(`refresh`)
},
colorChanged(color) {
let theme = setFontSize(this.currentSize.replace(`px`, ``))
theme = setColorWithCustomTemplate(theme, color)
this.setWxRendererOptions({
theme: theme,
})
this.setCurrentColor(color)
this.selectColor = color
this.$emit(`refresh`)
},
codeThemeChanged(theme) {
this.setCurrentCodeTheme(theme)
this.selectCodeTheme = theme
this.$emit(`refresh`)
},
statusChanged() {
this.citeStatus = !this.citeStatus
this.setCiteStatus(this.citeStatus)
this.$emit(`refresh`)
},
codeBlockChanged() {
this.isMacCodeBlock = !this.isMacCodeBlock
this.setIsMacCodeBlock(this.isMacCodeBlock)
this.$emit(`refresh`)
},
// 复制到微信公众号
copy() {
this.$emit(`startCopy`)
setTimeout(() => {
solveWeChatImage()
const clipboardDiv = document.getElementById(`output`)
clipboardDiv.innerHTML = mergeCss(clipboardDiv.innerHTML)
if (this.isMacCodeBlock) {
clipboardDiv.innerHTML = clipboardDiv.innerHTML.replaceAll(
/(<code class="prettyprint.+?(?<=style="))/g,
`$1font-family: Menlo, 'Operator Mono', Consolas, Monaco, monospace;`
)
}
clipboardDiv.focus()
window.getSelection().removeAllRanges()
let range = document.createRange()
range.setStartBefore(clipboardDiv.firstChild)
range.setEndAfter(clipboardDiv.lastChild)
window.getSelection().addRange(range)
document.execCommand(`copy`)
window.getSelection().removeAllRanges()
clipboardDiv.innerHTML = this.output
// 输出提示
this.$notify({
showClose: true,
message: `已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴`,
offset: 80,
duration: 1600,
type: `success`,
})
this.$emit(`refresh`)
this.$emit(`endCopy`)
}, 350)
},
// 自定义CSS样式
async customStyle() {
this.$emit(`showCssEditor`)
this.$nextTick(() => {
if (!this.cssEditor) {
this.cssEditor.refresh()
}
})
setTimeout(() => {
this.cssEditor.refresh()
}, 50)
let flag = localStorage.getItem(`__css_content`)
if (!flag) {
this.setCssEditorValue(DEFAULT_CSS_CONTENT)
}
},
// 重置样式
confirmReset() {
this.showResetConfirm = false
localStorage.clear()
this.cssEditor.setValue(DEFAULT_CSS_CONTENT)
this.citeStatus = false
this.statusChanged(false)
this.fontChanged(this.config.builtinFonts[0].value)
this.colorChanged(this.config.colorOption[0].value)
this.sizeChanged(this.config.sizeOption[2].value)
this.codeThemeChanged(this.config.codeThemeOption[2].value)
this.$emit(`cssChanged`)
this.selectFont = this.currentFont
this.selectSize = this.currentSize
this.selectColor = this.currentColor
this.selectCodeTheme = this.codeTheme
this.isMacCodeBlock = false
this.codeBlockChanged()
},
cancelReset() {
this.showResetConfirm = false
this.editor.focus()
},
...mapMutations([
`setCurrentColor`,
`setCiteStatus`,
`themeChanged`,
`setCurrentFont`,
`setCurrentSize`,
`setCssEditorValue`,
`setCurrentCodeTheme`,
`setWxRendererOptions`,
`setIsMacCodeBlock`,
]),
},
mounted() {
this.selectFont = this.currentFont
this.selectSize = this.currentSize
this.selectColor = this.currentColor
this.selectCodeTheme = this.codeTheme
this.citeStatus = this.currentCiteStatus
this.isMacCodeBlock = this.currentIsMacCodeBlock
const fileInput = this.$refs.fileInput
fileInput.onchange = () => {
const file = fileInput.files[0]
if (file == null) {
return
}
const read = new FileReader()
read.readAsText(file)
read.onload = () => {
this.$emit(`import-md`, read.result)
}
}
},
}
</script>
<style lang="less" scoped>
.header-container {
padding: 10px 20px;
align-items: center;
}
.dropdowns {
flex: 1;
}
.el-dropdown {
margin: 0 10px;
}
.el-dropdown-link {
cursor: pointer;
}
.padding-left-3 {
padding-left: 3em;
}
// 添加边距影响了 divided 行的移入效果,此处做一个兼容处理
.el-dropdown-menu__item--divided.padding-left-3 {
position: relative;
&::after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 3em;
height: 6px;
background: white;
}
}
.format-item {
.padding-left-3;
width: 180px;
kbd {
font-size: 0.75em;
float: right;
color: #666;
}
}
</style>