# 五、文章发布## 创建组件并配置路由1、创建 `src/views/publish/index.vue` 组件```html
<template><div class="publish-container">发布文章</div>
</template><script>
export default {name: 'PublishIndex',components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less"></style>```2、配置页面路由<img src="assets/image-20200425162406736.png" alt="image-20200425162406736" style="zoom:50%;" />3、访问测试## 页面布局```html
<template><div class="publish-container"><el-card class="box-card"><div slot="header" class="clearfix"><!-- 面包屑路径导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item to="/">首页</el-breadcrumb-item><el-breadcrumb-item>发布文章</el-breadcrumb-item></el-breadcrumb><!-- /面包屑路径导航 --></div><el-form ref="form" :model="form" label-width="40px"><el-form-item label="标题"><el-input v-model="form.name"></el-input></el-form-item><el-form-item label="内容"><el-input type="textarea" v-model="form.desc"></el-input></el-form-item><el-form-item label="封面"><el-radio-group v-model="form.resource"><el-radio label="单图"></el-radio><el-radio label="三图"></el-radio><el-radio label="无图"></el-radio><el-radio label="自动"></el-radio></el-radio-group></el-form-item><el-form-item label="频道"><el-select v-model="form.region" placeholder="请选择频道"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="onSubmit">发表</el-button><el-button>存入草稿</el-button></el-form-item></el-form></el-card></div>
</template><script>
export default {name: 'PublishIndex',components: {},props: {},data () {return {form: {name: '',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''}}},computed: {},watch: {},created () {},mounted () {},methods: {onSubmit () {console.log('submit!')}}
}
</script><style scoped lang="less"></style>```## 处理表单数据绑定> 思路:根据后端接口的要求处理绑定数据绑定```html
<template><div class="publish-container"><el-card class="box-card"><div slot="header" class="clearfix"><!-- 面包屑路径导航 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item to="/">首页</el-breadcrumb-item><el-breadcrumb-item>发布文章</el-breadcrumb-item></el-breadcrumb><!-- /面包屑路径导航 --></div><el-form ref="form" :model="form" label-width="40px"><el-form-item label="标题"><el-input v-model="article.title"></el-input></el-form-item><el-form-item label="内容"><el-input type="textarea" v-model="article.content"></el-input></el-form-item><el-form-item label="封面"><el-radio-group v-model="article.cover.type"><el-radio :label="1">单图</el-radio><el-radio :label="3">三图</el-radio><el-radio :label="0">无图</el-radio><el-radio :label="-1">自动</el-radio></el-radio-group></el-form-item><el-form-item label="频道"><el-select v-model="form.region" placeholder="请选择频道"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="onSubmit">发表</el-button><el-button>存入草稿</el-button></el-form-item></el-form></el-card></div>
</template><script>
export default {name: 'PublishIndex',components: {},props: {},data () {return {form: {name: '',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''},article: {title: '', // 文章标题content: '', // 文章内容cover: { // 文章封面type: 0, // 封面类型 -1:自动,0-无图,1-1张,3-3张images: [] // 封面图片的地址}}}},computed: {},watch: {},created () {},mounted () {},methods: {onSubmit () {console.log('submit!')}}
}
</script><style scoped lang="less"></style>```## 处理文章频道数据一、展示频道列表1、请求获取频道列表数据<img src="assets/image-20200425163038862.png" alt="image-20200425163038862" style="zoom:50%;" />2、绑定到模板中展示<img src="assets/image-20200425163137597.png" alt="image-20200425163137597" style="zoom:50%;" />二、将频道绑定到表单元素选择器1、在 article 中添加频道数据字段<img src="assets/image-20200425163209293.png" alt="image-20200425163209293" style="zoom:50%;" />2、将数据绑定到频道选择器中<img src="assets/image-20200425163242141.png" alt="image-20200425163242141" style="zoom:50%;" />## 发布文章一、封装数据接口```js
/*** 新建文章*/
export const addArticle = (data, draft = false) => {return request({method: 'POST',url: '/mp/v1_0/articles',params: {draft // 是否存为草稿(true 为草稿)},data})
}
```二、在发布文章组件中请求调用1、加载请求方法<img src="assets/image-20200425163618795.png" alt="image-20200425163618795" style="zoom:50%;" />2、给发布和存入草稿绑定事件处理函数<img src="assets/image-20200425163526654.png" alt="image-20200425163526654" style="zoom:50%;" />3、处理函数如下```js
onPublish (draft = false) {// 找到数据接口// 封装请求方法// 请求提交表单addArticle(this.article, draft).then(res => {// 处理响应结果// console.log(res)this.$message({message: '发布成功',type: 'success'})})
}
```## 编辑文章### 展示编辑文章内容1、封装获取文章的数据接口```js
/*** 获取指定文章*/
export const getArticle = articleId => {return request({method: 'GET',url: `/mp/v1_0/articles/${articleId}`})
}
```2、点击编辑,导航到发布文章页面<img src="assets/image-20200425163826162.png" alt="image-20200425163826162" style="zoom:50%;" />3、在发布文章页面组件中,处理加载编辑文章内容<img src="assets/image-20200425164127166.png" alt="image-20200425164127166" style="zoom:50%;" /><img src="assets/image-20200425164215467.png" alt="image-20200425164215467" style="zoom:50%;" />### 提交更新1、封装更新文章的数据接口```js
/*** 编辑文章*/
export const updateArticle = (articleId, data, draft = false) => {return request({method: 'PUT',url: `/mp/v1_0/articles/${articleId}`,params: {draft // 是否存为草稿(true 为草稿)},data})
}
```2、加载请求方法<img src="assets/image-20200425164349629.png" alt="image-20200425164349629" style="zoom:50%;" />3、修改发布文章的处理逻辑```js
onPublish (draft = false) {// 找到数据接口// 封装请求方法// 请求提交表单// 如果是修改文章,则执行修改操作,否则执行添加操作const articleId = this.$route.query.idif (articleId) {// 执行修改操作updateArticle(articleId, this.article, draft).then(res => {console.log(res)this.$message({message: `${draft ? '存入草稿' : '发布'}成功`,type: 'success'})// 跳转到内容管理页面this.$router.push('/article')})} else {addArticle(this.article, draft).then(res => {// 处理响应结果// console.log(res)this.$message({message: `${draft ? '存入草稿' : '发布'}成功`,type: 'success'})// 跳转到内容管理页面this.$router.push('/article')})}
},
```## 使用富文本编辑器基于 Vue 的富文本编辑器有很多,例如官方就收录推荐了一些: https://github.com/vuejs/awesome-vue#rich-text-editing 。这里我们以 element-tiptap 为例。- GitHub 仓库:https://github.com/Leecason/element-tiptap
- 在线示例:https://leecason.github.io/element-tiptap
- 中文文档:https://github.com/Leecason/element-tiptap/blob/master/README_ZH.md1、安装```shell
npm i element-tiptap
```2、初始配置```html
<template><el-tiptap v-model="content" :extensions="extensions"></el-tiptap>
</template><script>import {ElementTiptap,Doc,Text,Paragraph,Heading,Bold,Underline,Italic,Image,Strike,ListItem,BulletList,OrderedList,TodoItem,TodoList,HorizontalRule,Fullscreen,Preview,CodeBlock
} from 'element-tiptap'
import 'element-tiptap/lib/index.css'export default {components: {'el-tiptap': ElementTiptap},data () {return {content: 'hello world',extensions: [new Doc(),new Text(),new Paragraph(),new Heading({ level: 3 }),new Bold({ bubble: true }), // 在气泡菜单中渲染菜单按钮new Image(),new Underline(), // 下划线new Italic(), // 斜体new Strike(), // 删除线new HorizontalRule(), // 华丽的分割线new ListItem(),new BulletList(), // 无序列表new OrderedList(), // 有序列表new TodoItem(),new TodoList(),new Fullscreen(),new Preview(),new CodeBlock()]}}
}
</script>```## 处理富文本编辑器中的图片<img src="assets/image-20200426121121725.png" alt="image-20200426121121725" style="zoom:50%;" />1、创建 `src/api/image.js` 封装数据接口```js
/*** 素材请求相关模块*/import request from '@/utils/request'/*** 上传图片素材*/
export const uploadImage = data => {return request({method: 'POST',url: '/mp/v1_0/user/images',// 一般文件上传的接口都要求把请求头中的 Content-Type 设置为 multipart/form-data,但是我们使用 axios 上传文件的话不需要手动设置,你只要给 data 一个 FormData 对象即可。// new FormData()data})
}```2、自定义图片上传到服务器<img src="assets/image-20200426173740077.png" alt="image-20200426173740077" style="zoom:50%;" />## 表单验证处理<img src="assets/image-20200426173930626.png" alt="image-20200426173930626" style="zoom:50%;" />验证规则配置对象:```js
formRules: {title: [{ required: true, message: '请输入文章标题', trigger: 'blur' },{ min: 5, max: 30, message: '长度在 5 到 30 个字符', trigger: 'blur' }],content: [// { required: true, message: '请输入文章内容', trigger: 'change' }{validator (rule, value, callback) {console.log('content validator')if (value === '<p></p>') {// 验证失败callback(new Error('请输入文章内容'))} else {// 验证通过callback()}}},{ required: true, message: '请输入文章内容', trigger: 'blur' }],channel_id: [{ required: true, message: '请选择文章频道' }]
}
```## 文章封面> 该业务功能比较复杂,需要自定义封装组件,所以放到项目最后讲解。1、创建 `src/views/publish/components/upload-image.vue` 组件并写入```html
<template><div>上传图片组件</div>
</template><script>export default {name: "UploadImage",components: {},props: {},data() {return {};},computed: {},watch: {},created() {},methods: {}};
</script><style scoped></style>
```2、在文章发布中加载使用![image-20191121151753691](assets/image-20191121151753691.png)> 注册![image-20191121154827697](assets/image-20191121154827697.png)> 在模板中使用### 使用对话框```html
<template><div class="upload-image"><div class="preview" @click="centerDialogVisible=true"><!-- <img src="" class="avatar"> --><i class="el-icon-plus avatar-uploader-icon"></i></div><!--visible 控制对话框的显示和隐藏--><el-dialogtitle="请选择文章封面图片":visible.sync="centerDialogVisible"width="30%"center><span slot="footer" class="dialog-footer"><el-button @click="centerDialogVisible = false">取 消</el-button><el-button type="primary" @click="centerDialogVisible = false">确 定</el-button></span></el-dialog></div>
</template><script>export default {name: "UploadImage",components: {},props: {},data() {return {centerDialogVisible: false};},computed: {},watch: {},created() {},methods: {}};
</script><style scoped>.upload-image {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.upload-image .el-upload:hover {border-color: #409eff;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;}.avatar {width: 178px;height: 178px;display: block;}
</style>
```### 展示素材库```html
<template><div class="upload-image"><div class="preview" @click="onUploadShow"><!-- <img src="" class="avatar"> --><i class="el-icon-plus avatar-uploader-icon"></i></div><!--visible 控制对话框的显示和隐藏--><el-dialogtitle="请选择文章封面图片":visible.sync="centerDialogVisible"width="50%"center><!-- 标签导航 --><!--el-tabs 组件v-model 双向绑定数据驱动视图:当绑定数据发生改变,激活的标签页受影响视图影响数据:当标签改变的时候,标签的 name 会同步到数据中label 标签的标题name 相当于标签的 id--><el-tabs v-model="activeName"><el-tab-pane label="素材库" name="first"><!-- 标签内容写到里面来 --><!--radio 有个 change 事件当选择的 radio 改变的时候会触发--><el-radio-group v-model="activeImage" @change="loadImages"><el-radio label="all">全部</el-radio><el-radio label="collect">收藏</el-radio></el-radio-group><el-row :gutter="20"><el-col :span="6" v-for="item in images" :key="item.id"><img height="100" :src="item.url" /></el-col></el-row></el-tab-pane><el-tab-pane label="上传图片" name="second">配置管理</el-tab-pane></el-tabs><!-- /标签导航 --><span slot="footer" class="dialog-footer"><el-button @click="centerDialogVisible = false">取 消</el-button><el-button type="primary" @click="centerDialogVisible = false">确 定</el-button></span></el-dialog></div>
</template><script>export default {name: "UploadImage",components: {},props: {},data() {return {centerDialogVisible: false, // 对话框的显示状态activeName: "first", // 激活的标签页activeImage: "all", // 激活的图片筛选类型images: []};},computed: {},watch: {},created() {console.log(123);},methods: {onUploadShow() {// 请求加载数据this.loadImages();// 显示弹窗this.centerDialogVisible = true;},loadImages() {this.$axios({method: "GET",url: "/user/images",params: {// this.activeImage 双向绑定了 radio 选择框组// 所以获取 this.activeImage 也就是在获取选中的那个 radio 的状态collect: this.activeImage === "collect" // 是否获取收藏图片}}).then(res => {this.images = res.data.data.results;}).catch(err => {console.log(err);});}}};
</script><style scoped>.upload-image {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.upload-image .el-upload:hover {border-color: #409eff;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;}.avatar {width: 178px;height: 178px;display: block;}
</style>
```### 处理选择图片1、添加一个数据字段用来存储当前点击的图片项的索引![image-20191121171951851](assets/image-20191121171951851.png)2、模板绑定![image-20191121172033149](assets/image-20191121172033149.png)### 数据绑定1、在父组件中绑定数组元素给图片上传组件![image-20191121171755076](assets/image-20191121171755076.png)2、在上传图片组件中![image-20191121171811156](assets/image-20191121171811156.png)> 声明 value 接收数据![image-20191121171832121](assets/image-20191121171832121.png)> 点击对话框确认触发:将所选的图片路径发送给父组件### 上传图片1、添加上传图片组件![image-20191121174046912](assets/image-20191121174046912.png)2、记录选择的上传图片![image-20191121174107207](assets/image-20191121174107207.png)3、对话框确定的时候![image-20191121174133611](assets/image-20191121174133611.png)## 禁用路由缓存我们发现一个小问题,从编辑文章导航到发布文章,表单内容并没有被清空,这是因为两个路由共用的同一个组件,两者之间相互跳转的时候,原来的组件实例会被复用。 因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。**不过,这也意味着组件的生命周期钩子不会再被调用**。路由默认提供的这个功能的用意是好的,但是有时候却会带来问题,解决方案就是:**禁用缓存**。在路由出口 `router-view` 上添加一个唯一的 `key` 即可。![image-20191118155038989](assets/image-20191118155038989.png)> 详细内容请查阅官方文档:[响应路由参数的变化](<[https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%93%8D%E5%BA%94%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E7%9A%84%E5%8F%98%E5%8C%96](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化)>)。

前端学习(2457):文章发布相关推荐

  1. 入门前端学习路线图【送书】

    大家好,我是若川.记得点上方音频听小姐姐配音,超级好听. 华章图书又赞助了书籍送福利给大家.本次送4本书的抽奖方式是:截止到9月6日(周一)20:00,在留言区留言任意内容.我会在留言区抽取「1位」关 ...

  2. 资深前端开发工程师分享:关于前端学习路线的一些建议

    前言 对于刚开始学习前端的伙伴俩说,问得最多的问题就是 --前端技术现在如此繁杂,我到底应该如何学习.这个话题太大了,几句话回答不好:也由于这个问题确实困扰了很多前端开发人员,所以我也就着手系统的输出 ...

  3. 关于前端学习路线的一些建议(值得零基础拥有)

    前言 对于刚开始学习前端的伙伴俩说,问得最多的问题就是 --前端技术现在如此繁杂,我到底应该如何学习.这个话题太大了,几句话回答不好:也由于这个问题确实困扰了很多前端开发人员,所以我也就着手系统的输出 ...

  4. Spring Boot 综合示例-整合thymeleaf、mybatis、shiro、logging、cache开发一个文章发布管理系统...

    一.概述 经过HelloWorld示例(Spring Boot 快速入门(上)HelloWorld示例)( Spring Boot  快速入门 详解 HelloWorld示例详解)两篇的学习和练习,相 ...

  5. 前端学习总结,经验分享,项目经验分享过程

    前言 来,和魔王哪吒总结一下,分享一下自己对前端学习总结,经验分享,以及写过的项目经验分享过程. 如果觉得还不错的话,浏览的过程中,需要您: 点赞,分享,评论 有钱的捧个钱场,没钱的捧个人场 技术实践 ...

  6. 2020 - 2021 年 Web 前端最新导航 - 前端学习资源分享前端面试资源汇总

    前端javascriptvue.jses6typescript 发布于 10月9日 国庆这几天,我收集了大量可以显著提升开发效率的前端导航链接. 这些导航链接对我很有帮助,希望对你也是如此. 这些好用 ...

  7. Java EE Web开发与项目实战_【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第一节)...

    312334546574820.jpg 序 前一段时间有很多朋友来问我各种问题,比如java怎么学,c,c++,c#,java,还有php等等,到底学哪个好,哪个好就业?其中不乏刚毕业找不到工作的学生 ...

  8. 2021年万众瞩目的前端学习路线图来了

    文章目录 第一阶段:前端入门HTML5 +Css3+电商网页制作 第二阶段:JavaScript系列 第三阶段 服务端编程 第四阶段 Vue.js 第五阶段 小程序 第六阶段 React.js 基础 ...

  9. 文章发布系统的设计与实现

    摘 要 随着计算机技术的迅速发展,网络正以一种前所未有的冲击力影响着人类的生产和生活.网络的快速发展,颠覆了传统的信息传播方式,冲破了传统的时间,空间的局限性,继而引发了人类阅读方式的变革.现如今,网 ...

最新文章

  1. c语言 迷宫深度遍历 算法,图的遍历迷宫生成算法浅析
  2. bzoj3224: Tyvj 1728 普通平衡树(splay)
  3. 安装ESXI 5.1
  4. elasticsearch 6.x.x 获取客户端方法
  5. 具有Spring Boot和数据功能的Java头优先弹性搜索
  6. Struts2初始化流程及源码分析
  7. cocos2d- AtlasSprite AtlasSpriteManager
  8. 关于RDP报表工具参数配置
  9. ppt保存为高分辨率图片(ppt2016)
  10. 革新科技CIDE-EDA:实验1 3-8译码器
  11. HTML5页面增强元素
  12. 计算机网络共享文件密码,如何给局域网共享文件夹设置访问密码
  13. 【JPress】Menu
  14. 【10个精品网站】找素材、找图片、PDF在线工具、免费图片降噪修复、免费可商用字体、PPT模板
  15. Prometheus企业级监控——理论入门
  16. 性能测试分析之-CPU时间分类与饱和度
  17. vaex库使用方法python_Python秒开100G数据是怎么办到的?
  18. centos7中安装pip
  19. 地球椭球体与大地基准面的关系
  20. Excel2007右键被禁用的恢复方法

热门文章

  1. SpringBoot声明式事务
  2. [BZOJ4008]亚瑟王
  3. 如何理解JavaScript中给变量赋值,是引用还是复制
  4. There is no public key available for the following key IDs:3B4FE6ACC0B21F32
  5. 绑定注意事项——数据源的属性
  6. Metropolis Hasting算法
  7. 另一个域的cookie_一定要知道的第一方Cookie和第三方Cookie
  8. python读写磁盘扇区数据_C++-如何直接读取Windows磁盘扇区的数据?
  9. python中add_Python add()函数是如何使用呢?
  10. 带码农《手写Mybatis》进度3:实现映射器的注册和使用