前言
记录尚品汇后台管理系统的开发过程,包括登录、主页、品牌管理、平台属性管理等功能模块SKU管理、SPU管理、用户管理、角色管理、菜单管理模块。后台管理系统是CMS通过项目实战,内容管理系统的子集可以彻底了解如何实现菜单权限和按钮权限,掌握市场数据的可视化ECharts、V-charts的运用。
主要技术点:Vue-cli、Axios、Vuex、Element-UI、菜单权限、按钮权限、数据可视化、Scss……
由于项目写得太长,编辑器真的卡住了,所以项目分为上、中、下三部分. 【Vue】项目:尚品汇后台管理系统(中) 【Vue】项目:尚品汇后台管理系统(下)
文章目录
- 前言
- 一、项目介绍
- 二、项目配置
- 三、完成登录业务
- 四、退出登录业务
- 五、项目路由的搭建
- 六、完成品牌管理(tradeMark)静态组件
- 七、展示品牌管理列表
- 八、添加品牌,修改品牌静态组件
- 九、完成品牌添加功能
- 十、完成品牌修改功能
- 十一、表格验证功能
- 十二、删除品牌操作
一、项目介绍
1.后台管理系统项目的开发也是用于信息系统管理的网页。类似于学生管理系统等。
2.后台管理项目有一些模板些模板,方便程序员构建项目框架。
简洁版: https://github.com/PanJiaChen/vue-admin-template (本次使用) 加强版: https://github.com/PanJiaChen/vue-element-admin (如果github可以进不去gitee上下载:https://gitee.com/panjiachen/vue-admin-template?_from=gitee_search)
3.模板文件和文件夹的认知简洁版
-
build ----index.js webpack配置文件【很少修改这个文件】
-
mock ----mock数据文件夹模拟一些假数据mockjs实现,因为在实际开发中,使用的是真实的界面
-
node_modules ------模块依赖于项目
-
public ------ico图标,静态页面,publick一些静态资源经常放置在文件夹中,当项目打包时webpack这个文件夹不会编译,原封不动地打包到dist文件夹里面
-
src -----程序员源代码的位置 -----api文件夹:与请求相关的文件夹: -----assets文件夹:放置一些静态资源(一般共享)aseets文件夹中的静态资源webpack包装时,将进行编译
-----components文件夹:一般放置非路由组件获取全局组件
-----icons这个文件夹里面放了一些svg矢量图
-----layout文件夹:他把一些组件和混合物放在里面
-----router文件夹:与路由相关的文件夹:
----store文件夹:必须是和vuex先关的
----style文件夹:与样式先关
-----utils文件夹:request.js是axios二次包装文件
-----views文件夹:路由组件放置在里面
-
App.vue:根组件
-
main.js:入口文件
-
permission.js:与导航守卫有关
-
settings:项目配置项文件
二、项目配置
1.首先从github简单项目上下载。
2.原项目不依赖环境,需要运行cmd,cnpm install 依赖环境进行安装。
- 遇到报错: npm ERR! Cannot set [properties]
- 解决办法: 用cnpm安装
3.运营项目:查看package.json
-
可以看出,运使用的语句是,npm run dev
-
遇到报错:* core-js/modules/es. …
- 解决方案:安装core-js cnpm core-js --save
4.后台管理系统API接口在线文档:
http://39.98.123.211:8170/swagger-ui.html
http://39.98.123.211:8216/swagger-ui.html
三、完成登录业务
1.找到登录业务的位置并修改文本。
2.书写API,将api user.js在线文档中的接口被真实接口取代
// api/user.js中 // 引入axios(axios二次封装) import request from '@/utils/request' // 外露登录接口函数 export function login(data) {
return request({
url: '/admin/acl/index/login', method: 'post', data }) } // 获取用户信息的户信息的函数 export function getInfo(token) span class="token punctuation">{
return request({
url: '/admin/acl/index/info',
method: 'get',
params: {
token }
})
}
// 对外暴露退出登录的函数
export function logout() {
return request({
url: '/admin/acl/index/logout',
method: 'post'
})
}
- axios二次封装:axios所在位置是utils/requerst.js中,由于原文件使用的是mock数据,返回的code如果成功的话是20000,但是如果是真实的接口,返回正确的code应该是200,因此修改代码。
if (res.code !== 20000 && res.code !== 200)
4.换成真实接口之后需要解决代理跨域问题:webpack涉及的知识
,可以去webpack官网-配置-devServer-devServer.proxy中寻找
// vue.config.js
// 配置代理跨域
proxy: {
'/dev-api': {
target: 'http://39.98.123.211',
pathRewrite: {
'^/dev-api': '' }
}
}
四、退出登录业务
-
静态页面的更改:layout中包含系统的框架,比如菜单栏等,其中Navbar.vue中有退出等按钮,修改即可。
-
同时也要修改路由为真实路由。
五、项目路由的搭建
-
删除views中不需要的组件,只留下dashboard和login,同时删除相关路由。
-
创建project文件夹,里面存放管理系统需要的功能(tradeMark,Attr,Sku,Spu),分别创建vue文件。
-
模仿其他路由的书写方式,编写router中的index.js路由部分
{
path: '/product',
component: Layout,
name: 'Product',
meta: {
title:'商品管理',icon:'el-icon-goods'
},
children: [
{
path: 'trademark',
name: 'Trademark',
component: () => import('@/views/product/tradeMark'),
meta: {
title:'品牌管理'}
},
{
path: 'attr',
name: 'Attr',
component: () => import('@/views/product/Attr'),
meta: {
title:'品牌属性'}
},
{
path: 'sku',
name: 'Sku',
component: () => import('@/views/product/Sku'),
meta: {
title:'Sku管理'}
},
{
path: 'spu',
name: 'Spu',
component: () => import('@/views/product/Spu'),
meta: {
title:'Spu管理'}
}
]
}
- 一个小错误:styles/index.scss中最后的一个类名写错了,导致一个布局不能实现,应为
.app-main {
padding: 20px;
}
六、完成品牌管理(tradeMark)静态组件
- 首先有一个添加按钮,并有一个加号图标
<!-- 按钮 -->
<el-button type="primary" icon="el-icon-plus" style="margin: 10px 0">添加</el-button>
- 表格组件,表格中的数据的行数可以根据提供数组的长度来自动调整,因此我们只需要控制列数,有几列便有几个el-table-column。
<el-table style="width: 100%" border>
<el-table-column prop="prop" label="序号" width="80px" align="center" type="index">
</el-table-column>
<el-table-column prop="prop" label="品牌名称" width="width">
</el-table-column>
<el-table-column prop="prop" label="品牌LOGO" width="width">
</el-table-column>
<el-table-column prop="prop" label="操作" width="width">
</el-table-column>
</el-table>
-
border:添加边框 ----整个表格是有边框的
-
label:显示的标题 ----每一列的表头是什么
-
width:对应列的宽度 ----第一列的宽度不一样,width的就是均分
-
align:对齐方式 左 中 右 ----序号列是居中对齐
- 分页器,之前封装过分页器,element-ui中也有封装好的分页器
<el-pagination
style="margin-top:20px;text-align:center"
:current-page="1"
:page-sizes="[3, 5, 10]"
:page-size="3"
:pager-count="7"
layout="prev, pager, next, jumper,->,sizes,total"
:total="99"
@current-change="getPageList"
@size-change="handleSizeChange"
>
</el-pagination>
-
current-page当前第几页
-
total总页数
-
page-size每页数据数
-
page-sizes每页数据数备选
-
layout布局位置 — ->是居右
-
pager-count页面有的页码数,连续页码数为pager-count - 2 个
七、完成品牌管理列表的展示
-
数据获取,首先书写获取数据的请求,在线文档中有API.
- 在api中创建product存放product相关请求(attr.js sku.js spu.js tradeMark.js).
// api/tradeMark.js中
import request from '@/utils/request'
export const reqTradeMarkList = (page, limit) => request({
url: `/admin/product/baseTrademark/${
page}/${
limit}`, method: 'get' });
- 为了方便其他API的引入,可以把API挂载在原型上,就可以通过this.直接引入了。
// main.js中
// 引入相关API请求接口
import API from '@/api'
//将API挂载在原型上
// 任何组件可以使用API相关接口
Vue.prototype.$API = API
- 由于这个接口需要两个参数,分别是page 和 limit,因此现在data中给出两个参数,以及给出一个数组,用于接收得到的数据。total是分析返回数据结构后也需要接收的。
data() {
return {
page:1,
limit:3,
total:0,
list:[]
}
- 数据获取
mounted(){
this.getPageList()
},
methods:{
async getPageList(pager = 1){
this.page = pager
//解构参数
const {
page,limit} = this
let result = await this.$API.trademark.reqTradeMarkList(page,limit)
if(result.code == 200){
this.total = result.data.total
this.list = result.data.records
}
},
}
-
数据展示
-
数组的数据在el-table中用data表示,每一列的数据用el-table-column中的prop表示。
-
如果表格中含有图片、按钮等其他结构,需要使用
定义域插槽
。定义域插槽的理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
。
-
<el-table style="width: 100%" border :data="this.list">
<el-table-column prop="prop" label="序号" width="80px" align="center" type="index">
</el-table-column>
<el-table-column prop="tmName" label="品牌名称" width="width">
</el-table-column>
<el-table-column prop="prop" label="品牌LOGO" width="width">
<template slot-scope="{row,$index}">
<img :src="row.logoUrl" alt="" style="width:100px;height:100px">
</template>
</el-table-column>
<el-table-column prop="prop" label="操作" width="width">
<template slot-scope="{row,$index}">
<el-button icon="el-icon-edit" type="warning" size="mini">修改</el-button>
<el-button icon="el-icon-delete" type="danger" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
- 作用域插槽中的row表示el-table中含有的数据,已经按照行分配好的。
-
分页器点击的实现
-
分页器还包括两个事件
@current-change=“handleCurrentChange” ----点击页码的时候触发,会携带着点击的页码paper,可以和数据加载放在一起写,因为更换页码后也要重新请求。
@size-change=“handleSizeChange” ----修改每页展示的数目,会携带这改变后的个数。
-
分页器相关代码
-
<el-pagination style="margin-top:20px;text-align:center" :current-page="page" :page-sizes="[3, 5, 10]" :page-size="3" :pager-count="7" layout="prev, pager, next, jumper,->,sizes,total" :total="total" @current-change="getPageList" @size-change="handleSizeChange" >
methods:{
async getPageList(pager = 1){
this.page = pager
//解构参数
const {page,limit} = this
let result = await this.$API.trademark.reqTradeMarkList(page,limit)
if(result.code == 200){
this.total = result.data.total
this.list = result.data.records
}
},
// 当分页器某一页需要展示数据条数发生变化时会触发
handleSizeChange(limit){
this.limit = limit
this.getPageList()
}
}
八、添加品牌与修改品牌的静态组件
-
点击添加或者修改会弹出相同的遮罩层,element-ui中有这个组件。图片上传也有相应的组件。
-
相关代码
<el-dialog title="添加品牌" :visible.sync="dialogFormVisible">
<el-form style="width: 80%" :model="tmForm">
<el-form-item label="品牌名称" label-width="100px">
<el-input autocomplete="off" v-model="tmForm.tmName"></el-input>
</el-form-item>
<el-form-item label="品牌LOGO" label-width="100px">
<el-upload class="avatar-uploader" action="dev-api/admin/product/fileUpload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" >
<img v-if="tmForm.logoUrl" :src="tmForm.logoUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="addOrUpdateTradeMark">确 定</el-button>
</div>
</el-dialog>
九、完成添加品牌功能
- 添加品牌有自己的接口,且添加品牌和修改品牌的操作非常相似,区别就是修改品牌会返回id,因此,在API书写的时候,将添加品牌和修改品牌封装成一个请求。
export const reqAddOrUpdateTradeMark = (tradeMark) => {
if (tradeMark.id) {
//修改品牌
return request({
url: '/admin/product/baseTrademark/update', method: 'put', data: tradeMark })
} else {
// 新增品牌
return request({
url: '/admin/product/baseTrademark/save', method: 'post', data: tradeMark })
}
}
- 为了对应请求参数,创建一个数据tmForm,包含logoUrl和tmName,用于参数传递。
data() {
return {
tmForm: {
logoUrl: "",
tmName: "",
},
};
},
-
添加品牌请求携带的参数是品牌名称和品牌图片,因此首先要收集到弹出框的信息,element-ui中用于收集表单元素的标签是 :model,可以告诉表单把数据收集到哪个元素身上。文字的收集采用v-model,图片的收集要使用action,可以设置图片的上传的地址。el-upload标签还有两个事件,:on-success:可以监测图片上次成功,会执行一次,:before-upload 可以在上传图片之前,会执行一次。
-
图片上传成功之前,要对图片进行格式大小判断,上传之后,要将图片url传递给tmForm中的logoUrl,展示的图片也要变成logoUrl。(见八的相关代码)
-
点击确认以后,首先隐藏弹出框,然后待着参数发请求,并弹出结果,重新获取数据。
// 点击添加
showDialog() {
this.dialogFormVisible = true;
// 每次点击添加之前都要清空数据,避免残留
this.tmForm.logoUrl = ""
this.tmForm.tmName = ""
},
//图片上传成功
handleAvatarSuccess(res, file) {
this.tmForm.logoUrl = res.data;
},
//图片上传之前
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error
标签: p150光电传感器