1、CSS样式:项目展示页面可分为头部,侧边导航栏,内容三部分,可使用element-ui里的布局容器进行布局。
头部使用弹性盒子均匀分布。
侧边导航栏使用element-ui组件。通过collapse为true或false控制是否折叠。
主体内容是设置子路由,将子路由的内容放入一个坑里。
设置子路由:
{
path: '/',
name: 'home',
component: HomeView,
redirect: 'welcome',
children: [
{
path: '/welcome',
name: 'welcome',
component: () => import('../views/Welcome.vue')
},]
}
简单的后台管理有 Login登录页面,welcome欢迎页面。
用户管理的user用户列表;权限管理的roles角色列表,rights权限列表;商品管理的goods商品列表,params分类参数,categories商品分类;订单管理的orders订单列表;数据统计的reports数据报表
为方便项目的维护,可以将请求进行封装。
封装流程:
1、在src路径下创建API文件夹
2、文件夹内创建request.js文件封装axios
3、创建api.js文件将请求放在一起,便于后期维护查找接口
request.js文件封装axios:
import axios from "axios";
import { MessageBox, Message } from 'element-ui'
import router from '../router'
const instance = axios.create({
baseURL: 'http://www.ysqorz.top:8888/api/private/v1/',
// baseURL: process.env.VUE_APP_API,
timeout: 5000
})
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
//设置请求头
config.headers.Authorization = window.sessionStorage.getItem('token')
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
let msg = response.data.meta.msg
let code = response.data.meta.status
// 无效token处理
if (msg === '无效token') {
MessageBox.confirm('token已过期, 是否跳转登录?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
sessionStorage.removeItem('token')
router.push('/login')
Message({
type: 'success',
message: '跳转成功!'
});
}).catch(() => {
Message({
type: 'info',
message: '已取消'
});
return false
});
}
//错误状态码的处理
//400 请求的地址不存在或者包含不支持的参数
//401 未授权
//403 被禁止访问
//404 请求的资源不存在
//500 内部错误,后台出现问题
if (code === 400 || code === 401 || code === 403 || code === 404 || code === 500) {
Message({
message: msg,
type: 'error'
});
}
return response;
}, function (error) {
// 对响应错误做点什么
console.log(error);
//网络异常处理
let { message } = error
if (message == 'Network Error') {
message = '网络连接异常'
}
if (message.includes('timeout')) {
message = '网络请求超时'
}
Message({
message: message,
type: 'error'
});
return Promise.reject(error);
});
export default instance
登录页面实现流程:
退出登录时需先清除本地存储的token值再进行跳转
注意事项:
vuex实现登录的几种方式:
state: {
token: ''
},
getters: {
},
mutations: {
gettoken(state, token) {
state.token = token
sessionStorage.setItem('token', token)
}
},
actions: {
//方法一
// Login({ commit }, token) {
// commit('gettoken', token)
// }
//方法二 async await 使用
// async Login({ commit }, userinfo) {
// let res = await getLogin(userinfo)
// console.log(res);
// if (res.data.meta.status == 200) {
// commit('gettoken', res.data.data.token)
// Message({
// type: 'success',
// message: res.data.meta.msg
// });
// location.href = '#/welcome'
// }
// }
// promise.then操作
// Login({ commit }, userinfo) {
// getLogin(userinfo).then((res) => {
// console.log(res);
// if (res.data.meta.status == 200) {
// commit('gettoken', res.data.data.token)
// Message({
// type: 'success',
// message: res.data.meta.msg
// });
// location.href = '#/welcome'
// }
// })
// }
// 方法三 组件级操作 要把请求成功的结果拿到外面使用
Login({ commit }, userinfo) {
return new Promise((resolve, reject) => {
getLogin(userinfo).then((res) => {
console.log(res);
commit('gettoken', res.data.data.token)
resolve(res)
})
})
}
},
方法一通过 this.$store.dispatch(Login,res.data.data.token) 进行使用
方法二通过this.$store.dispatch(‘Login’,this.ruleForm)
方法三
this.$store.dispatch("Login", this.ruleForm).then((res) => {
console.log(res);
if (res.data.meta.status == 200) {
this.$message({
message: res.data.meta.msg,
type: "success",
});
this.$router.push("/");
}
});
用户列表可分为:
搜索用户:
添加用户:
修改用户:
// 编辑
handleEdit(index, row) {
console.log(index, row);
this.title = "编辑用户";
this.dialogFormVisible = true;
this.addForm.username = row.username;
this.addForm.email = row.email;
this.addForm.mobile = row.mobile;
this.addForm.id = row.id;
},
删除用户:
分配角色:
切换用户状态:
分页思路:
本接口主要是后台分页:后台要求传入当前页数pagenum,每页条数pagesize。后台会返回总条数total,当前页数pagesize。
使用element-ui里的分页插件实现分页样式,当条数改变时传入条数提交接口,当页数改变时传入页数提交接口
权限管理 分为角色列表和权限列表
角色列表增删改查基本与角色列表一致,注意传参形式!!!
**渲染权限列表:**使用三重嵌套for循环生成权限下拉列表
权限分配:
商品添加页面:
布局:使用步骤条el-step组件,每个tab组件里套着表单组件。给tab声明1个激活索引v-model="activeIndex"同时每个tab子项给到name=“索引”,步骤条调用该索引。
tab切换验证:切换下一个,得验证上一个是否选择。可用离开之前属性-:before-leave 调用方法,判断用户是否填写完整,是否选择了下拉的3级列表。
点击商品属性或商品参数,要根据上次选择的3级分类id请求对应数据。点击时@tab-click="tabClicked"通过判断当前激活索引是多少,做相应的接口请求,得知是商品属性,还是商品参数。
图片上传:
使用upload组件完成图片上传
在element.js中引入upload组件,并注册
因为upload组件进行图片上传的时候并不是使用axios发送请求
所以,我们需要手动为上传图片的请求添加token,即为upload组件添加headers属性
图片预览:
移除某个图片:在移除的时候删除的是临时路径 。
如何获取将要删除的图片的临时路径 ?通过存在内存中的文件流, 从 提交表单图片pics 数组中,找到这个图片对应的索引值。从该数组中splice切除掉
图片上传成功:点击上传成功后 ,后台会返回临时路径和正式地址 。拼接得到一个图片信息对象 const picInfo = { pic: response.data.tmp_path } !!注意,因为后端接口需要传pic字段,所以拼接下 ,然后将图片信息对象picInfo,push 到表单图片pics数组中。
使用富文本编辑器插件
想要使用富文本插件vue-quill-editor,就必须先从依赖安装该插件引入并注册vue-quill-editor。
点击提交填加商品:需要将已有数据,转换成接口要求的格式。最后提交整个表单
<el-cascader expand-trigger="hover" :options="catelist" :props="cateProps" vmodel="selectedCateKeys" @change="handleChange">
//可放在计算属性里,因为cateId是动态获取的
computed: {
// 当前选中的三级分类的Id
cateId() {
if (this.selectedCateKeys.length === 3) {
return this.selectedCateKeys[2]
}
return null
}
}
添加分类:
<!-- expandTrigger='hover'(鼠标悬停触发级联) v-model(设置级联菜单绑定数据) :options(指定级
联菜单数据源) :props(用来配置数据显示的规则)
clearable(提供“X”号完成删除文本功能) change-on-select(是否可以选中任意一级的菜单)
@change="parentCateChanged" 选择项改变时触发事件-->
props: {
value: "cat_id",//绑定自己数据
label: "cat_name",
children: "children",
checkStrictly: true,//显示单选框按钮,单级也可选
expandTrigger: "hover",//划上显示数据
},
在 vue.confing.js 文件中配置
1、配置输出路径,没有设置会导致白屏 publicPath: ‘./’
2、去除生产环境的代码镜像-productionSourceMap
3、在项目文件夹下运行npm run build
如果有跨域 也要配置跨域
4、我们会发现在原来的项目文件夹中多出了一个dist文件夹,
这个文件夹就是我们要放到线上的内容 把这个文件挂载到后端服务器上
优化:
通过 externals 加载外部 CDN 资源
通过 import 语法导入的第三方依赖包,最终会被打包合并到同一个文件中,
从而导致打包成功后,单文件体积过大的问题。 为了解决上述问题,可以通过 webpack 的 externals 节点,来配置并加载外部的 CDN 资源。
凡是声明在externals 中的第三方依赖包,都不会被打包。
通过 CDN 优化 ElementUI 的打包
路由懒加载
去除 console.log() 通过下载 插件
抽离第三方插件 注意 别名的配置
在bable.config.js文件中配置
// 项目上线时-去除console.log 。注意需要安装cnpm i babel-plugin-transform-remove-console -D
const prodPlugins = [];
if (process.env.NODE_ENV === 'production') {
prodPlugins.push('transform-remove-console');
}
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
...prodPlugins
]
}