# 极客园PC端
# 第一章:项目起步
# 01-项目介绍
目标:了解项目背景,了解项目功能。
项目背景:
- 它对标 CSDN 博客园 等竞品,致力成为全球知名的IT技术交流平台,它包含 技术文章,问答内容,视频解答 的专业IT资讯平台,它提供原创,优质,完整内容的专业IT社区。它是
极客园
IT资讯社区。
项目功能:
极客园-个人端PC
是PC桌面web个人自媒体管理端- 主要功能有:
- 登录
- 首页
- 内容管理(筛选,删除)
- 发布文章(包含编辑文章)
项目物料:http://geek.itheima.net
总结: 我们知道项目的大致功能即可。
# 02-使用技术
目标:了解会使用到的技术
开发依赖大致如下:
- 基础环境:
nodejss12+
vscode
vuecli4.x
- 配套工具:
eslint
babel
less
- 使用技术:
vue2.6.12
element-ui
vue-router
项目中的解决方案:
- 使用vue-cli创建vue单页应用解决方案
- 使用vue-router实现前端路由解决方案
- 使用element-ui快速搭建PC界面解决方案
总结: 我们大概知道用了那些东西即可。
# 03-创建项目
目标:知道如何使用vue-cli创建项目
大致步骤:
- 在某个目录打开命令行工具输入创建项目的命令
- 安装项目需求选择具体的工具,然后等待创建吧
- 最后进入创建好的项目,启动项目即可
具体如下:
- 执行创建命令
vue create geek-client-pc
- 选中自定义创建
- 选择Vue版本,依赖Babel降级ES6语法,使用css预处理器,使用代码风格校验。
- 选择vue2.0版本
- 是否使用历史模式API,输入 n
- 选择less这种css预处理器
- 选择 通用语法风格配置
- 语法风格校验的时机,保存代码校验,提交代码校验且自动修复。
- 选择使用不同的配置文件对于所依赖工具
- 是否记录此次操作记录,输入 n
- 最后等待安装即可,安装完毕进入项目目录,执行
npm run serve
即可启动项目。
总结: 我们可以使用vuecli根据自己项目需求创建合适的项目。
# 04-调整目录
目标:根据项目功能调整下目录结构
大致步骤:
- 配置文件解释说明
- 调整src下目录结构
落地内容:
- 根目录和配置文件。都是自动生成的,了解作用即可
├─node_modules
├─public
├─src
├─.browserslistrc # 适配浏览器列表
├─.editorconfig # 提供给编辑器的配置
├─.eslintrc.js # eslint代码风格配置
├─.gitignore # git忽略文件配置
├─.babel.config.js # babelES降级配置
├─package-lock.json # 包下载版本说明文件
├─package.json # 项目包说明文件
├─README.md # 说明MD文件
- src 目录结构如下,仅供参考 (分模块的思维才重要)
├─assets # 项目资源
│ ├─images # 图片
│ └─styles # less代码
├─components # 全局组件,通用组件
├─router # 路由
├─utils # 工具
└─views # 路由组件(页面)
├─home # 首页
├─login # 登录
├─article # 内容管理
└─publish # 发布文章
总结: 做好开发前的准备,调整下项目结构。
# 第二章:项目架构
# 01-使用element-ui
目标:引入element-ui组件库,能够在项目中使用它。
在pc管理系统开发过程中,需要创建静态页面,如果自己去编写,成本比较高。使用第三方的UI库,来快速构建页面会是最好选择。我们使用的是基于vue最流行的UI组件库 element-ui
饿了么UI (opens new window),它提供了网页开发中常用的功能,而且基于vue封装成了组件,我们只需参考官方提供的使用文档来使用即可。
使用步骤:
安装
npm i element-ui
导入
- 完整引入(建议学习阶段使用,简单粗暴)
- 按需引入(建议开发阶段使用,优化体积)
// 完整引入,main.js写入以下代码 import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI)
使用
App.vue
<div id="app"> <el-button type="success">成功按钮</el-button> </div>
小结一下:
- 饿了么UI是什么?
- 是一个基于vue的UI组件库
- 如果使用饿了么UI?
- 安装,完整导入,全局使用。
# 02-使用vue-router
目标:在项目中使用vue-router实现前端路由
使用步骤:
安装:
npm i vue-router
引入:
以前是在main.js完成的,但是路由相关代码会比较多,现在是在
src/router/index.js
进行引入和配置。import VueRouter from 'vue-router'
注册:
import Vue from 'vue' Vue.use(VueRouter)
初始化:
const router = new VueRouter({ routes: [] // 路由规则 })
导出路由实例:
export default router
导入路由实例:
main.js
// @是路径别名,代表 src import router from '@/router'
挂载:
main.js
new Vue({ render: h => h(App), + router }).$mount('#app')
总结:安装使用vue-router,提取路由模块。
# 03-约定路由
目的:约定好前端路由规则,什么路径渲染什么组件。
设计思路:
具体规则:
路由路径 | 路由对应组件(页面) | 路由等级 |
---|---|---|
/login | 登录 | 一级路由 |
/ | 首页 | 一级路由 |
├─ / | 概览页面 | 二级路由 |
├─ /article | 文章管理 | 二级路由 |
├─ /publish | 发布文章 | 二级路由 |
总结: 登录---->首页,内容完整切换使用一级路由,概览,文章,发布,再Layout基础之上切换嵌套路由,二级路由切换。
# 第三章:登录模块
# 01-登录-路由与组件
目的:准备好登录组件,配置好路由。
组件:src/views/login.vue
<template>
<div class="container">Login</div>
</template>
<script>
export default {
name: 'Login'
}
</script>
<style scoped lang="less"></style>
路由:src/router/index.js
import Login from '@/views/login'
const router = new VueRouter({
routes: [
+ { path: '/login', component: Login }
]
})
出口:src/App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
# 02-登录-基础布局
目的:完成登录页面基础布局
结构:src/views/login/index.vue
<div class="container">
<el-card>
<img class="logo" src="../../assets/logo.png" alt="">
<!-- 表单 -->
</el-card>
</div>
样式:
src/views/login/index.vue
.container {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
background: url(../../assets/login.png);
.el-card {
width: 440px;
height: 380px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
box-shadow: 0 0 50px rgba(0,0,0,0.1);
.logo {
width: 200px;
display: block;
margin: 0 auto 20px;
}
}
}
App.vue
* {
margin: 0;
padding: 0;
}
总结:el-card是饿了么UI卡片组件,组件根元素有class 类 .el-card
# 03-登录-绘制表单
目的:使用el-form组件绘制登录表单
大致步骤:
- 先去阅读 el-form 组件的使用文档
- 了解表单元素组件类型
- 绘制表单
落的内容:src/views/login.vue
- el-form用法
1. el-form 是表单容器组件
2. el-form-item 是表单项组件,label可以设置表单元素文字说明,可以放置表单元素组件。
- el的表单元素组件
el-input el-checkbox el-select el-radio 等等
- 绘制表单
<el-form>
<el-form-item>
<el-input placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item>
<el-checkbox :value="true">我已阅读并同意「用户协议」和「隐私条款」</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary">登 录</el-button>
</el-form-item>
</el-form>
.el-form {
padding: 0 20px;
.el-button {
width: 100%;
}
}
总结: 知道绘制表单用的是el-form,el-form-item和其他的表单元素组件。
# 04-登录-基本的表单校验
目的:完成表单的基本校验
大致步骤:
- 先阅读element-ui的表单组件文档,找到校验功能总结方法
- 明确下校验需求
- 实践下校验功能
落的内容:src/views/login.vue
- 校验方法
1. el-form 需要加上一个属性 model 通过这个属性绑定表单数据对象
2. el-form 需要加上一个属性 rules 通过这个属性绑定校验规则对象(定义校验逻辑)
3. el-from-item 需要加上一个属性 prop 通过这个属性来指定当前表单项使用的校验规则
4. 校验规则的名称需要和绑定表单数据中的字段名一致
5. 具体的校验规则可参考官方示例
- 校验需求
手机号:必填,1开头,3-9之间,9个数字
验证码:必填,6个字符
- 实践代码
data () {
return {
// 数据
form: {
mobile: '',
code: ''
},
// 规则
rules: {
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码6个字符', trigger: 'blur' }
]
}
}
}
+ <el-form :model="form" :rules="rules">
+ <el-form-item prop="mobile">
+ <el-input v-model="form.mobile" placeholder="请输入手机号"></el-input>
</el-form-item>
+ <el-form-item prop="code">
+ <el-input v-model="form.code" placeholder="请输入验证码"></el-input>
</el-form-item>
<el-form-item>
<el-checkbox :value="true">我已阅读并同意「用户协议」和「隐私条款」</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary">登 录</el-button>
</el-form-item>
</el-form>
总结: el-form上绑定model指定表单数据对象,绑定rules指定表单校验对象,el-form-item的prop属性指定当前表单项使用校验规则中的 xxx 规则去校验表单对象中的 xxx 数据。
# 05-登录-自定义表单校验
目的:完成自己定义校验规则
大致步骤:
- 先阅读element-ui的表单组件文档,找到自定校验规律
- 实践自定义校验
落地内容:src/views/login.vue
- 自定义校验规律
1. 在 校验规则 对象中, validator 是用来指定自定义校验函数
2. 这个 自定义校验函数 必须先声明,在data的return之前声明即可
3. 自定义校验函数三个参数:rule value callback
3.1 rule 当前字段对应的校验对象 不会使用
3.2 value 当前字符段对应的值 我们需要校验这个值
3.3 callback 失败--->callback(new Error('校验失败的提示')) 成功---->callback()
- 自定义校验实践
data () {
+ const checkMobile = (rule, value, callback) => {
+ if (/^1[3-9]\d{9}$/.test(value)) {
+ callback()
+ } else {
+ callback(new Error('手机号格式不对'))
+ }
+ }
return {
// 数据
form: {
mobile: '',
code: ''
},
// 规则
rules: {
mobile: [
+ { required: true, message: '请输入手机号', trigger: 'blur' },
{ validator: checkMobile, trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码6个字符', trigger: 'blur' }
]
}
}
}
总结: 使用validator属性可以指定一个校验函数,再校验函数中来校验value数据,调用callback代表校验完成,传入错误对象代表失败,不传代表成功。
# 06-登录-整体表单校验
目的:对整个表单进行校验
大致步骤:
- 先阅读element-ui的表单组件文档,找到整体表单校验方式
- 实践整体表单校验
落地内容:src/views/login.vue
- 整体表单校验方式
1. 通过调组件实例的函数 validate 进行整体校验
2. validate(function(valid){ // valid 为 true 校验成功 })
3. 通过ref绑定el-form,然后this.$refs获取组件实例,再去调用validate函数
- 实践整体表单校验,点击登录按钮校验
<el-form ref="form" :model="form" :rules="rules">
<el-button @click="login()" type="primary">登 录</el-button>
methods: {
login () {
this.$refs.form.validate(valid => {
console.log(valid)
})
}
}
总结: 使用ref="xxx"绑定el-form组件,使用this.$refs.xxx.validate()校验整体表单。
# 07-登录-挂载axios
目的:创建一个axios实现挂载再Vue原型上,将来再任何vue组件下可通过this访问,方便开发。
大致步骤:
- 安装axios
- 创建axios
- 挂载axios
落地代码:
- 安装axios
npm i axios
- 创建axios
src/utils/http.js
import axios from 'axios'
const instance = axios.create({
baseURL: 'http://geek.itheima.net/',
timeout: 5000
})
export default instance
- 挂载axios
src/main.js
import http from '@/utils/http'
Vue.prototype.$http = http
总结: 创建一个axios实例通过prototype挂载到Vue上,所有vue实例可以访问。
# 08-登录-进行登录
目的:进行登录实现
大致步骤:
- 发起登录请求
- 成功:保存token在本地,跳转首页
- 失败:提示错误消息
落地代码:
login () {
this.$refs.form.validate(async valid => {
if (valid) {
try {
// 请求
const res = await this.$http.post('/v1_0/authorizations', this.form)
// 成功
localStorage.setItem('geek-client-pc-store', res.data.data.token)
this.$router.push('/')
} catch (e) {
// 失败
this.$message.error(e.response.data.message || '登录失败')
}
}
})
}
try catch
的用法
try{
// 写可能出现异常的代码片段
}catch(e) {
// 如果出现异常执行catch函数
// e 就是错误对象
}
总结: 发起登录请求,成功后存储token后跳转到首页。
# 09-登录-操作token工具
目标:封装一个操作本地token的工具模块auth.js
大致步骤:
- 约定好存储数据的key定义成常量
- 提供一个获取token函数
- 提供一个设置token函数
- 提供一个删除token函数
落地代码:
- 定义工具
src/utils/auth.js
// 操作认证信息token的工具函数
const KEY = 'geek-client-pc-store'
export default {
// 获取token
getToken () {
return localStorage.getItem(KEY)
},
// 设置token
setToken (token) {
localStorage.setItem(KEY, token)
},
// 删除token
delToken () {
localStorage.removeItem(KEY)
}
}
- 使用工具
src/views/login.vue
import auth from '@/utils/auth'
// 请求
const res = await this.$http.post('/v1_0/authorizations', this.form)
// 成功
// localStorage.setItem('geek-client-pc-store', res.data.data.token)
+ auth.setToken(res.data.data.token)
this.$router.push('/')
# 10-登录-访问控制
目的:实现除去登录页面,其他页面访问都需要判断是否存储token,没有拦截到登录
大致步骤:
- 弄清楚访问控制的逻辑
- 使用路由导航守卫实现
落地代码:
- 弄清楚访问控制的逻辑
- 使用路由导航守卫实现
src/router/index.js
import auth from '@/utils/auth'
router.beforeEach((to, from, next) => {
// 获取token
const token = auth.getToken()
// 不是访问登录,有没有token,跳转登录页面
if (to.path !== '/login' && !token) return next('/login')
// 其他情况放行
next()
})
总结:使用router的breforeEach进行访问权限控制。
# 第四章:概览页面
# 00-代码片段
目的:使用vue快捷方式创建vue模版组件
步骤:
ctrl + shift + p
打开功能搜索框- 输入
代码片段
搜索功能 ,找到如下功能点击它
- 点击新建代码片段
- 将下面的代码复制到文件
{
"vue-component": {
"scope": "vue",
"prefix": "vue",
"body": [
"<template>",
" <div class=\"container\"></div>",
"</template>",
"<script>",
"export default {",
" name: ''",
"}",
"</script>",
"<style scoped lang=\"less\"></style>",
""
],
"description": "生成一个vue单文件组件"
}
}
- 最后再vue文件输入
vue
提示代码生成方式
# 01-概览-路由与布局组件
组件:src/views/Layout.vue
<template>
<div class="container">Layout</div>
</template>
<script>
export default {
name: 'Layout'
}
</script>
<style scoped lang="less"></style>
路由:src/router/index.js
import Layout from '@/views/Layout'
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
+ {path: '/',component: Layout}
]
})
# 02-概览-布局组件架子
目标:使用element-ui布局容器准备Layout组件的页面架子
大致步骤:
- 找到element-ui文档中的布局容器组件,找到对应的结构代码。
- 在Layout.vue组件中使用代码,添加结构和样式
落地代码:src/views/Layout.vue
<template>
<el-container class="container">
<el-aside width="200px">
<div class="logo"></div>
</el-aside>
<el-container>
<el-header>
<span style="margin-right:20px">用户名</span>
<el-link icon="el-icon-unlock" :underline="false">退出</el-link>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
name: 'Layout'
}
</script>
<style scoped lang="less">
.container {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
.el-aside {
background: #023;
.logo {
width: 200px;
height: 60px;
background:#023 url(../assets/logo.png) no-repeat center / 160px auto;
}
}
.el-header {
border-bottom: 1px solid #ddd;
display: flex;
justify-content: flex-end;
align-items: center;
}
}
</style>
# 03-概览-布局组件菜单
目的:在左侧栏加上el-menu组件搭建菜单
大致步骤:
- 找到element-ui文档中的导航菜单组件,找到对应的结构代码。
- 在Layout.vue组件中使用代码,改造成我们需要的菜单。
落的代码:
- 分析示例代码
1. el-menu 是导航菜单容器
2. el-menu-item 是菜单项
3. 如果有子级菜单使用 el-submenu 包裹 el-menu-item 组件,如果有分组 el-menu-item 外包裹 el-menu-item-group 组件
- 使用导航菜单
<el-menu
background-color="#023"
style="border-right:none"
text-color="#fff"
default-active="/"
>
<el-menu-item index="/">
<i class="el-icon-s-home"></i>
<span slot="title">数据概览</span>
</el-menu-item>
<el-menu-item index="/article">
<i class="el-icon-document"></i>
<span slot="title">内容管理</span>
</el-menu-item>
<el-menu-item index="/publish">
<i class="el-icon-s-promotion"></i>
<span slot="title">发布文章</span>
</el-menu-item>
</el-menu>
属性:
- el-menu ---> default-active 当前激活那个菜单项,值为el-menu-item的index的值。
- el-menu-item ---> index 菜单项标识
总结: 使用element-ui组件,先分析,再使用。诀窍:试一试。
# 04-概览-路由与概览组件
目的:完成概览组件,完成二级路由配置,以及出口的配置。
组件:src/home/index.vue
<template>
<div class="home"></div>
</template>
<script>
export default {
name: 'Home'
}
</script>
<style scoped lang="less">
.home {
width: 100%;
height: 100%;
background:#f5f5f5 url(../../assets/chart.png) no-repeat center / contain;
}
</style>
路由:src/router/index.js
import Home from '@/views/home'
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
{
path: '/',
component: Layout,
+ children: [
+ { path: '/', component: Home }
+ ]
}
]
})
出口:src/views/Layout.vue
<el-main>
+ <router-view></router-view>
</el-main>
# 05-概览-获取个人信息
目的:Layout.vue组件中需要登录用户的名称,需要调用接口获取个人信息。
大致步骤:
- data中声明 用户数据 对象
- 组件初始化获取个人信息,设置用户信息
- 模版中使用 name 没有就使用 mobile
落地代码:src/views/Layout.vue
data () {
return {
user: {}
}
},
async created () {
const res = await this.$http.get('v1_0/user/profile')
this.user = res.data.data
},
<el-header>
+ <span style="margin-right:20px">{{user.name||user.mobile}}</span>
<el-link icon="el-icon-unlock" :underline="false">退出</el-link>
</el-header>
由于我们请求的时候没有带上token请求后台,导致后台认为我们没有登录,响应401告诉你token失效或未传。
总结: 调用需要登录状态的后台接口是需要携带token的,下一节我们实现下携带token
# 06-概览-请求携带token
目的:在请求前获取本地存储的token设置在请求头
大致步骤:
- 分析示例代码
- 完成token携带
落地代码:src/utils/http.js
- 分析-请求拦截器
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
- 使用
import auth from '@/utils/auth'
instance.interceptors.request.use(config => {
const token = auth.getToken()
if (token) config.headers.Authorization = `Bearer ${token}`
return config
}, err => Promise.reject(err))
总结: 使用axios请求拦截器可以在请求前给请求头带上token
# 07-概览-拦截token失效
目的:在token失效的时候拦截跳转到登录页面
大致步骤:
- token在服务端是会去判断有效时间的,极客园的是2小时失效
- 我们不能等两小时再来操作,可以在浏览器把token改成一个错误的,其实就是默认token失效。
- 响应拦截器来根据401判断,分析示例代码
- 完成token失效拦截
落地代码:src/utils/http.js
- 分析-响应拦截器
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
- 使用
import router from '@/router'
instance.interceptors.response.use(res => res, err => {
// 失效token
if (err.response && err.response.status === 401) {
auth.delToken()
router.push('/login')
}
return Promise.reject(err)
})
总结: 使用axios响应拦截器可以根据401响应状态码拦截token失效
# 08-概览-退出功能
目的:失效退出登录
大致步骤:
- 本地删除token就是退出登录
- 绑定按钮事件,事件中删除token跳转登录页面即可。
落地代码:src/views/Layout.vue
<el-link @click="logout()" icon="el-icon-unlock" :underline="false">退出</el-link>
import auth from '@/utils/auth.js'
methods: {
logout () {
auth.delToken()
this.$router.push('/login')
}
}
# 第五章:内容管理
# 01-内容管理-路由与组件
组件:src/views/article/index.vue
<template>
<div class="article">Article</div>
</template>
<script>
export default {
name: 'Article'
}
</script>
<style scoped lang="less"></style>
路由:src/router/index.js
import Article from '@/views/article'
children: [
{ path: '/', component: Home },
+ { path: '/article', component: Article }
]
# 02-内容管理-开启菜单路由
目的:点击菜单进行路由跳转,刷新页面需要激活当前菜单。
大致步骤:
- 开启导航菜单路由功能
- 刷新页面后保存激活状态
落地代码:src/views/Layout.vue
- 开启导航菜单路由功能
<el-menu
background-color="#023"
style="border-right:none"
text-color="#fff"
default-active="/"
+ router
>
加上router属性后,el-menu-item的index属性值就是跳转的路径
- 刷新页面后保存激活状态
<el-menu
background-color="#023"
style="border-right:none"
text-color="#fff"
+ :default-active="$route.path"
router
>
$route.path
获取当前路由路径,设置给default-active属性,激活对应的el-menu-item菜单项。
# 03-内容管理-筛选区域
目的:完成筛选区域布局
大致步骤:
- 准备基本结构
- 添加-状态-单选框
- 添加-频道-下拉框
- 添加-时间-日期控件
落地代码:src/views/article/index.vue
1)基本结构
<div class='article-page'>
<!-- 筛选区域 -->
<el-card>
<div slot="header">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>内容管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 表单 -->
<el-form label-width="80px">
<el-form-item label="状态:"></el-form-item>
<el-form-item label="频道:"></el-form-item>
<el-form-item label="日期:"></el-form-item>
<el-form-item label="">
<el-button type="primary">筛选</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 结果区域 -->
</div>
2)状态-单选框
<el-form-item label="状态:">
<el-radio-group v-model="reqParams.status">
<el-radio :label="null">全部</el-radio>
<el-radio :label="0">草稿</el-radio>
<el-radio :label="1">待审核</el-radio>
<el-radio :label="2">审核通过</el-radio>
<el-radio :label="3">审核失败</el-radio>
<el-radio :label="4">已删除</el-radio>
</el-radio-group>
</el-form-item>
data () {
return {
// 提交给后台的参数
reqParams: {
// 默认值为null,使用axios提交的时候null值是不会传递的。
status: null
}
}
}
3)频道-下拉框
<el-form-item label="频道:">
<el-select v-model="reqParams.channel_id" placeholder="请选择">
<el-option
v-for="item in channelOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
data () {
return {
// 提交给后台的参数
reqParams: {
// 默认值为null,使用axios提交的时候null值是不会传递的。
status: null,
+ channel_id: null
},
// 频道选项
+ channelOptions: [{ value: 1, label: '前端' }, { value: 2, label: 'java' }]
}
}
4)时间-日期控件
<el-form-item label="日期:">
<el-date-picker
v-model="dateArr"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
data () {
return {
// 提交给后台的参数
reqParams: {
// 默认值为null,使用axios提交的时候null值是不会传递的。
status: null,
channel_id: null,
+ begin_pubdate: null,
+ end_pubdate: null
},
// 频道选项
channelOptions: [{ value: 1, label: '前端' }, { value: 2, label: 'java' }],
// 日期范围 [起始时间,结束时间]
+ dateArr: []
}
}
# 04-内容管理-结果区域
目的:完成结果区域布局
大致步骤:
- 基本布局
- 分析表格组件
- 绘制表格组件
- 使用分页组件
落地代码:src/views/article/index.vue
1)基本布局
<!-- 结果区域 -->
<el-card style="margin-top:20px">
<div slot="header">根据筛选条件共查询到 46148 条结果:</div>
<!-- 表格 -->
<!-- 分页 -->
</el-card>
2)分析表格组件
3)绘制表格组件
- el-table 属性 data 接收表格数组数据,必填。
<!-- 表格 -->
<el-table :data="articles">
<el-table-column label="封面"></el-table-column>
<el-table-column label="标题"></el-table-column>
<el-table-column label="状态"></el-table-column>
<el-table-column label="发布时间"></el-table-column>
<el-table-column label="阅读数"></el-table-column>
<el-table-column label="评论数"></el-table-column>
<el-table-column label="点赞数"></el-table-column>
<el-table-column label="操作"></el-table-column>
</el-table>
dateArr: [],
+ articles: []
4)使用分页组件
<!-- 分页 -->
<el-pagination
style="margin-top:20px"
background
layout="prev, pager, next"
:total="1000">
</el-pagination>
# 05-内容管理-渲染频道选项
目的:获取文章频道数据且进行渲染
大致步骤:
- 在mehtods中定义一个获取选项的函数 ,获取数据赋值给data中的选项
- 在created中调用改函数即可
- 渲染模版
落地代码:src/views/article/index.vue
- 获取选项函数
methods: {
async getChannelOptions () {
const res = await this.$http.get('v1_0/channels')
this.channelOptions = res.data.data.channels
}
}
- 调用函数
created () {
this.getChannelOptions()
},
- 渲染模版
<el-form-item label="频道:">
<el-select v-model="reqParams.channel_id" placeholder="请选择">
<el-option
v-for="item in channelOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id">
</el-option>
</el-select>
</el-form-item>
# 06-内容管理-渲染简单列
目的:获取文章列表数据,渲染 标题,发布时间,阅读数,评论数,点赞数 5列。
大致步骤:
- 获取文章列表数据在组件初始化后
- 渲染 标题,发布时间 两列,它们简单些
落地代码:src/views/article/index.vue
- 获取数据
data () {
return {
// 提交给后台的参数
reqParams: {
// 默认值为null,使用axios提交的时候null值是不会传递的。
status: null,
channel_id: null,
begin_pubdate: null,
end_pubdate: null,
+ page: 1,
+ per_page: 10
},
methods: {
+ async getArticles () {
+ const res = await this.$http.get('v1_0/mp/articles', { params: this.reqParams })
+ this.articles = res.data.data.results
+ }
created () {
this.getChannelOptions()
+ this.getArticles()
},
- 渲染内容
<!-- 表格 -->
<el-table :data="articles">
<el-table-column label="封面"></el-table-column>
+ <el-table-column label="标题" prop="title" width="400px"></el-table-column>
<el-table-column label="状态"></el-table-column>
+ <el-table-column label="阅读数" width="120px" prop="read_count"></el-table-column>
+ <el-table-column label="评论数" width="120px" prop="comment_count"></el-table-column>
+ <el-table-column label="点赞数" width="120px" prop="like_count"></el-table-column>
+ <el-table-column label="发布时间" prop="pubdate"></el-table-column>
<el-table-column label="操作" width="120px"></el-table-column>
</el-table>
总结: 使用el-table-column的prop属性指定当前列显示什么字段的数据
# 07-内容管理-渲染复杂列
目的:渲染 封面,状态,操作 三列。
大致步骤:
- 参考element-ui文档,总结渲染自定义列套路
- 渲染封面列
- 渲染状态列
- 渲染操作列
落地代码:src/views/article/index.vue
- 参考 https://element.eleme.cn/#/zh-CN/component/table#zi-ding-yi-lie-mo-ban
1. 需要使用作用域插槽,暴露的数据 scope = { $index, row }
2. $index 是遍历数组(articles)的索引
3. row 是遍历数组(articles)的每一项 (行数据)
- 渲染封面
<el-table-column label="封面">
<template slot-scope="scope">
<el-image :src="scope.row.cover.images[0]" style="width:200px;height:150px">
<div slot="error" class="image-slot">
<img src="@/assets/error.png" alt="" width="200px" height="150px">
</div>
</el-image>
</template>
</el-table-column>
- 渲染状态列
<el-table-column label="状态">
<template slot-scope="scope">
<el-tag v-if="scope.row.status===0" type="info">草稿</el-tag>
<el-tag v-if="scope.row.status===1">待审核</el-tag>
<el-tag v-if="scope.row.status===2" type="success">审核通过</el-tag>
<el-tag v-if="scope.row.status===3" type="warning">审核失败</el-tag>
<el-tag v-if="scope.row.status===4" type="danger">已删除</el-tag>
</template>
</el-table-column>
- 渲染操作列
<el-table-column label="操作" width="120px">
<template>
<el-button type="primary" icon="el-icon-edit" circle plain></el-button>
<el-button type="danger" icon="el-icon-delete" circle plain></el-button>
</template>
</el-table-column>
# 08-内容管理-实现分页功能
目标:完成分页渲染和切换功能
大致步骤:
- 准备总条数数据total
- 完成分页渲染,通过分页组件提供的属性
- total属性,指定一共多少条
- page-size属性,指定一页多少条
- current-page属性,指定当前是第几页
- 切换分页功能
- current-change事件,触发事件得到当前改变的页码,通过页码去请求
落地代码:src/views/article/index.vue
1)total数据
articles: [],
+ total: 0
async getArticles () {
const res = await this.$http.get('v1_0/mp/articles', { params: this.reqParams })
this.articles = res.data.data.results
+ this.total = res.data.data.total_count
},
<div slot="header">根据筛选条件共查询到 {{total}} 条结果:</div>
2)完成分页渲染
<!-- 分页 -->
<el-pagination
style="margin-top:20px"
background
layout="prev, pager, next"
+ :current-page="reqParams.page"
+ :page-size="reqParams.per_page"
+ :total="total">
</el-pagination>
3)切换分页
<!-- 分页 -->
<el-pagination
style="margin-top:20px"
background
layout="prev, pager, next"
+ @current-change="togglePage"
:current-page="reqParams.page"
:page-size="reqParams.per_page"
:total="total">
</el-pagination>
togglePage (changedPage) {
this.reqParams.page = changedPage
this.getArticles()
}
# 09-内容管理-实现筛选功能
目标:实现文章筛选功能
大致步骤:src/views/article/index.vue
- 绑定筛选按钮的点击事件,指定处理函数。
- 需要带上筛选条件进行查询,而且页码需要变成第一页,发请求渲染列表即可。
- 页码第一页,this.reqParams.page = 1 搞定
- 双向绑定reqParams的条件不用管,只要改动了,reqParams中已经变化。
- 状态 status
- 频道 channel_id
- 两个条件 begin_pubdate end_pubdate 需要在时间范围选择之后,给他们赋值。
- 频道选项需要清空。
落地代码:
- 筛选函数
<el-button type="primary" @click="filterArticles()">筛选</el-button>
filterArticles () {
this.reqParams.page = 1
this.getArticles()
},
- 日期条件
<el-date-picker
+ @change="changeDate"
v-model="dateArr"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
changeDate (dateArr) {
// 日期访问改变后给reqParams中的日期赋值
// dateArr 选择日期后 [start,end]
// dateArr 用户做清空 null
if (dateArr) {
this.reqParams.begin_pubdate = dateArr[0]
this.reqParams.end_pubdate = dateArr[1]
} else {
this.reqParams.begin_pubdate = null
this.reqParams.end_pubdate = null
}
},
- 频道清空
+ <el-select clearable v-model="reqParams.channel_id" placeholder="请选择" >
<el-option
v-for="item in channelOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
# 10-内容管理-实现删除功能
目的:实现文章删除功能
大致步骤:
- 给删除按钮绑定点击事件,指定处理函数,把文章ID传递给函数。
- 弹出一个确认框,标题:
温馨提示
,内容:此操作将永久删除该文章, 是否继续?
- 点击确认的时候,发送删除请求,删除成功之后,更新当前列表。
落地代码:src/views/article/index.vue
+ <template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" circle plain></el-button>
+ <el-button @click="deleteArticle(scope.row.id)" type="danger" icon="el-icon-delete" circle plain></el-button>
</template>
deleteArticle (id) {
this.$confirm('此操作将永久删除该文章, 是否继续?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
// 发送删除请求
await this.$http.delete(`v1_0/mp/articles/${id}`)
// 删除成功
this.$message.success('删除文章成功')
this.getArticles()
}).catch(() => {
// 取消不做任何事情
})
}
# 第六章:发布文章
# 01-发布文章-路由与组件
组件:src/views/publish/index.vue
<template>
<div class="publish">Publish</div>
</template>
<script>
export default {
name: 'Publish'
}
</script>
<style scoped lang="less"></style>
路由:src/router/index.vue
import Publish from '@/views/publish'
children: [
{ path: '/', component: Home },
{ path: '/article', component: Article },
+ { path: '/publish', component: Publish }
]
# 02-发布文章-基础布局
目的:完成发布文章基础布局
大致步骤:
- 卡片容器 el-card
- 头部,面板屑
- 内容,表单组件 el-form
- 标题 el-input
- 频道 封装频道组件(空)
- 封面 el-radio 单选框 + 上传组件(空)
- 内容 富文本组件(空)
- 按钮
落地代码:
<template>
<div class='publish-page'>
<el-card>
<div slot="header">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>发布文章</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 表单 -->
<el-form label-width="120px">
<el-form-item label="标题:">
<el-input v-model="articleForm.title" placeholder="请输入文章标题" style="width:400px"></el-input>
</el-form-item>
<el-form-item label="频道:">频道组件</el-form-item>
<el-form-item label="封面:">
<el-radio-group v-model="articleForm.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>
<div>上传组件</div>
</el-form-item>
<el-form-item label="内容:">富文本</el-form-item>
<el-form-item label="">
<el-button type="primary">发布文章</el-button>
<el-button>存入草稿</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
export default {
name: 'PublishPage',
data () {
return {
// 表单数据
articleForm: {
title: null,
content: null,
cover: {
type: 1,
images: []
},
channel_id: null
}
}
}
}
</script>
<style scoped lang='less'></style>
# 03-发布文章-频道组件-功能实现
目的:实现频道组件默认功能,能选择即可。
大致步骤:
- 在components中新建一个组件,实现频道下拉选择功能。
- 在main.js中注册为一个全局组件。
- 在内容管理,发布文章,使用这个组件。
落地代码:
- 定义组件
src/components/my-channel.vue
<template>
<el-select
v-model="channelId"
placeholder="请选择"
clearable
>
<el-option
v-for="item in channelOptions"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</template>
<script>
export default {
name: 'MyChannel',
data () {
return {
channelId: null,
channelOptions: []
}
},
created () {
this.getChannelOptions()
},
methods: {
async getChannelOptions () {
const res = await this.$http.get('/v1_0/channels')
this.channelOptions = res.data.data.channels
}
}
}
</script>
<style scoped lang='less'></style>
- 注册组件
src/main.js
// 注册全局组件
import MyChannel from '@/components/my-channel'
Vue.component(MyChannel.name, MyChannel)
- 使用组件
src/views/article.vue
<el-form-item label="频道:">
+ <my-channel></my-channel>
</el-form-item>
总结: 这样只是封装了功能,数据还需要实现双向数据绑定。
# 04-发布文章-频道组件-双向绑定
目的:实现频道组件双向数据绑定功能,支持v-model指令。
大致步骤:
回顾父子传值,以及v-model语法糖
- 父组件传递子组件:父
:value="count"
----> 子props:['value']
- 子组件传递父组件:子
$emit('input', 100)
----> 父@input="count=$event"
- 然而
<my-com :value="count" @input="count=$event" ></my-com>
是可以简写成<my-com v-model="count"></my-com>
基于v-model语法糖原理
- 父组件传递子组件:父
频道ID父传子
频道ID子传父
使用v-model绑定频道ID
落地代码:
- 父传子
- 父组件
<my-channel :value="reqParams.channel_id"></my-channel>
- 子组件
// props接收的数据,特点:只读
+ props: ['value'],
<el-select
@change="changeChannel"
- v-model="channeId"
+ :value="value"
- 子传父
- 父组件
<my-channel :value="reqParams.channel_id" @input="reqParams.channel_id=$event"></my-channel>
- 子组件
data () {
return {
+ // channelId: null,
channelOptions: []
}
},
created () {
this.getChannelOptions()
},
methods: {
changeChannel (changedChannelId) {
// 通知父组件,频道ID已变化,你也需要改变
+ this.$emit('input', changedChannelId)
},
- v-model的使用
<el-form-item label="频道:">
<!-- 使用my-channel.vue组件 -->
<!-- <my-channel :value="reqParams.channel_id" @input="reqParams.channel_id=$event"></my-channel> -->
<my-channel v-model="reqParams.channel_id"></my-channel>
</el-form-item>
4)发布文章中使用组件
<el-form-item label="频道:">
<my-channel v-model="articleForm.channel_id"></my-channel>
</el-form-item>
.el-select {
width: 400px;
}
axios传参:后端提供接口
- 地址?后键值对
query
对应axiosaxios({params:参数对象})
axios.get(url,{params:参数对象})
- 请求体传参
body
对应axiosaxios({data:参数对象})
axios.post(url, 参数对象)
put patch - 地址/后参数
路径参数
对应axiosaxios({url:'进行拼接'})
- 请求头参数
headers
对应axiosaxios({headers:参数对象})
# 05-发布文章-封面-上传组件
目的:使用上传组件完成上传图片位置布局和交互
大致步骤:
- 查看element-ui文档找到el-upload组件照片墙示例,分析代码
- 使用el-upload组件
- 完成根据封面图类型切换组件形态
落地代码:
- 一张图,三张图才使用组件,上传图片的限制和type的值一致。
<div v-if="articleForm.cover.type===1||articleForm.cover.type===3">
<el-upload
ref="upload"
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
:limit="articleForm.cover.type"
>
<i class="el-icon-plus"></i>
</el-upload>
</div>
- 当类型修改的时候,需要重置选择的图片
+ <el-radio-group @change="changeCoverType()" v-model="articleForm.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>
changeCoverType () {
this.$refs.upload && this.$refs.upload.clearFiles()
},
- 修改样式,上传组件动画
::v-deep .el-upload-list__item {
transition: none;
}
总结: 能够基本使用el-upload组件完成上传图片的布局和交互
# 06-发布文章-封面-完成上传
目的:调用后台接口完成上传和数据保存
大致步骤:
- 阅读接口文档得到:地址,参数,返回数据
- 找到el-upload组件设置 地址,参数,请求头,的属性并且设置
- 找到el-upload组件设置 上传成功函数,处理成功后的逻辑
落地代码:
- 配置请求地址,参数,请求头
<el-upload
ref="upload"
+ :action="`${$http.defaults.baseURL}v1_0/upload`"
+ name="image"
+ :headers="{Authorization: `Bearer ${token}`}"
list-type="picture-card"
:limit="articleForm.cover.type"
>
<i class="el-icon-plus"></i>
</el-upload>
// data 中数据
+ token: auth.getToken()
import auth from '@/utils/auth'
- 配置上传成功函数,处理数据
<el-upload
ref="upload"
:action="`${$http.defaults.baseURL}v1_0/upload`"
name="image"
:headers="{Authorization: `Bearer ${token}`}"
list-type="picture-card"
:limit="articleForm.cover.type"
+ :on-success="uploadSuccess"
>
<i class="el-icon-plus"></i>
</el-upload>
uploadSuccess (res) {
this.articleForm.cover.images.push(res.data.url)
}
切换类型清理数据
changeCoverType () {
+ this.articleForm.cover.images = []
this.$refs.upload && this.$refs.upload.clearFiles()
},
# 07-发布文章-富文本使用
目的:掌握在vue项目中使用富文本。
大致步骤:
- 找到我们需要的富文本组件
- 安装
- 注册
- 使用
落地代码:
vue项目中使用富文本:
- https://github.com/vuejs/awesome-vue 找vue相关的技术
- https://github.com/surmon-china/vue-quill-editor 富文本编辑器
安装
npm install vue-quill-editor
- 局部注册
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
+import { quillEditor } from 'vue-quill-editor'
export default {
name: 'PostPage',
+ components: { quillEditor },
- 使用
<el-form-item label="内容:">
<quill-editor v-model="articleForm.content" :options="editorOption"/>
</el-form-item>
data () {
return {
// 表单数据
articleForm: {
title: null,
content: null,
cover: {
type: 1,
images: []
},
channel_id: null
},
// 富文本配置
+ editorOption: {}
}
}
::v-deep .ql-editor {
min-height: 300px;
}
# 08-发布文章-基本校验
目的:完成发布文章表单的基本校验功能
大致步骤:
- 绑定表单数据对象和校验规则对象
- 准备校验规则对象中具体的规则,
- 通过prop给表单项添加校验
落地代码:
- 校验规则对象
// 校验规则
articleRules: {
title: [
{ required: true, message: '请输入文章标题', trigger: 'blur' },
{ min: 4, max: 50, message: '文章标题4-50字符', trigger: 'blur' }
],
cover: [
// 需要自定义
],
channel_id: [
{ required: true, message: '请选择文章频道', trigger: 'change' }
],
content: [
{ required: true, message: '请输入文章内容', trigger: 'blur' }
]
},
- 使用校验规则
<!-- 表单 -->
+ <el-form label-width="120px" :model="articleForm" :rules="articleRules">
+ <el-form-item label="标题:" prop="title">
<el-input v-model="articleForm.title" placeholder="请输入文章标题" style="width:400px"></el-input>
</el-form-item>
+ <el-form-item label="频道:" prop="channel_id">
<my-channel v-model="articleForm.channel_id"></my-channel>
</el-form-item>
+ <el-form-item label="封面:" prop="cover">
<el-radio-group @change="changeCoverType()" v-model="articleForm.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>
<div v-if="articleForm.cover.type===1||articleForm.cover.type===3">
<el-upload
ref="upload"
action="https://jsonplaceholder.typicode.com/posts/"
name="image"
:headers="{Authorization: `Bearer ${token}`}"
list-type="picture-card"
:limit="articleForm.cover.type"
:on-success="uploadSuccess"
>
<i class="el-icon-plus"></i>
</el-upload>
</div>
</el-form-item>
+ <el-form-item label="内容:" prop="content">
<quill-editor v-model="articleForm.content" :options="editorOption"/>
</el-form-item>
<el-form-item label="">
<el-button type="primary">发布文章</el-button>
<el-button>存入草稿</el-button>
</el-form-item>
</el-form>
总结: 还是按照表单校验的套路完成基本的校验功能。
# 09-发布文章-特殊校验
目的:完成特殊表单项的校验,封面图,富文本。
大致步骤:
- 完成 文章内容 校验
- 完成 封面图 校验
落地代码:
- 完成 文章内容 校验
methods: {
checkContent () {
// 校验内容表单项
this.$refs.articleForm.validateField('content')
},
<el-form-item label="内容:" prop="content">
<quill-editor @blur="checkContent()" v-model="articleForm.content" :options="editorOption"/>
</el-form-item>
+<el-form ref="articleForm" :model="articleForm" :rules="articleRules" label-width="120px">
- 完成 封面图 校验
// 自定义校验规则
const checkCoverFn = (rule, value, cb) => {
// 自己对value进行校验
if (value.type === 1) {
if (!value.images[0]) {
return cb(new Error('请选择一张封面图'))
}
}
if (value.type === 3) {
if (!(value.images[0] && value.images[1] && value.images[2])) {
return cb(new Error('请选择三张封面图'))
}
}
// 代码走到这,校验成功
cb()
}
cover: [
{ validator: checkCoverFn, trigger: 'change' }
]
// 上传图片成功
uploadSuccess (res) {
// res就是响应数据
this.articleForm.cover.images.push(res.data.url)
+ // 触发一次校验
+ this.$refs.articleForm.validateField('cover')
}
- 边界情况:删除的时候需要删除数据还需要进行校验,三张图上传成功切换一张图(清空了图片数据)加一次校验。
<el-upload
ref="upload"
:action="$http.defaults.baseURL+'v1_0/upload'"
name="image"
:headers="{Authorization: `Bearer ${token}`}"
:limit="articleForm.cover.type"
:on-success="uploadSuccess"
+ :on-remove="removeFile"
list-type="picture-card">
<i class="el-icon-plus"></i>
</el-upload>
// 删除文件
removeFile (file) {
// 主动:删除images数组中对应的图片
// file 中保存了之前上传图片响应的信息 response.data.url 图片地址
// 根据这个图片地址找到images对应的索引,通过splice(索引,1) 删除图片
const index = this.articleForm.cover.images.findIndex(item => item === file.response.data.url)
this.articleForm.cover.images.splice(index, 1)
// 自己再次校验
this.$refs.articleForm.validateField('cover')
}
// 切换封面类型
changeCoverType () {
// 重置数据
this.articleForm.cover.images = []
// 重置组件
this.$refs.upload && this.$refs.upload.clearFiles()
+ // 自己再次校验
+ this.$refs.articleForm.validateField('cover')
},
总结: 遇到校验规则需要自定义使用 validator 配置,遇到非element-ui表单组件自己触发校验方法。
# 10-发布文章-进行提交
目的:能够完成发布文章功能
大致步骤:
- 绑定 发布文章 和 存入草稿 按钮点击事件,传入不同标识区别操作
- 再函数中先校验整体表单,通过后发送请求
- 成功:提示,跳转列表
- 失败:提示
落地代码:
- 绑定事件
<el-form-item label="">
<el-button @click="submit(false)" type="primary">发布文章</el-button>
<el-button @click="submit(true)">存入草稿</el-button>
</el-form-item>
- 提交函数
submit (draft) {
this.$refs.articleForm.validate(valid => {
if (valid) {
this.$http.post('/v1_0/mp/articles?draft=' + draft, this.articleForm).then(res => {
this.$message.success('发布成功')
this.$router.push('/article')
}).catch(() => {
this.$message.error('发布失败')
})
}
})
}
# 11-修改文章-填充数据
目的:当你是进入编辑文件页面,获取文章详情,填充表单,修改标题和按钮。
大致步骤:
- 文章管理页面添加跳转逻辑代码,带上文章ID跳转到文章编辑
- 从文章管理进入文章编辑,组件初始化的时候,判断是否有ID,获取文章详情数据,填充表单
- 切换标题,和操作按钮
落地代码:
src/views/article/index.vue
<el-button @click="$router.push('/publish?id='+scope.row.id)" type="primary" icon="el-icon-edit" circle plain></el-button>
src/views/publish/index.vue
created () {
if (this.$route.query.id) {
this.getArticle(this.$route.query.id)
}
},
methods: {
getArticle (id) {
this.$http.get('/v1_0/mp/articles/' + id).then(res => {
this.articleForm = res.data.data
})
},
<el-breadcrumb-item>{{$route.query.id?'修改':'发布'}}文章</el-breadcrumb-item>
<el-form-item label="" v-if="$route.query.id">
<el-button type="primary">修改文章</el-button>
<el-button>存入草稿</el-button>
</el-form-item>
<el-form-item label="" v-else>
<el-button @click="submit(false)" type="primary">发布文章</el-button>
<el-button @click="submit(true)">存入草稿</el-button>
</el-form-item>
填充图片
:file-list="fileList"
computed: {
fileList () {
return this.articleForm.cover.images.map(item=>({name:item,url:item}))
}
}
# 12-修改文章-路由组件钩子
目的:在由编辑切换发布组件不会初始化,可以使用路由组件钩子
大致步骤:
- 地址栏参数变化,路径不变化,对应的组件不会重新初始化。
- 我们使用
beforeRouteUpdate
来监听,编辑和发布 之间的切换。 - 在钩子函数中根据将要切换的地址,ID就填充表单,没有就重置表单。
落地代码:
beforeRouteUpdate (to, from, next) {
if (to.query.id) {
this.getArticle(to.query.id)
} else {
this.$refs.articleForm.resetFields()
}
next()
},
总结:
- 何时地址变化不会初始化组件?参数变化,路径不变
- 如何监听路由参数的变化?
beforeRouteUpdate
或者 watch也行
# 13-修改文章-进行修改
目的:完成文章的修改
- 大致步骤:
- 绑定 发布文章 和 存入草稿 按钮点击事件,传入不同标识区别操作
- 再函数中先校验整体表单,通过后发送请求
- 成功:提示,跳转列表
- 失败:提示
落地代码:
- 绑定事件
<el-form-item label="" v-if="$route.query.id">
<el-button @click="update(false)" type="primary">修改文章</el-button>
<el-button @click="update(true)">存入草稿</el-button>
</el-form-item>
- 进行修改
update (draft) {
this.$refs.articleForm.validate(valid => {
if (valid) {
this.$http.put('/v1_0/mp/articles/' + this.$route.query.id + '?draft=' + draft, this.articleForm).then(res => {
this.$message.success('修改成功')
this.$router.push('/article')
}).catch(() => {
this.$message.error('修改失败')
})
}
})
}
# 第七章:项目收尾
# 01-项目打包
目的:能够打包项目启动托管服务,通过路由懒加载优化首屏加载速度。
大致步骤:
- 先做一次简单的打包
- 然后做项目文件托管
- 最后优化下首屏加载
落地代码:
- 先做一次简单的打包
npm run build
- 然后做项目文件托管
npm i serve -g
安装 serve
工具,在 dist
目录执行 serve
通过 http:localhost:5000
访问
- 最后优化下首屏加载
src/router/index.js
const Login = () => import('@/views/login')
const Layout = () => import('@/views/Layout')
const Home = () => import('@/views/home')
const Article = () => import('@/views/article')
const Publish = () => import('@/views/publish')
路由懒加载:访问 /login 路由,只去加载登录对应的资源
# 02-项目总结
总结: 项目中重点内容。
- 使用vue-cli创建项目
- element-ui需要会用
- vue-router需要会用
- axios需要会用,配置,拦截器
- 登录模块,校验需要使用
- 首页模块,导航菜单
- 内容管理,筛选,列表,一套实现
- 发布文章,频道组件封装,合并修改