27.blog前端-发布文章

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

点击右上角的发布,会弹出发布的对话框因此,我们要调用文章分类列表和文章标签列表的接口

以及如果是点击文字的编辑按钮进入该页面的话,还需要通过调用文章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"
                                  autosize
                                  v-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=false
                       custom-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, // code
                            fullscreen: 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 = this
                getAllCategorys().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=true
                         menu-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=true
                         menu-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-editor
            class="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 = this
                let formData = new FormData();
                formData.append('image', $file);

                upload(formData).then(res =>
                {
                    // 第二步.将返回的url替换到文本原位置![...](./0) -> ![...](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>

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值