路由配置
加一个路由规则:
const routes = [// 最大级别的规则,对应的组件,会显示在 App.vue 中// 登录{ path: '/login', component: () => import('@/views/Login.vue') },// 注册{ path: '/reg', component: () => import('@/views/Reg.vue') },// 后台主页{path: '/',component: () => import('@/views/Home.vue'),children: [{ path: 'home', component: () => import('@/views/Chart.vue') },{ path: 'user-info', component: () => import('@/views/user/UserInfo.vue') },{ path: 'user-pwd', component: () => import('@/views/user/RePwd.vue') },{ path: 'user-avatar', component: () => import('@/views/user/UserAvatar.vue') },{ path: 'art-cate', component: () => import('@/views/article/CateList.vue') },+ { path: 'art-list', component: () => import('@/views/article/ArtList.vue') }]}]
页面布局
<template>
<!-- 保留一个最大的div,因为页面有卡片、有弹层等 -->
<div>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>文章列表</span>
</div>
<!-- 内容区 -->
<!-- 内容区一:表单搜索区 -->
<div class="search-box">
<!-- inline属性,控制表单项,以水平方式排列 -->
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="文章分类">
<el-select placeholder="活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="文章状态">
<el-select 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">查询</el-button>
<el-button type="info">重置</el-button>
</el-form-item>
</el-form>
<el-button type="primary" class="add-btn">添加文章</el-button>
</div>
<!-- 内容区二:表格区,展示数据 -->
<!-- 内容区三:分页区 -->
</el-card>
</div>
</template>
<script>
export default {}
</script>
<style lang="less" scoped>
.search-box {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.add-btn {
margin-top: 3px;
}
</style>
全屏的对话框
复制一个对话框,内容区留下一个 <span>这是一段信息</span>,其他全部删除。
对话框保留或新增的属性如下:
<!-- 添加文章的对话框 start -->
<el-dialog
title="发布文章"
:visible.sync="dialogVisible"
:before-close="handleClose"
fullscreen
>
<span>这是一段信息</span>
</el-dialog>
<!-- 添加文章的对话框 end -->
JS中,必须设置好数据项、方法:
export default {
data() {
return {
// 控制添加文章的对话显示 | 隐藏
dialogVisible: false
}
},
methods: {
// 关闭添加对话框之前的操作
handleClose(done) {
// done() // 调用done()可以关闭对话框
this.dialogVisible = false // 通过修改数据项,也可以关闭对话框
}
}
}
最后,点击添加按钮的时候,修改数据项,让对话框显示:
<el-button type="primary" class="add-btn" @click="dialogVisible = true">添加文章</el-button>
处理标题和分类
设置好对应的数据项和验证规则:
data() {
return {
// 控制添加文章的对话显示 | 隐藏
dialogVisible: false,
// 添加文章的数据项,用于存储添加表单中的每一项数据
addData: {
title: '', // 文章标题
cate_id: '' // 下拉框的值,取值为分类id(为什么叫做 cate_id,因为接口文档的字段叫cate_id)
},
// 验证规则对象
rules: {
title: [
{ required: true, message: '标题必填', trigger: 'blur' },
{ min: 1, max: 30, message: '标题长度1~30位', trigger: ['change', 'blur'] }
],
cate_id: [
{ required: true, message: '必须选择一个分类', trigger: ['blur', 'change'] },
{
pattern: /^[1-9][0-9]*$/,
message: '分类id必须是大于0的整数',
trigger: ['blur', 'change']
}
]
}
}
}
对话框中,放表单,表单中先设置标题和选择分类这两项:
<!-- 添加的表单 start -->
<el-form label-width="80px" :model="addData" :rules="rules">
<!-- 第一行:文章标题 -->
<el-form-item label="文章标题" prop="title">
<el-input v-model="addData.title" placeholder="请输入标题"></el-input>
</el-form-item>
<!-- 第二行:选择分类 -->
<el-form-item label="选择分类" prop="cate_id">
<el-select placeholder="请选择分类" style="width: 100%" v-model="addData.cate_id">
<el-option label="北京" value="2"> </el-option>
<el-option label="上海" value="0"> </el-option>
<el-option label="广州" value="53"> </el-option>
</el-select>
</el-form-item>
</el-form>
<!-- 添加的表单 end -->
下拉框使用真实的分类
封装 获取分类的 API方法,但是,做分类页面的时候,已经封装过这个 API 方法了。
组件中,设置好数据项 cateList: [] ,设置方法,调用API获取分类,将结果复制给 cateList:
export default {
data() {
return {
// ...........其他略
cateList: []
}
},
methods: {
// 其他方法略...........................
async initCategory() {
const { data: res } = await getCategoryAPI()
if (res.code === 0) {
// 成功得到分类列表数据,把数据赋值给数据项
this.cateList = res.data
} else {
this.$message.error(res.message)
}
}
},
created() {
this.initCategory()
}
}
最后,页面中,循环渲染下拉框即可:
<!-- 第二行:选择分类 -->
<el-form-item label="选择分类" prop="cate_id">
<el-select placeholder="请选择分类" style="width: 100%" v-model="addData.cate_id">
<el-option
v-for="item in cateList"
:key="item.id"
:label="item.cate_name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
文章内容
使用富文本编辑器 quill-editor,参考网站:https://www.npmjs.com/package/vue-quill-editor
- 下载安装
npm install vue-quill-editor --save
- main.js 导入样式、js、并注册为Vue的插件 ```javascript import VueQuillEditor from ‘vue-quill-editor’
// require styles import ‘quill/dist/quill.core.css’ import ‘quill/dist/quill.snow.css’ import ‘quill/dist/quill.bubble.css’
Vue.use(VueQuillEditor)
3. 组件中,把 富文本编辑器当做子组件使用即可
```vue
<!-- 第三行:文章内容 -->
<el-form-item label="文章内容">
<!-- 富文本编辑器 -->
<quill-editor v-model="addData.content"></quill-editor>
</el-form-item>
<script>
export default {
addData: {
title: '',
cate_id: '',
content: '', // 文章内容。。。。。。。。。。。。。。。。。。。。。。。
}
}
</script>
点击选择图片按钮,能够选择图片:
```vue
<el-button type="text" @click="$refs.fileRef.click()">选择图片</el-button>
文件域内容改变的时候,处理数据项和预览:
<input type="file" ref="fileRef" @change="chooseImg" style="display: none" />
JS中,设置好数据项:
data() {
return {
imgUrl: '', // 存储临时的url地址,准备做预览用
addData: {
title: '',
cate_id: '',
content: '',
cover_img: '' // 接口要求的名字,值必须的文件对象格式
}
}
}
在文件域change方法中,设置数据项:
// 选择图片的处理方法
chooseImg(e) {
// 判断一下,是否选择了图片
// 文件域.files ---- 选择的文件列表
if (e.target.files.length > 0) {
// 如果选择了图片,则把文件对象,加入到数据项中
this.addData.cover_img = e.target.files[0]
// 做预览,创建预览的url
this.imgUrl = URL.createObjectURL(e.target.files[0])
} else {
// 说明取消选择图片
this.addData.cover_img = ''
this.imgUrl = ''
}
}
控制页面的预览图片:
<img :src="imgUrl" class="cover-img" alt="" v-if="imgUrl" />
<img src="@/assets/images/cover.jpg" class="cover-img" alt="" v-else />
处理文章状态
点击发布、存为草稿,都是提交数据的意思。
只不过,点击发布,让文章的状态为”已发布”;点击存为草稿,让文章的状态是 ”草稿”
先加好数据项:
data() {
return {
addData: {
title: '',
cate_id: '',
content: '',
cover_img: '',
state: '' // 文章状态,可选已发布、草稿
}
}
}
页面中,设置最后一行,放两个按钮,点击事件加好:
<!-- 第五行:按钮 -->
<el-form-item>
<el-button type="primary" @click="publish('已发布')">发布</el-button>
<el-button type="info" @click="publish('草稿')">存为草稿</el-button>
</el-form-item>
补充 publish方法,设置文章的状态:
// 发布文章方法
publish(s) {
// s 就是点击按钮的时候,传递过来的 已发布 或 草稿
this.addData.state = s
}
最后,通过 vue 调试工具,查看这几项数据是否有问题。调试工具如果没有反应过来,手动点击刷新。
最后的发布
封装API方法(api/article.js):
import request from '@/utils/request'
export const addArticleAPI = fd => {
// return request.post('/my/article/add', fd)
return request({
method: 'POST',
url: '/my/article/add',
data: fd
})
}
组件中,加载API方法:
// 按需导入 添加文章的 API 方法
import { addArticleAPI } from '@/api/article'
publish方法中,将数据项,转成 FormData 格式,然后提交给接口:
// 发布文章方法
publish(s) {
// s 就是点击按钮的时候,传递过来的 已发布 或 草稿
this.addData.state = s
// 下面进行完整的验证
this.$refs.addForm.validate(async valid => {
if (!valid) return
// 手动验证文章内容和封面图片
if (this.addData.content === '') return this.$message.error('请输入内容')
if (this.addData.cover_img === '') return this.$message.error('请选择图片')
// 提交数据
// console.log(this.addData)
// 创建 FormData 对象
const fd = new FormData()
for (const key in this.addData) {
// key 就是 cate_id/ content/ cover_img/ state/ title
fd.append(key, this.addData[key])
}
// Ajax 提交 fd 对象即可
const { data: res } = await addArticleAPI(fd)
if (res.code === 0) {
this.$message.success(res.message)
// 添加成功,关闭对话框
this.dialogVisible = false
// 重置表单
this.$refs.addForm.resetFields()
this.addData.content = ''
this.addData.cover_img = ''
this.imgUrl = ''
// 重新获取列表数据,重新渲染。。。。。。
} else {
this.$message.error(res.message)
}
})
}
