尚品汇后台管理系统
- 1、登录(退出登录)
- 2、路由设置(***)
-
- 路由表component属性
- 3、品牌列表模块
-
- 3.1 el-pagination layout属性
- 3.2 封装四个模块api
- 3.3 this滥用和参数解构
- 3.4 品牌列表展示(插槽)
- 3.5 品牌信息新增和修改
- 3.6 表单验证
- 3.7 删除品牌信息
- 4、商品属性
-
- 4.1 三级联动
- 4.2 商品属性展示
1、登录(退出登录) 后台接口swagger在线文档:
后台接口1
后台接口2
1、将所有的api接口url更换为和swagger在线文档相同的url。
2、跨域解决
devServer部分(proxy代理解决跨域)
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
proxy: {
// 会把请求路径中的/api换为后面的代理服务器
'/dev-api': {
// 提供数据的服务器地址
target: 'http://39.98.123.211',
pathRewrite: { '^/dev-api': '' }
}
}},
记得重启项目
3、切记将请求拦截器的X-Token改为token,否则后台会返回jwt空错误。
config.headers['token'] = getToken()
退出登录部分可以不做修改。
2、路由设置(***) 路由部分代码
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}
]
},
{
path: '/product',
name: 'Product',
component: Layout,
meta: { title: '商品管理', icon: 'el-icon-goods' },
children: [{
path: 'trademark',
name: 'TradeMark',
component: () => import('@/views/tradeMark'),
meta: { title: '品牌管理' }
}, {
path: 'attr',
name: 'Attr',
component: () => import('@/views/Attr'),
meta: { title: '平台属性管理' }
}, {
path: 'sku',
name: 'Sku',
component: () => import('@/views/Sku'),
meta: { title: 'Sku管理' }
}, {
path: 'spu',
name: 'Spu',
component: () => import('@/views/Spu'),
meta: { title: 'Spu管理' }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
路由表component属性 component用法一:
path: 'trademark',
name: 'TradeMark',
component: () => import('@/views/tradeMark'),
当访问path为trademark时,会显示tradeMark这个组件。上面只是component最简单的应用。
component用法二
path: '/product',
name: 'Product',
component: Layout,
meta: { title: '商品管理', icon: 'el-icon-goods' },
children: [{
path: 'trademark',
name: 'TradeMark',
component: () => import('@/views/tradeMark'),
meta: { title: '品牌管理' }
}]
如果一个路由只是一个菜单项,并不对应相应的组件,那么此时的component为本菜单项所在的路由组件。
例如:上面代码中product只是一个菜单项,并不对应相应组件。所以他的component应该为他所在的组件,即
component: Layout
。trademark为子菜单项,有具体对应的组件,所以component: () => import('@/views/tradeMark'),
trademark的component原理就是用法一。前端菜单栏显示的实现具体如下:
遍历路由信息数组,将路由项渲染为菜单项。菜单项的信息在路由信息中获取。这个前端菜单项显示代码值得研究,写的非常好。
(1)如果路由项只有一个子路由,则将该子路由信息作为作为菜单项信息。
(2)如果路由项有多个子路由,将父路由信息作为一级菜单项信息。然后,遍历所有的子路由,生成相应的二级菜单项。
3、品牌列表模块 3.1 el-pagination layout属性 layout是指分页器各组件的排列方式,例如:
layout=" prev, pager, next, jumper,->,sizes,total"
文章图片
->
后面的组件会被放在页面的最右边。如下图所示:文章图片
3.2 封装四个模块api 四个模块分别为attr、sku、spu、tradeMark。
每个模块的api封装为对应的js文件,如下图所示:
文章图片
为了方便使用,将四个模块再次封装到一个js文件中,然后统一暴露。
文章图片
这样如果我们使用时,只需要import这个js文件就可以使用了,但是为了更加简介,我们可以将这个js文件在main.js中注册,将其是设置为全局属性。
// 引入api请求接口
import API from '@/api'
Vue.prototype.$API = API
之后在组件中使用四个模块的api函数时,可直接通过this.$API.函数名调用。
3.3 this滥用和参数解构 data中的数据十分常用,当我们使用时可能会频繁的通过this.使用data中的数据,这样容易发生滥用this去读取data中数据,程序性能会下降。具体原因链接描述
解决方法:ES6允许我们通过参数解构来避免this的滥用。
例如,getPageList函数需要使用page, limit两个属性。直接
const { page, limit } = this
解构出这两个属性。export default {
name: 'TradeMark',
data() {
return {
page: 1,
limit: 3
}
},
mounted() {
this.getPageList()
},
methods: {
getPageList() {
const { page, limit } = this
this.$API.trademark.getTradeMarkInfo(page, limit)
}
}
}
3.4 品牌列表展示(插槽) 1、获取数据函数
设置了pager = 1参数,调用时,如果传值就会将值赋给pager ,如果不传pager = 1。即默认获取的是第一页数据。
async getPageList(pager = 1) {
this.page = pager
const { page, limit } = this
const result = await this.$API.trademark.getTradeMarkInfo(page, limit)
if (result.code === 200) {
this.total = result.data.total
this.list = result.data.records
}
}
2、需要注意的是插槽的使用。我们需要通过插槽获取父组件数据。
el-table插槽使用方法:
文章图片
品牌logo列使用了插槽。所以通过一下代码探究插槽的使用
{{row}}
{{column}}
{{$index}}
下面三个箭头分别对应
slot-scope="{row, column, $index}
中的row, column, $ index。row表示表格当前一行的全部信息;
column表示当前列的一些属性;
$index表示当前行数据在整个数据数组中的下标。
文章图片
这里我们只需要用到当前行所包含的品牌logo图片链接,所以只需要使用row,完整代码如下:
文章图片
修改
删除
3、分页器修改页面大小和修改当前页码都比较简单,直接贴代码。
// 修改当前页码
handleCurrentChange(current) {
this.getPageList(current)
},
// 修改页面大小
handleSizeChange(size) {
this.limit = size
this.getPageList()
},
注意:handleSizeChange中的getPageList需要带参数,因为getPageList定义了默认参数pager=1,如果不传入current,则getPageList的默认参数pager=1就会生效,就会一直请求第一页数据。
3.5 品牌信息新增和修改 1、头像上传组件
新增中比较重要的时头像上传组件,代码如下
文章图片
只能上传jpg文件,且不超过2MB
图片数据不能用v-model绑定,
action 必选参数,上传的地址。这里的 action取值是上传图片的接口,是后台提供好的接口。
before-upload表示上传成功前的函数
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 2if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isJPG && isLt2M
}
on-success表示上传成功后的函数
handleAvatarSuccess(res, file) {
// res是上传成功后返回给前端的数据
// file是上传成功后服务器返回给前端的数据
this.tmFrom.logoUrl = URL.createObjectURL(file.raw)
}
上面两个函数饿了吗UI官网都以给出,理解即可。
2、新增和修改函数
新增和修改按钮使用的是同一个弹窗。
(1)点击添加按钮显示弹窗
记得清空上次操作的数据
addTradeMark() {
this.dialogFormVisible = true
// 清空上次操作的数据
this.tmFrom = { tmName: '', logoUrl: '' }
}
(2)点击修改按钮显示弹窗(浅拷贝问题)
this.$tmFrom = row会使得二者同时指向数据的引用,当修改tmFrom表单项时,表格中数据也会改变,如果修改后点了取消按钮,表格中数据会发生改变。所以,要解决浅拷贝
updateTradeMark(row) {
this.dialogFormVisible = true
// ...解决单层浅拷贝问题
this.tmFrom = { ...row }
}
当赋值的对象row中还有对象元素时,又会出现上面的问题,所以可以通过深拷贝JSON.parse(JSON.stringfy())实现。
(3)击弹窗的确定按钮实现数据的修改或新增
新增和修改成功后,需要再次获取品牌信息。需要注意,如果时新增则刷新后显示第一页,如果是修改,则要在当前页刷新显示。
async addOrUpdate() {
this.dialogFormVisible = false
let result = await this.$API.trademark.addOrUpdateTradeMark(this.tmFrom)
if (result.code === 200) {
this.$message.success(this.tmFrom.id ? '修改品牌成功' : '添加品牌成功')
}
// 现需要再次获取品牌信息,修改数据要在当前页刷新
this.getPageList(this.tmFrom.id ? this.page : 1)
},
新增或修改品牌API函数
// 新增或修改品牌
export const addOrUpdateTradeMark = (tradeMark) => {
// 有id则为修改
if (tradeMark.id) {
return request({ url: 'admin/product/baseTrademark/update', method: 'put', data: tradeMark })
}
// 无id则为新增
return request({ url: 'admin/product/baseTrademark/save', method: 'post', data: tradeMark })
}
3.6 表单验证 表单验证,主要分以下几步:
(1)定义rules
(2)表单绑定rules,表单项prop绑定验证属性
(3)提交表单时触发的函数内,先进行表单验证,如果成功执行后续操作,否则弹出失败。
3.7 删除品牌信息 (1)气泡确认框
@onConfirm="deleteTradeMark(row)"绑定的是删除函数
删除
(2)删除函数
删除成功后要刷新页面。
async deleteTradeMark(row) {
const result = await this.$API.trademark.deleteTradeMark(row.id)
if (result.code === 200) {
this.$message.success('删除成功')
// 成功后,重新获取数据
await this.getPageList(this.list.length > 1 ? this.page : 1)
} else {
this.$message.error('删除失败')
}
}
示例:
文章图片
4、商品属性 4.1 三级联动 (1)三级联动静态组件
示例
文章图片
由于多个页面都使用到了上面的el-card,所以将其封装为全局组件。
main.js
import CategorySelect from './components/CategorySelect'
Vue.component(CategorySelect.name, CategorySelect)
注意:虽然该全局组件的名字是CategorySelect ,但是在使用时最好用后面的形式
。原因:html不区分大小写,会将大写字母转换为小写,并在前面加
-
。即CategorySelect
转换为category-select
(2)三级联动数据展示
示例:
文章图片
html静态代码
查询
data属性
data() {
return {
category1List: [],
category2List: [],
category3List: [],
formList: {
category1: '',
category2: '',
category3: ''
}
}
注意:formList内容是三个选择框选中属性的id。后续也是通过id去查询。
针对于选中一级属性后,自动获取二级属性列表,选中二级属性后,再自动获取三级属性列表问题。
最初思路:通过computed或者watch去实现,但都没有成功。
老师的方法:通过给选择框添加@change属性,再回调函数内去获取下一级属性列表。
@change绑定的获取三级列表函数都比较简单,这里就不过多描述。
// 获取一级分类
// 获取一级分类
async getCategory1List() {
const result = await this.$API.attr.reqCategory1List()
if (result.code === 200) {
this.category1List = result.data
} else {
alert(result.message)
}
},
// 通过选中一级属性id获取二级属性列表
async getCategory2List() {
// 当一级分类改变时,上一次选择的二级和三级分类应设置为空
this.category2List = []
this.category3List = []
this.formList.category2 = ''
this.formList.category3 = ''
const result = await this.$API.attr.reqCategory2List(this.formList.category1)
if (result.code === 200) {
this.category2List = result.data
} else {
alert(result.message)
}
},
// 通过选中二级属性id获取三级属性列表
async getCategory3List() {
// 当二级分类改变时,上一次选择的三级分类应设置为空
this.category3List = []
this.formList.category3 = ''
const result = await this.$API.attr.reqCategory3List(this.formList.category2)
if (result.code === 200) {
this.category3List = result.data
} else {
alert(result.message)
}
},
(3)将选中的三级分类id传递给父组件
这里是通过点击查询按钮实现的父子组件通信
查询按钮绑定的回调
// 子组件将三级分类id传递给父组件,父组件使用该id去查询并展示数据
async onSubmit() {
this.$emit('getCategoryId', this.formList)
}
父组件
父组件data
data() {
return {
category1Id: '',
category2Id: '',
category3Id: ''
}
},
父子组件通信,父组件的接收函数:getCategoryId函数
只有当三级id不为空时,才发请求。
// 获取子组件传递三级分类id
getCategoryId(list) {
this.category1Id = list.category1Id
this.category2Id = list.category2Id
this.category3Id = list.category3Id
if (this.category3Id) {
console.log('发请求')
}
}
4.2 商品属性展示 【vue.js|尚品汇后台管理系统】通过三级联动获取到要展示的属性id后,接下来就是通过该id请求数据,然后展示。
推荐阅读
- vue.js|尚品汇学习笔记
- Vue|解决导航守卫router.beforeResolve使用不了this.$store
- javascript|尚品汇个人理解笔记
- 尚硅谷电商项目|尚硅谷-尚品汇项目开发总结(第三天)
- 尚硅谷电商项目|尚硅谷-尚品汇项目开发总结(第四天)
- 尚硅谷电商项目|尚硅谷-尚品汇项目开发总结(第十二天)
- 尚硅谷电商项目|尚硅谷-尚品汇项目开发总结(第十六天,电商项目完)
- 尚硅谷电商项目|尚硅谷-尚品汇项目开发总结(第一天)
- vue.js|B站尚品汇项目学习笔记整理(P1-P10)