一、配置子路由
{
name: 'index',
path: '/index',
// 异步引入组件
component: () => import('@/views/index.vue'),
redirect: { name: 'postList' },
children: [ {
name: 'PostPublish',
path: 'PostPublish',//无需添加斜杠
component: () => import('@/views/PostPublish.vue')
},
]
},
二、创建组件并在首页设置跳转路由
创建单独的发布文章组件
PostPublish.vue
,并在首页左侧二级菜单的index
设置路由跳转.
<template>
<div class="PostPublish">文章发布</div>
</template>
<el-menu-item index="PostPublish">
<i class="el-icon-user"></i>
<span>文章发布</span>
</el-menu-item>
效果
三、发布文章组件
需要用到的
element
组件:
1.Breadcrumb
面包屑
2.Card
卡片
3.Button
按钮
4.Form
表单
5.Radio
单选框
5.Upload
上传
富文本框:富文本框下载及解说
3.1插入面包屑组件
<!-- 路径导航 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/index' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>文章管理</el-breadcrumb-item>
<el-breadcrumb-item>文章发布</el-breadcrumb-item>
</el-breadcrumb>
3.1.1面包屑效果
3.2卡片视图
<!-- 卡片视图 -->
<el-card class="box-card" style="margin-top: 20px"> </el-card>
3.3定义数据
发布文章基本的就是收集数据,那在最初就先把需要的参数设置好
export default {
data() {
return {
postFrom: {
title: "", //文章标题
content: "", //文章内容
categories: [], //所属栏目id集合
cover: [], //封面图片id集合
type:1, //1为文章,2为视频,默认选文章
},
};
},
};
3.4收集标题数据
卡片内填写代码,将
from
表单包裹input
,:model
动态绑定数据对象,点击按钮获取数据
<!-- 卡片视图 -->
<el-card class="box-card" style="margin-top: 20px">
<!-- 用from表单包裹 -->
<!-- :model:绑定的数据 -->
<el-form ref="postFrom" :model="postFrom" label-width="80px">
<!-- 标题 -->
<el-form-item label="标题">
<el-input v-model="postFrom.title"></el-input> </el-form-item
></el-form>
<!-- 类型 -->
<!-- 内容 -->
<!-- 栏目 -->
<!-- 封面 -->
<!-- 按钮 -->
<!-- 点击发送事件 -->
<el-button type="success" @click="publishPost">发布文章</el-button>
</el-card>
methods: {
publishPost() {
console.log(this.postFrom);
},
},
3.4.1标题数据效果
3.5收集类型数据
v-model
:是当前被选中的单选按钮的label
,得定义一个变量进行存储,后期等
用户选中单选按钮的时候,就会将这个被选中的单选按钮label
值赋值给v-model
所绑定的属性.
<!-- 类型 -->
<!-- v-model:是当前被选中的单选按钮的label,得定义一个变量进行存储,后期等
用户选中单选按钮的时候,就会将这个被选中的单选按钮label值赋值给v-model所绑定的属性 -->
<el-form-item label="类型">
<el-radio-group v-model="postFrom.type">
<el-radio :label="1">文章</el-radio>
<el-radio :label="2">视频</el-radio>
</el-radio-group>
</el-form-item>
3.5.1类型数据效果
3.6收集文章内容数据
3.6.1富文本框下载安装
npm install vue-word-editor --save
地址:https://github.com/hsian/vue-word-editor
3.6.2引入富文本框
//引入富文本框
import VueEditor from "vue-word-editor";
import "quill/dist/quill.snow.css";
3.6.3注册富文本框
components: {
VueEditor, //注册富文本框
},
3.6.4使用富文本框
<!-- 内容 -->
<el-form-item label="内容">
<!-- 使用富文本框 -->
<!-- 给组件添加ref标识 -->
<!-- v-if:类别为1才显示 -->
<VueEditor
:config="config"
ref="vueEditor"
v-if="postFrom.type == 1"
/>
<!-- v-if:类别为2才显示 -->
<!-- headers:传递token -->
<!-- action:上传的地址,必传 -->
<!-- file-list:当前用户所选择文件列表 -->
<!-- on-success:视频上传成功之后调用钩子函数 -->
<el-upload
class="upload-demo"
v-if="postFrom.type == 2"
:headers="getToken()"
:action="baseURL + '/upload'"
:file-list="fileList"
:on-success="onsuccess"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传视频文件</div>
</el-upload>
</el-form-item>
3.6.5在data中引入必要属性
上传文件需要验证用户信息,意味着需要拦截器.
但几乎所有框架都不会用用户封装的拦截器,因为它们自己封装了内部请求,意味着我们添加的拦截器不会经过,所以需要自己设置请求头(header)
进行token
的传递.
因为图片跟视频都需要用信息,所以做了个简单封装
图片路径为选择的图片在服务器上的路径
需要做的是:
1.将图片上传到服务器
2.接收服务器返回的地址
3.填充
baseURL: axios.defaults.baseURL, //http://localhost:3000
fileList: [],
// 富文本框的必要配置
config: {
// 上传图片的配置
uploadImage: {
url: axios.defaults.baseURL + "/upload", //文章上传的服务器端的处理路径
// url: "http://localhost:3000/upload",
name: "file", //后台所需要的参数名称
//请求头
headers: this.getToken(),
// res是结果,insert方法会把内容注入到编辑器中,res.data.url是资源地址
uploadSuccess(res, insert) {
//文件上传成功之后的回调
insert(axios.defaults.baseURL + res.data.data.url);
// insert("http://localhost:3000" + res.data.data.url)
console.log(res, insert);
},
},
// 上传视频的配置
uploadVideo: {
url: axios.defaults.baseURL + "/upload",
// url: "http://localhost:3000/upload",
name: "file",
uploadSuccess(res, insert) {
insert(axios.defaults.baseURL + res.data.url);
// insert("http://localhost:3000" + res.data.data.url)
},
},
},
methods: {
publishPost() {
// 如果类型为文章, 那将标识为vueEditor的文本框内容赋值给content;
if (this.postFrom.type == 1) {
this.postFrom.content = this.$refs.vueEditor.editor.root.innerHTML;
}
console.log(this.postFrom);
},
getToken() {
return {
// 获取存储的token
Authorization: JSON.parse(localStorage.getItem("usermanager")).token,
};
},
// 视频上传成功之后调用钩子函数
onsuccess(response, file, fileList) {
console.log(response, file, fileList);
if (response.message == "文件上传成功") {
//类型为视频的时候,content中存储的内容就是视频在服务器中的全路径
this.postFrom.content = axios.defaults.baseURL + response.data.url;
}
},
},
3.6.6富文本框文字效果
3.6.7富文本框图片文件上传效果
3.6.8富文本框视频文件上传效果
3.7封面处理
1.
on-exceed
限制最大三张图片,超出给提示
2.limit
:限制图片个数
3.上传成功后返回图片路径
4.处理细节,选中图片删除后要把数据也一并删除
<!-- 封面 -->
<!-- list-type:文件列表的类型 -->
<!-- limit: 最大允许上传个数 -->
<!-- on-exceed:文件超出个数限制时的钩子 -->
<!-- on-preview:点击文件列表中已上传的文件时的钩子 -->
<!-- on-remove:文件列表移除文件时的钩子 -->
<el-form-item label="封面">
<el-upload
:action="baseURL + '/upload'"
:headers="getToken()"
list-type="picture-card"
:limit="3"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:on-success="onPosterSucess"
:on-remove="onPosterRemove"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="userimg" alt="" />
</el-dialog>
</el-form-item>
date里添加
dialogVisible: false,
userimg: "",
methods里添加
//移出封面:重点是将对用的图片数据从postFrom.cover移出
//file:当前移出的
//fileList:现在还保留的
onPosterRemove(file, fileList) {
let id = file.response.data.id;
for (let i = 0; i < this.postFrom.cover.length; i++) {
if (this.postFrom.cover[i].id == id) {
this.postFrom.cover.splice(i, 1);
break;
}
}
},
//处理图片上传后操作
onPosterSucess(response) {
console.log(response);
if (response.message == "文件上传成功") {
this.postFrom.cover.push({ id: response.data.id });
}
},
//实现封面的预览
handlePictureCardPreview(file) {
console.log(file);
this.userimg = file.url;
this.dialogVisible = true;
},
// 文件超出个数限制时的钩子
handleExceed(files) {
console.log(files);
this.$message.warning("最多只能选择三张图片");
},
3.7.1封面效果
四、发送文章组件全部代码
<template>
<div class="PostPublish">
<!-- 路径导航 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/index' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>文章管理</el-breadcrumb-item>
<el-breadcrumb-item>文章发布</el-breadcrumb-item>
</el-breadcrumb>
<!-- 卡片视图 -->
<el-card class="box-card" style="margin-top: 20px">
<!-- 用from表单包裹 -->
<!-- :model:绑定的数据 -->
<el-form ref="postFrom" :model="postFrom" label-width="80px">
<!-- 标题 -->
<el-form-item label="标题">
<el-input v-model="postFrom.title"></el-input>
</el-form-item>
<!-- 类型 -->
<!-- v-model:是当前被选中的单选按钮的label,得定义一个变量进行存储,后期等
用户选中单选按钮的时候,就会将这个被选中的单选按钮label值赋值给v-model所绑定的属性 -->
<el-form-item label="类型">
<el-radio-group v-model="postFrom.type">
<el-radio :label="1">文章</el-radio>
<el-radio :label="2">视频</el-radio>
</el-radio-group>
</el-form-item>
<!-- 内容 -->
<el-form-item label="内容">
<!-- 使用富文本框 -->
<!-- 给组件添加ref标识 -->
<!-- v-if:类别为1才显示 -->
<VueEditor
:config="config"
ref="vueEditor"
v-if="postFrom.type == 1"
/>
<!-- v-if:类别为2才显示 -->
<!-- headers:传递token -->
<!-- action:上传的地址,必传 -->
<!-- file-list:当前用户所选择文件列表 -->
<!-- on-success:视频上传成功之后调用钩子函数 -->
<el-upload
class="upload-demo"
v-if="postFrom.type == 2"
:headers="getToken()"
:action="baseURL + '/upload'"
:file-list="fileList"
:on-success="onsuccess"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传视频文件</div>
</el-upload>
</el-form-item>
<!-- 栏目 -->
<!-- 封面 -->
<!-- list-type:文件列表的类型 -->
<!-- limit: 最大允许上传个数 -->
<!-- on-exceed:文件超出个数限制时的钩子 -->
<!-- on-preview:点击文件列表中已上传的文件时的钩子 -->
<!-- on-remove:文件列表移除文件时的钩子 -->
<el-form-item label="封面">
<el-upload
:action="baseURL + '/upload'"
:headers="getToken()"
list-type="picture-card"
:limit="3"
:on-exceed="handleExceed"
:on-preview="handlePictureCardPreview"
:on-success="onPosterSucess"
:on-remove="onPosterRemove"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="userimg" alt="" />
</el-dialog>
</el-form-item>
<!-- 按钮 -->
</el-form>
<!-- 点击发送事件 -->
<el-button type="success" @click="publishPost">发布文章</el-button>
</el-card>
</div>
</template>
<script>
//引入富文本框
import VueEditor from "vue-word-editor";
import "quill/dist/quill.snow.css";
import axios from "@/utils/myaxios";
export default {
data() {
return {
dialogVisible: false,
userimg: "",
baseURL: axios.defaults.baseURL, //http://localhost:3000
fileList: [],
postFrom: {
title: "", //文章标题
content: "", //文章内容
categories: [], //所属栏目id集合
cover: [], //封面图片id集合
type: 1, //1为文章,2为视频,默认选文章
},
// 富文本框的必要配置
config: {
// 上传图片的配置
uploadImage: {
url: axios.defaults.baseURL + "/upload", //文章上传的服务器端的处理路径
// url: "http://localhost:3000/upload",
name: "file", //后台所需要的参数名称
//请求头
headers: this.getToken(),
// res是结果,insert方法会把内容注入到编辑器中,res.data.url是资源地址
uploadSuccess(res, insert) {
//文件上传成功之后的回调
insert(axios.defaults.baseURL + res.data.data.url);
// insert("http://localhost:3000" + res.data.data.url)
console.log(res, insert);
},
},
// 上传视频的配置
uploadVideo: {
url: axios.defaults.baseURL + "/upload",
// url: "http://localhost:3000/upload",
name: "file",
uploadSuccess(res, insert) {
insert(axios.defaults.baseURL + res.data.url);
// insert("http://localhost:3000" + res.data.data.url)
},
},
},
};
},
components: {
VueEditor, //注册富文本框
},
methods: {
//移出封面:重点是将对用的图片数据从postFrom.cover移出
//file:当前移出的
//fileList:现在还保留的
onPosterRemove(file, fileList) {
let id = file.response.data.id;
for (let i = 0; i < this.postFrom.cover.length; i++) {
if (this.postFrom.cover[i].id == id) {
this.postFrom.cover.splice(i, 1);
break;
}
}
},
//处理图片上传后操作
onPosterSucess(response) {
console.log(response);
if (response.message == "文件上传成功") {
this.postFrom.cover.push({ id: response.data.id });
}
},
//实现封面的预览
handlePictureCardPreview(file) {
console.log(file);
this.userimg = file.url;
this.dialogVisible = true;
},
// 文件超出个数限制时的钩子
handleExceed(files) {
console.log(files);
this.$message.warning("最多只能选择三张图片");
},
publishPost() {
// 如果类型为文章, 那将标识为vueEditor的文本框内容赋值给content;
if (this.postFrom.type == 1) {
this.postFrom.content = this.$refs.vueEditor.editor.root.innerHTML;
}
console.log(this.postFrom);
},
getToken() {
return {
// 获取存储的token
Authorization: JSON.parse(localStorage.getItem("usermanager")).token,
};
},
// 视频上传成功之后调用钩子函数
onsuccess(response, file, fileList) {
console.log(response, file, fileList);
if (response.message == "文件上传成功") {
//类型为视频的时候,content中存储的内容就是视频在服务器中的全路径
this.postFrom.content = axios.defaults.baseURL + response.data.url;
}
},
},
};
</script>
<style lang="less" scoped>
</style>