459 lines
13 KiB
Vue
459 lines
13 KiB
Vue
<template>
|
||
<div>
|
||
<dir-tree ref="dirTree" :append-to-body='true'>
|
||
<el-button slot="footer" size="small" type="primary" @click="confirmSelectDir">确 定</el-button>
|
||
</dir-tree>
|
||
<div class="article-editor">
|
||
<div class="editor-left">
|
||
<el-header>
|
||
<div class="header-item">
|
||
<span class="title-label">标题:</span>
|
||
<el-input class="articles-title" placeholder="文章标题" v-model="filename" />
|
||
<!-- <el-input v-if="!editStatus" class="articles-storage" placeholder="存储位置" size="small" v-model="storageLocation" :readonly="true" @focus="selectDir">-->
|
||
<!-- <el-button slot="prepend" size="small" @click="selectDir">保存位置</el-button>-->
|
||
<!-- </el-input>-->
|
||
<!-- <el-button type="primary" @click="moreSetting = true" size="small">更多设置</el-button>-->
|
||
<!-- <el-button v-if="!editStatus" class="release-button" type="primary" size="small" @click="add" :loading="adding">发布文章</el-button>-->
|
||
<!-- <el-button v-if="editStatus" class="release-button" type="primary" size="small" @click="update" :loading="updating">更新文章</el-button>-->
|
||
<!-- <el-button v-if="!editStatus" class="release-button" type="warning" size="small" @click="add" :loading="adding">保存草稿</el-button>-->
|
||
</div>
|
||
</el-header>
|
||
<el-main>
|
||
<div id="vditor"></div>
|
||
</el-main>
|
||
</div>
|
||
<div class="editor-right">
|
||
<div class="operation">
|
||
<el-button class="release-button" type="warning" size="small" @click="add" :loading="updating">保存草稿</el-button>
|
||
<el-button v-if="!editStatus" class="release-button" type="primary" size="small" @click="add" :loading="updating">发布文章</el-button>
|
||
<el-button v-if="editStatus" class="release-button" type="primary" size="small" @click="update" :loading="updating">更新文章</el-button>
|
||
</div>
|
||
<div class="more-setting">
|
||
<p class="mark-setting-label">文章封面:</p>
|
||
<el-input
|
||
class="mark-setting-input"
|
||
type="textarea"
|
||
:autosize="{ minRows: 3, maxRows: 8}"
|
||
placeholder="输入图片URL"
|
||
v-model="file.cover">
|
||
</el-input>
|
||
|
||
<p class="mark-setting-label">分类:</p>
|
||
<el-cascader
|
||
v-model="categoryIds"
|
||
class="mark-setting-input"
|
||
placeholder="不选择"
|
||
:collapse-tags="false"
|
||
:options="categories"
|
||
:show-all-levels="false"
|
||
:props="{ multiple: true, checkStrictly: true }"
|
||
clearable></el-cascader>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<el-drawer
|
||
:visible.sync="moreSetting"
|
||
:append-to-body='true'
|
||
:with-header="false">
|
||
<div class="more-setting">
|
||
<h2>更多设置</h2>
|
||
<p class="mark-setting-label">文章封面:</p>
|
||
<el-input
|
||
type="textarea"
|
||
:autosize="{ minRows: 3, maxRows: 8}"
|
||
placeholder="输入图片URL"
|
||
v-model="file.cover">
|
||
</el-input>
|
||
</div>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import markdownApi from '@/api/markdown-api'
|
||
import uploadApi from '@/api/file-api'
|
||
import DirTree from"@/components/FileTree/DirTree"
|
||
import fileConfig from '@/utils/file-config'
|
||
|
||
import Vditor from 'vditor'
|
||
import "vditor/src/assets/scss/index.scss"
|
||
import categoryApi from "@/api/category";
|
||
|
||
let toolbar = [
|
||
'emoji',
|
||
'headings',
|
||
'bold',
|
||
'italic',
|
||
'strike',
|
||
'link',
|
||
'|',
|
||
'list',
|
||
'ordered-list',
|
||
'check',
|
||
'outdent',
|
||
'indent',
|
||
'|',
|
||
'quote',
|
||
'line',
|
||
'code',
|
||
'inline-code',
|
||
'insert-before',
|
||
'insert-after',
|
||
'|',
|
||
'upload',
|
||
'record',
|
||
'table',
|
||
'|',
|
||
'undo',
|
||
'redo',
|
||
'|',
|
||
'edit-mode',
|
||
'content-theme',
|
||
'code-theme',
|
||
'outline',
|
||
'preview',
|
||
{
|
||
name: 'more',
|
||
toolbar: [
|
||
'fullscreen',
|
||
'export',
|
||
'both',
|
||
'info',
|
||
'help',
|
||
],
|
||
}]
|
||
|
||
export default {
|
||
name: 'MarkdownEditor',
|
||
components: {
|
||
DirTree, Vditor
|
||
},
|
||
props: {
|
||
hasChange: {
|
||
type: Boolean,
|
||
defalut: false
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
file: {},
|
||
editStatus: false,
|
||
html:'', // 及时转的html
|
||
filename: '',
|
||
clientHeight: document.documentElement.clientHeight - 210,
|
||
updating: false,
|
||
isFullScreen: false,
|
||
storageLocation: '/',
|
||
selectLocationVisible: false,
|
||
moreSetting: false,
|
||
contentEditor: '',
|
||
categoryIds: [],
|
||
categories: []
|
||
}
|
||
},
|
||
created() {
|
||
console.log('created')
|
||
},
|
||
mounted() {
|
||
console.log('mounted')
|
||
this.getMarkdown()
|
||
this.categoryTree()
|
||
const that = this
|
||
window.onresize = function temp() {
|
||
that.reHeight()
|
||
}
|
||
},
|
||
destroyed() {
|
||
console.log('destroyed')
|
||
},
|
||
watch: {
|
||
file(){
|
||
console.log('filename is changed')
|
||
this.valueHasChanged()
|
||
},
|
||
filename(val){
|
||
console.log('filename is changed')
|
||
this.valueHasChanged()
|
||
this.$emit('onTitle', val)
|
||
},
|
||
categoryIds(){
|
||
console.log('categoryIds is changed')
|
||
this.valueHasChanged()
|
||
},
|
||
storageLocation(){
|
||
console.log('storageLocation is changed')
|
||
this.valueHasChanged()
|
||
}
|
||
},
|
||
methods: {
|
||
valueHasChanged(){
|
||
this.$emit('update:hasChange', true)
|
||
},
|
||
getMarkdown(){
|
||
if(this.$route.query.id){
|
||
this.editStatus = true
|
||
markdownApi.getMarkdown({
|
||
mark: this.$route.query.id
|
||
}).then((res) => {
|
||
this.file = res.data
|
||
this.content = this.file.contentText
|
||
this.filename = this.file.name.split('.md')[0]
|
||
this.storageLocation = res.data.path
|
||
if(this.file.categoryIds){
|
||
this.file.categoryIds.forEach(categoryId => {
|
||
this.categoryIds.push(new Array(categoryId))
|
||
})
|
||
}
|
||
// 初始化编辑器
|
||
this.vditorInit(res.data.contentText)
|
||
})
|
||
} else {
|
||
this.vditorInit('')
|
||
}
|
||
},
|
||
categoryTree() {
|
||
categoryApi.categoryTree({userId: this.$store.state.user.userId}).then(res => {
|
||
this.categories = res.data
|
||
})
|
||
},
|
||
vditorInit(content){
|
||
this.contentEditor = new Vditor('vditor', {
|
||
height: this.clientHeight,
|
||
toolbar,
|
||
toolbarConfig: {
|
||
pin: true,
|
||
},
|
||
preview: {
|
||
mode: 'both',
|
||
hljs: {
|
||
lineNumber: true
|
||
}
|
||
},
|
||
cache: {
|
||
enable: false,
|
||
},
|
||
after: () => {
|
||
this.contentEditor.setValue(content)
|
||
},
|
||
})
|
||
},
|
||
$imgAdd(pos, $file) {
|
||
const maxSize = 1024 * 1024 * 5
|
||
if($file.size > maxSize){
|
||
this.$message({
|
||
message: '请选择小于5M的图片! 大于5M的图片将无法上传',
|
||
type: 'warning',
|
||
duration: 3000,
|
||
});
|
||
return
|
||
}
|
||
// 第一步.将图片上传到服务器.
|
||
let data = new FormData();
|
||
data.append('filename', this.filename)
|
||
data.append('username', this.$store.state.user.name)
|
||
data.append('userId', this.$store.state.user.userId);
|
||
data.append('file', $file);
|
||
uploadApi.uploadImage(data).then((res) => {
|
||
// 第二步.将返回的url替换到文本原位置 -> 
|
||
/**
|
||
* $vm 指为mavonEditor实例,可以通过如下两种方式获取
|
||
* 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后,`$vm`为`mavonEditor`
|
||
* 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>,`$vm`为 `this.$refs.md`
|
||
*/
|
||
// const url = process.env.VUE_APP_BASE_FILE_API + '/public/image/' + res.data
|
||
const url = fileConfig.publicPreviewUrl(res.data);
|
||
console.log(url)
|
||
this.$refs.md.$img2Url(pos, url);
|
||
})
|
||
},
|
||
$imgDel(pos, $file) {
|
||
},
|
||
// 所有操作都会被解析重新渲染
|
||
change(value, render){
|
||
// render 为 markdown 解析后的结果[html]
|
||
this.html = render;
|
||
},
|
||
fullScreen(status) {
|
||
this.isFullScreen = status
|
||
this.reHeight()
|
||
},
|
||
reHeight(){
|
||
if(this.isFullScreen){
|
||
this.clientHeight = document.documentElement.clientHeight
|
||
}else{
|
||
this.clientHeight = document.documentElement.clientHeight - 210
|
||
}
|
||
},
|
||
save(){
|
||
if(this.editStatus){
|
||
this.update()
|
||
}else{
|
||
this.add()
|
||
}
|
||
},
|
||
imageFilter() {
|
||
return true
|
||
},
|
||
checkParam(){
|
||
if(!this.filename){
|
||
this.$message.warning("请输入文章标题")
|
||
return false
|
||
}
|
||
if(this.contentEditor.getValue().length < 2){
|
||
this.$message.warning("请输入文章内容")
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
// add
|
||
add(){
|
||
this.update()
|
||
// if(!this.checkParam()){
|
||
// return
|
||
// }
|
||
// this.updating = true
|
||
// const filename = this.filename + ".md"
|
||
// markdownApi.addMarkdown({
|
||
// userId: this.$store.state.user.userId,
|
||
// username: this.$store.state.user.name,
|
||
// filename: filename,
|
||
// cover: this.file.cover,
|
||
// categoryIds: this.getCategoryList(),
|
||
// currentDirectory: this.storageLocation,
|
||
// contentText: this.contentEditor.getValue()
|
||
// }).then(() => {
|
||
// this.updating = false
|
||
// this.$message({
|
||
// message: "发布成功",
|
||
// type: 'success',
|
||
// duration : 1000
|
||
// });
|
||
// }).catch(()=> {
|
||
// this.updating = false
|
||
// })
|
||
},
|
||
getCategoryList(){
|
||
let categoryIdList = []
|
||
this.categoryIds.forEach(categoryIds => {
|
||
categoryIdList.push(categoryIds[0])
|
||
})
|
||
return categoryIdList
|
||
},
|
||
// update
|
||
update(){
|
||
if(!this.checkParam()){
|
||
return
|
||
}
|
||
this.updating = true
|
||
if(this.filename){
|
||
const filename = this.filename + ".md"
|
||
markdownApi.editMarkdown({
|
||
fileId: this.$route.query.id,
|
||
userId: this.$store.state.user.userId,
|
||
username: this.$store.state.user.name,
|
||
filename: filename,
|
||
cover: this.file.cover,
|
||
categoryIds: this.getCategoryList(),
|
||
currentDirectory: this.storageLocation,
|
||
contentText: this.contentEditor.getValue()
|
||
}).then(() => {
|
||
this.updating = false
|
||
this.$message({
|
||
message: "发布成功",
|
||
type: 'success',
|
||
duration : 1000
|
||
});
|
||
}).catch(()=> {
|
||
this.updating = false
|
||
})
|
||
}
|
||
},
|
||
selectDir(){
|
||
this.$refs.dirTree.show()
|
||
},
|
||
confirmSelectDir(){
|
||
const node = this.$refs.dirTree.getSelectTreeNode()
|
||
this.storageLocation = node.path + node.name
|
||
this.$refs.dirTree.hide()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
/deep/ .el-main {
|
||
padding: 30px 20px 20px 20px;
|
||
.v-note-wrapper {
|
||
z-index: 200;
|
||
}
|
||
}
|
||
/deep/ .el-header {
|
||
height: 43px!important;
|
||
}
|
||
/deep/ .el-input-group {
|
||
width: unset;
|
||
}
|
||
/deep/ .header-item {
|
||
line-height: 40px;
|
||
padding: 5px 0 5px 0;
|
||
.release-button {
|
||
float: right;
|
||
margin-left: 10px;
|
||
}
|
||
.el-input {
|
||
width: 90%;
|
||
input[type='text'] {
|
||
font-weight: 900;
|
||
color: #000000!important;
|
||
font-size: 16px;
|
||
border: none;
|
||
border-bottom: #ccc 1px solid;
|
||
border-radius: 0;
|
||
}
|
||
}
|
||
.title-label {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
/deep/ .more-setting {
|
||
.el-input {
|
||
width: 100%;
|
||
}
|
||
margin-top: 68px;
|
||
.mark-setting-label {
|
||
font-weight: 500;
|
||
margin-top: 10px!important;
|
||
}
|
||
.mark-setting-input {
|
||
width: 100%;
|
||
}
|
||
.mark-description {
|
||
font-size: 12px;
|
||
font-weight: 100;
|
||
}
|
||
}
|
||
.article-editor {
|
||
min-width: 1160px;
|
||
margin: auto;
|
||
display: flex;
|
||
.editor-left {
|
||
width: 75%;
|
||
}
|
||
.editor-right {
|
||
width: 25%;
|
||
.operation {
|
||
float: right;
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1200px) {
|
||
.article-editor {
|
||
max-width: 1200px;
|
||
}
|
||
}
|
||
</style>
|