点击写文章,会跳转到该页面

点击右上角的发布,会弹出发布的对话框
因此,我们要调用文章分类列表和文章标签列表的接口
以及如果是点击文字的编辑按钮进入该页面的话,还需要通过调用文章id获取文章详情的接口
还需要调用发布文章的接口
/*** 获取分类列表*/
export function getAllCategorys()
{return request({url:"/categorys",method:'GET',})
}
/*** 查询标签列表*/
export function getAllTags()
{return request({url:"/tags",method:"GET",})
}
/*** 通过文章id获取文章详情* @param PageParams*/
export function getArticleById(id)
{return request({url: `/articles/${id}`,method: 'POST'})
}
/*** 通过token和文章参数发布文章* @param PageParams*/
export function publishArticle(article,token)
{return request({headers: {'Authorization': token},url: '/articles/publish',method: 'post',data: article})
}
这里的header是复用了baseHeader组件。
将simple传入到baseHeader中,baseHeader通过props获取,将之前首页展示的首页、文章分类、标签、文章归档全部屏蔽掉
然后用了slot,将传入的写文章、发布、取消全部插入到baseHeader中
<template><div id="write" v-title :data-title="title"><el-container><!--标题、发布、取消按钮--><base-header :simple=true><el-col :span="4" :offset="2"><div class="me-write-info">写文章</div></el-col><el-col :span="4" :offset="6"><div class="me-write-btn"><el-button round @click="publishShow">发布</el-button><el-button round @click="cancel">取消</el-button></div></el-col></base-header><el-container class="me-area me-write-box"><el-main class="me-write-main"><div class="me-write-title"><el-input resize="none"type="textarea"autosizev-model="articleForm.title"placeholder="请输入标题"class="me-write-input"></el-input></div><div id="placeholder" style="visibility: hidden;height: 89px;display: none;"></div><markdown-editor :editor="articleForm.editor" class="me-write-editor"></markdown-editor></el-main></el-container><!--点击发布之后弹出的对话框--><el-dialog title="摘要 分类 标签":visible.sync="publishVisible":close-on-click-modal=falsecustom-class="me-dialog"><el-form :model="articleForm" ref="articleForm" :rules="rules"><el-form-item prop="summary"><el-input type="textarea"v-model="articleForm.summary":rows="6"placeholder="请输入摘要"></el-input></el-form-item><el-form-item label="文章分类" prop="category"><el-select v-model="articleForm.category" value-key="id" placeholder="请选择文章分类"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c"></el-option></el-select></el-form-item><el-form-item label="文章标签" prop="tags"><el-checkbox-group v-model="articleForm.tags"><el-checkbox v-for="t in tags" :key="t.id" :label="t.id" name="tags">{{t.tagName}}</el-checkbox></el-checkbox-group></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="publishVisible = false">取 消</el-button><el-button type="primary" @click="publish('articleForm')">发布</el-button></div></el-dialog></el-container></div>
</template><script>import BaseHeader from '@/views/BaseHeader'import MarkdownEditor from '@/components/markdown/MarkdownEditor'import {publishArticle, getArticleById} from '@/api/article'import {getAllCategorys} from '@/api/category'import {getAllTags} from '@/api/tag'export default{name: 'publishArticle',mounted(){if(this.$route.params.id){this.getArticleById(this.$route.params.id)}/*获取是所有标签和分类*/this.getCategorysAndTags();this.editorToolBarToFixedWrapper = this.$_.throttle(this.editorToolBarToFixed, 200)window.addEventListener('scroll', this.editorToolBarToFixedWrapper, false);},beforeDestroy(){window.removeEventListener('scroll', this.editorToolBarToFixedWrapper, false)},data(){return {publishVisible: false,categorys: [],tags: [],articleForm: {id: '',title: '',summary: '',category: '',tags: [],editor: {value: '',ref: '',//保存mavonEditor实例 实际不该这样default_open: 'edit',toolbars: {bold: true, // 粗体italic: true, // 斜体header: true, // 标题underline: true, // 下划线strikethrough: true, // 中划线mark: true, // 标记superscript: true, // 上角标subscript: true, // 下角标quote: true, // 引用ol: true, // 有序列表ul: true, // 无序列表imagelink: true, // 图片链接code: true, // codefullscreen: true, // 全屏编辑readmodel: true, // 沉浸式阅读help: true, // 帮助undo: true, // 上一步redo: true, // 下一步trash: true, // 清空navigation: true, // 导航目录//subfield: true, // 单双栏模式preview: true, // 预览}}},rules: {summary: [{required: true, message: '请输入摘要', trigger: 'blur'},{max: 80, message: '不能大于80个字符', trigger: 'blur'}],category: [{required: true, message: '请选择文章分类', trigger: 'change'}],tags: [{type: 'array', required: true, message: '请选择标签', trigger: 'change'}]}}},computed: {title (){return '写文章'}},methods: {/*点击文章编辑时,获取文章详情*/getArticleById(id){let that = this;getArticleById(id).then(data =>{Object.assign(that.articleForm, data.data);that.articleForm.editor.value = data.data.body.content;let tags = this.articleForm.tags.map(function (item){return item.id;});this.articleForm.tags = tags}).catch(error =>{if (error !== 'error'){that.$message({type: 'error', message: '文章加载失败', showClose: true})}})},publishShow(){if (!this.articleForm.title){this.$message({message: '标题不能为空哦', type: 'warning', showClose: true})return}if (this.articleForm.title.length > 30){this.$message({message: '标题不能大于30个字符', type: 'warning', showClose: true})return}/*如果markdown编辑器内容为空*/if (!this.articleForm.editor.ref.d_render){this.$message({message: '内容不能为空哦', type: 'warning', showClose: true})return}/*点击发布,之后的对话框显示*/this.publishVisible = true;},/*文章发布*/publish(articleForm){let that = this;this.$refs[articleForm].validate((valid) =>{if (valid){let tags = this.articleForm.tags.map(function (item){return {id: item};});let article ={id: this.articleForm.id,title: this.articleForm.title,summary: this.articleForm.summary,category: this.articleForm.category,tags: tags,body: {content: this.articleForm.editor.value,contentHtml: this.articleForm.editor.ref.d_render}};this.publishVisible = false;let loading = this.$loading({lock: true,text: '发布中,请稍后...'});publishArticle(article,this.$store.state.token).then((data) =>{if(data.success){loading.close();that.$message({message: '发布成功啦', type: 'success', showClose: true})/*跳转到文章详情*/that.$router.push({path: `/view/${data.data.id}`})}else{that.$message({message: error, type: '发布文章失败:'+data.msg, showClose: true});}}).catch((error) =>{loading.close();if (error !== 'error'){that.$message({message: error, type: 'error', showClose: true});}});} else{return false;}});},cancel() {this.$confirm('文章将不会保存, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$router.push('/')})},/*获取所有标签以及分类*/getCategorysAndTags() {let that = thisgetAllCategorys().then(data =>{if(data.success){that.categorys = data.data}else{that.$message({type: 'error', message: '文章分类加载失败', showClose: true})}}).catch(error =>{if (error !== 'error'){that.$message({type: 'error', message: '文章分类加载失败', showClose: true})}})getAllTags().then(data =>{if(data.success){that.tags = data.data}else{that.$message({type: 'error', message: '标签加载失败', showClose: true})}}).catch(error =>{if (error !== 'error'){that.$message({type: 'error', message: '标签加载失败', showClose: true})}})},editorToolBarToFixed(){let toolbar = document.querySelector('.v-note-op');let curHeight = document.documentElement.scrollTop || document.body.scrollTop;if (curHeight >= 160){document.getElementById("placeholder").style.display = "block"; //bad 用计算属性较好toolbar.classList.add("me-write-toolbar-fixed");} else{document.getElementById("placeholder").style.display = "none";toolbar.classList.remove("me-write-toolbar-fixed");}}},components:{'base-header': BaseHeader,'markdown-editor': MarkdownEditor},//组件内的守卫 调整body的背景色beforeRouteEnter(to, from, next){window.document.body.style.backgroundColor = '#fff';next();},beforeRouteLeave(to, from, next){window.document.body.style.backgroundColor = '#f5f5f5';next();}}
</script><style>.el-header{position: fixed;z-index: 1024;min-width: 100%;box-shadow: 0 2px 3px hsla(0, 0%, 7%, .1), 0 0 0 1px hsla(0, 0%, 7%, .1);}.me-write-info{line-height: 60px;font-size: 18px;font-weight: 600;}.me-write-btn{margin-top: 10px;}.me-write-box{max-width: 700px;margin: 80px auto 0;}.me-write-main{padding: 0;}.me-write-title{}.me-write-input textarea{font-size: 32px;font-weight: 600;height: 20px;border: none;}.me-write-editor{min-height: 650px !important;}.me-header-left{margin-top: 10px;}.me-title img{max-height: 2.4rem;max-width: 100%;}.me-write-toolbar-fixed{position: fixed;width: 700px !important;top: 60px;}.v-note-op{box-shadow: none !important;}.auto-textarea-input, .auto-textarea-block{font-size: 18px !important;}
</style>
这里需要安装lodash插件:
npm i --save lodash
main.js引用:
import lodash from 'lodash'
Object.defineProperty(Vue.prototype, '$_', { value: lodash })
baseHeader:
<template><el-header class="my-area"><el-row class="my-header"><!--头像--><el-col :span="4" class="my-header-left"><router-link to="/" class="my-title"><img src="../../assets/img/logo.png"/></router-link></el-col><el-col v-if="!simple" :span="16"><!--:router=true是否使用vue-router的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转--><el-menu :router=truemenu-trigger="click"active-text-color="#00aaff":default-active="activeIndex"mode="horizontal"><el-menu-item index="/"><i class="el-icon-tickets"></i>首页</el-menu-item><el-menu-item index="/category/all"><i class="el-icon-s-order"></i>文章分类</el-menu-item><el-menu-item index="/tag/all"><i class="el-icon-price-tag"></i>标签</el-menu-item><el-menu-item index="/archives"><i class="el-icon-chat-line-square"></i>文章归档</el-menu-item><!--offset可以指定偏移数--><el-col :span="4" :offset="4"><el-menu-item index="/write"><i class="el-icon-edit"></i>写文章</el-menu-item></el-col></el-menu></el-col><template v-else><slot></slot></template><el-col :span="4"><el-menu :router=truemenu-trigger="click"mode="horizontal"active-text-color="#00aaff"><template v-if="!user.login"><el-menu-item index="/login"><el-button type="text">登录</el-button></el-menu-item><el-menu-item index="/register"><el-button type="text">注册</el-button></el-menu-item></template><template v-else><el-submenu index><template slot="title"><img class="my-header-picture" :src="user.avatar"/></template><el-menu-item index @click="logout"><i class="el-icon-back"></i>退出</el-menu-item></el-submenu></template></el-menu></el-col></el-row></el-header>
</template><script>export default {name: 'BaseHeader',props: {activeIndex: String,//选中哪个menu进行下方条形的一个显示simple: {type: Boolean,default: false}},computed: {user(){let login = this.$store.state.account.length !== 0;let avatar = this.$store.state.avatar;return{login,avatar}}},methods: {logout(){this.$store.dispatch("logout").then((res)=>{this.$router.push({path:"/login"});this.$message({message:"已退出账户,请重新登录",type:"success",showClose:true});}).catch((error)=>{this.$message({message:error,type:"error",showClose:true});})},}}
</script><style>/*登录、注册*/.el-button{color: #00aaff;}/*body*/.el-header{background-color: white;position: fixed;z-index: 1024;min-width: 100%;box-shadow: 0 1px 2px hsla(0, 0%, 7%, .1), 0 0 0 0px hsla(0, 0%, 7%, .1);}/*头像*/.my-title{margin-top: 10px;font-size: 24px;}.my-header-left{margin-top: 10px;}/*左上角头像*/.my-title img{max-height: 2.5rem;max-width: 100%;}.my-header-picture{width: 36px;height: 36px;border: 1px solid #ddd;border-radius: 50%;vertical-align: middle;background-color: #00aaff;}
</style>
markdown需要调用upload接口,将图片上传到七牛云
import request from '@/request'
/*发布文章时,有可能在markdown会上传图片*/
export function upload(formdata) {return request({headers: {'Content-Type': 'multipart/form-data'},url: '/upload',method: 'post',data: formdata})
}
markdown组件:
<template><mavon-editorclass="my-editor"ref="md"v-model="editor.value"@imgAdd="imgAdd"v-bind="editor"></mavon-editor>
</template><script>import {mavonEditor} from 'mavon-editor'import 'mavon-editor/dist/css/index.css'import {upload} from '@/api/upload'export default {name: 'MarkdownEditor',props: {editor: Object},data() {return {}},mounted() {this.$set(this.editor, 'ref', this.$refs.md)},methods: {/*图片上传*/imgAdd(pos, $file){let that = thislet formData = new FormData();formData.append('image', $file);upload(formData).then(res =>{// 第二步.将返回的url替换到文本原位置 -> if (res.data.success){that.$refs.md.$img2Url(pos, res.data.data);} else {that.$message({message: res.data.msg, type: 'error', showClose: true})}}).catch(err => {that.$message({message: err, type: 'error', showClose: true});})}},components: {mavonEditor}}
</script>
<style scoped>.my-editor{z-index: 6 !important;}.v-note-wrapper.fullscreen{top: 60px !important}
</style>


















