vue项目实战-电商后台管理系统

项目简介:

该项目为电商后台的管理系统。设计了登录页面。

管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面:

管理页面由五个子模块组成:用户管理,权限管理,商品管理,订单管理,数据统计;

每个子模块有若干子模块组成,用户管理下->用户列表,权限管理->角色列表,权限管理,商品管理->商品列表,分类参数,商品分配,订单管理->订单列表,数据统计->数据报表

登录页面

登录页面中对用户输入的内容进行预校验,如果不符合要求则,则不向后端发送请求,同事挂载路由守卫,防止强制跳转。同时设置令牌校验,避免重复登录。如果用户输入格式正确的用户名以及密码时,向后端发送请求,请求通过则跳转到管理页面,否则返回登录页面。

路由导航守卫:

// 挂载路由导航守卫
router.beforeEach((to, from, next) => {// to 将要访问的路径// from 代表从哪个路径跳转而来// next 是一个函数,表示放行//     next()  放行    next('/login')  强制跳转if (to.path === '/login') return next()// 获取tokenconst tokenStr = window.sessionStorage.getItem('token')if (!tokenStr) return next('/login')next()
})

登录页面核心代码:

<template><div class="login_container"><div class="login_box"><!-- 头像区域 --><div class="avatar_box"><img src="../assets/logo.png" alt=""></div><!-- 登录表单区域 --><el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form"><!-- 用户名 --><el-form-item prop="username"><el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input></el-form-item><!-- 密码 --><el-form-item prop="password"><el-input v-model="loginForm.password" prefix-icon="iconfont icon-3702mima" type="password"></el-input></el-form-item><!-- 按钮区域 --><el-form-item class="btns"><el-button type="primary" @click="login">登录</el-button><el-button type="info" @click="resetLoginForm">重置</el-button></el-form-item></el-form></div></div>
</template><script>
export default {data() {return {// 这是登录表单的数据绑定对象loginForm: {username: 'admin',password: '123456'},// 这是表单的验证规则对象loginFormRules: {// 验证用户名是否合法username: [{ required: true, message: '请输入登录名称', trigger: 'blur' },{ min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }],// 验证密码是否合法password: [{ required: true, message: '请输入登录密码', trigger: 'blur' },{ min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }]}}},methods: {// 点击重置按钮,重置登录表单resetLoginForm() {// console.log(this);this.$refs.loginFormRef.resetFields()},login() {this.$refs.loginFormRef.validate(async valid => {if (!valid) returnconst { data: res } = await this.$http.post('login', this.loginForm)if (res.meta.status !== 200) return this.$message.error('登录失败!')this.$message.success('登录成功')// 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中//   1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问//   1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中window.sessionStorage.setItem('token', res.data.token)// 2. 通过编程式导航跳转到后台主页,路由地址是 /homethis.$router.push('/home')})}}
}
</script><style lang="less" scoped>
.login_container {background-color: #2b4b6b;height: 100%;
}.login_box {width: 450px;height: 300px;background-color: #fff;border-radius: 3px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);.avatar_box {height: 130px;width: 130px;border: 1px solid #eee;border-radius: 50%;padding: 10px;box-shadow: 0 0 10px #ddd;position: absolute;left: 50%;transform: translate(-50%, -50%);background-color: #fff;img {width: 100%;height: 100%;border-radius: 50%;background-color: #eee;}}
}.login_form {position: absolute;bottom: 0;width: 100%;padding: 0 20px;box-sizing: border-box;
}.btns {display: flex;justify-content: flex-end;
}
</style>

菜单实现

管理页面有一个侧面的两级菜单,菜单的数据来自于后端,点击二级菜单会跳转到相应的子页面中。在el-menu中设置router属性,即可通过index添加到路由上进行跳转。

<template><el-container class="home-container"><!-- 头部区域 --><el-header><div><img src="../assets/heima.png" alt=""><span>电商后台管理系统</span></div><el-button type="info" @click="logout">退出</el-button></el-header><!-- 页面主体区域 --><el-container><!-- 侧边栏 --><el-aside :width="isCollapse ? '64px' : '200px'"><div class="toggle-button" @click="toggleCollapse">|||</div><!-- 侧边栏菜单区域 --><el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF" unique-opened :collapse="isCollapse" :collapse-transition="false" router :default-active="activePath"><!-- 一级菜单 --><el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id"><!-- 一级菜单的模板区域 --><template slot="title"><!-- 图标 --><i :class="iconsObj[item.id]"></i><!-- 文本 --><span>{{item.authName}}</span></template><!-- 二级菜单 --><el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" :key="subItem.id" @click="saveNavState('/' + subItem.path)"><template slot="title"><!-- 图标 --><i class="el-icon-menu"></i><!-- 文本 --><span>{{subItem.authName}}</span></template></el-menu-item></el-submenu></el-menu></el-aside><!-- 右侧内容主体 --><el-main><!-- 路由占位符 --><router-view></router-view></el-main></el-container></el-container>
</template><script>
export default {data() {return {// 左侧菜单数据menulist: [],iconsObj: {'125': 'iconfont icon-user','103': 'iconfont icon-tijikongjian','101': 'iconfont icon-shangpin','102': 'iconfont icon-danju','145': 'iconfont icon-baobiao'},// 是否折叠isCollapse: false,// 被激活的链接地址activePath: ''}},created() {this.getMenuList()this.activePath = window.sessionStorage.getItem('activePath')},methods: {logout() {window.sessionStorage.clear()this.$router.push('/login')},// 获取所有的菜单async getMenuList() {const { data: res } = await this.$http.get('menus')if (res.meta.status !== 200) return this.$message.error(res.meta.msg)this.menulist = res.dataconsole.log(res)},// 点击按钮,切换菜单的折叠与展开toggleCollapse() {this.isCollapse = !this.isCollapse},// 保存链接的激活状态saveNavState(activePath) {window.sessionStorage.setItem('activePath', activePath)this.activePath = activePath}}
}
</script><style lang="less" scoped>
.home-container {height: 100%;
}
.el-header {background-color: #373d41;display: flex;justify-content: space-between;padding-left: 0;align-items: center;color: #fff;font-size: 20px;> div {display: flex;align-items: center;span {margin-left: 15px;}}
}.el-aside {background-color: #333744;.el-menu {border-right: none;}
}.el-main {background-color: #eaedf1;
}.iconfont {margin-right: 10px;
}.toggle-button {background-color: #4a5064;font-size: 10px;line-height: 24px;color: #fff;text-align: center;letter-spacing: 0.2em;cursor: pointer;
}
</style>

用户管理

用户列表

用户管理下有用户列表,这里渲染了后端的用户列表,可以编辑用户信息,删除用户,为用户分配角色,还可以对用户是否禁用进行管理;除此之外,还添加了查询用户,添加用户,和分页功能。

核心代码:

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>用户管理</el-breadcrumb-item><el-breadcrumb-item>用户列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><!-- 搜索与添加区域 --><el-row :gutter="20"><el-col :span="8"><el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList"><el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button></el-input></el-col><el-col :span="4"><el-button type="primary" @click="addDialogVisible = true">添加用户</el-button></el-col></el-row><!-- 用户列表区域 --><el-table :data="userlist" border stripe><el-table-column type="index"></el-table-column><el-table-column label="姓名" prop="username"></el-table-column><el-table-column label="邮箱" prop="email"></el-table-column><el-table-column label="电话" prop="mobile"></el-table-column><el-table-column label="角色" prop="role_name"></el-table-column><el-table-column label="状态"><template slot-scope="scope"><el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch></template></el-table-column><el-table-column label="操作" width="180px"><template slot-scope="scope"><!-- 修改按钮 --><el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button><!-- 删除按钮 --><el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.id)"></el-button><!-- 分配角色按钮 --><el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false"><el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)"></el-button></el-tooltip></template></el-table-column></el-table><!-- 分页区域 --><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination></el-card><!-- 添加用户的对话框 --><el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"><!-- 内容主体区域 --><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px"><el-form-item label="用户名" prop="username"><el-input v-model="addForm.username"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input v-model="addForm.password"></el-input></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="addForm.email"></el-input></el-form-item><el-form-item label="手机" prop="mobile"><el-input v-model="addForm.mobile"></el-input></el-form-item></el-form><!-- 底部区域 --><span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">取 消</el-button><el-button type="primary" @click="addUser">确 定</el-button></span></el-dialog><!-- 修改用户的对话框 --><el-dialog title="修改用户" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"><el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="70px"><el-form-item label="用户名"><el-input v-model="editForm.username" disabled></el-input></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="editForm.email"></el-input></el-form-item><el-form-item label="手机" prop="mobile"><el-input v-model="editForm.mobile"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">取 消</el-button><el-button type="primary" @click="editUserInfo">确 定</el-button></span></el-dialog><!-- 分配角色的对话框 --><el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="setRoleDialogClosed"><div><p>当前的用户:{{userInfo.username}}</p><p>当前的角色:{{userInfo.role_name}}</p><p>分配新角色:<el-select v-model="selectedRoleId" placeholder="请选择"><el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id"></el-option></el-select></p></div><span slot="footer" class="dialog-footer"><el-button @click="setRoleDialogVisible = false">取 消</el-button><el-button type="primary" @click="saveRoleInfo">确 定</el-button></span></el-dialog></div>
</template><script>
export default {data() {// 验证邮箱的规则var checkEmail = (rule, value, cb) => {// 验证邮箱的正则表达式const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/if (regEmail.test(value)) {// 合法的邮箱return cb()}cb(new Error('请输入合法的邮箱'))}// 验证手机号的规则var checkMobile = (rule, value, cb) => {// 验证手机号的正则表达式const regMobile = /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/if (regMobile.test(value)) {return cb()}cb(new Error('请输入合法的手机号'))}return {// 获取用户列表的参数对象queryInfo: {query: '',// 当前的页数pagenum: 1,// 当前每页显示多少条数据pagesize: 2},userlist: [],total: 0,// 控制添加用户对话框的显示与隐藏addDialogVisible: false,// 添加用户的表单数据addForm: {username: '',password: '',email: '',mobile: ''},// 添加表单的验证规则对象addFormRules: {username: [{ required: true, message: '请输入用户名', trigger: 'blur' },{min: 3,max: 10,message: '用户名的长度在3~10个字符之间',trigger: 'blur'}],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{min: 6,max: 15,message: '用户名的长度在6~15个字符之间',trigger: 'blur'}],email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },{ validator: checkEmail, trigger: 'blur' }],mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' },{ validator: checkMobile, trigger: 'blur' }]},// 控制修改用户对话框的显示与隐藏editDialogVisible: false,// 查询到的用户信息对象editForm: {},// 修改表单的验证规则对象editFormRules: {email: [{ required: true, message: '请输入用户邮箱', trigger: 'blur' },{ validator: checkEmail, trigger: 'blur' }],mobile: [{ required: true, message: '请输入用户手机', trigger: 'blur' },{ validator: checkMobile, trigger: 'blur' }]},// 控制分配角色对话框的显示与隐藏setRoleDialogVisible: false,// 需要被分配角色的用户信息userInfo: {},// 所有角色的数据列表rolesList: [],// 已选中的角色Id值selectedRoleId: ''}},created() {this.getUserList()},methods: {async getUserList() {const { data: res } = await this.$http.get('users', {params: this.queryInfo})if (res.meta.status !== 200) {return this.$message.error('获取用户列表失败!')}this.userlist = res.data.usersthis.total = res.data.totalconsole.log(res)},// 监听 pagesize 改变的事件handleSizeChange(newSize) {// console.log(newSize)this.queryInfo.pagesize = newSizethis.getUserList()},// 监听 页码值 改变的事件handleCurrentChange(newPage) {console.log(newPage)this.queryInfo.pagenum = newPagethis.getUserList()},// 监听 switch 开关状态的改变async userStateChanged(userinfo) {console.log(userinfo)const { data: res } = await this.$http.put(`users/${userinfo.id}/state/${userinfo.mg_state}`)if (res.meta.status !== 200) {userinfo.mg_state = !userinfo.mg_statereturn this.$message.error('更新用户状态失败!')}this.$message.success('更新用户状态成功!')},// 监听添加用户对话框的关闭事件addDialogClosed() {this.$refs.addFormRef.resetFields()},// 点击按钮,添加新用户addUser() {this.$refs.addFormRef.validate(async valid => {if (!valid) return// 可以发起添加用户的网络请求const { data: res } = await this.$http.post('users', this.addForm)if (res.meta.status !== 201) {this.$message.error('添加用户失败!')}this.$message.success('添加用户成功!')// 隐藏添加用户的对话框this.addDialogVisible = false// 重新获取用户列表数据this.getUserList()})},// 展示编辑用户的对话框async showEditDialog(id) {// console.log(id)const { data: res } = await this.$http.get('users/' + id)if (res.meta.status !== 200) {return this.$message.error('查询用户信息失败!')}this.editForm = res.datathis.editDialogVisible = true},// 监听修改用户对话框的关闭事件editDialogClosed() {this.$refs.editFormRef.resetFields()},// 修改用户信息并提交editUserInfo() {this.$refs.editFormRef.validate(async valid => {if (!valid) return// 发起修改用户信息的数据请求const { data: res } = await this.$http.put('users/' + this.editForm.id,{email: this.editForm.email,mobile: this.editForm.mobile})if (res.meta.status !== 200) {return this.$message.error('更新用户信息失败!')}// 关闭对话框this.editDialogVisible = false// 刷新数据列表this.getUserList()// 提示修改成功this.$message.success('更新用户信息成功!')})},// 根据Id删除对应的用户信息async removeUserById(id) {// 弹框询问用户是否删除数据const confirmResult = await this.$confirm('此操作将永久删除该用户, 是否继续?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(err => err)// 如果用户确认删除,则返回值为字符串 confirm// 如果用户取消了删除,则返回值为字符串 cancel// console.log(confirmResult)if (confirmResult !== 'confirm') {return this.$message.info('已取消删除')}const { data: res } = await this.$http.delete('users/' + id)if (res.meta.status !== 200) {return this.$message.error('删除用户失败!')}this.$message.success('删除用户成功!')this.getUserList()},// 展示分配角色的对话框async setRole(userInfo) {this.userInfo = userInfo// 在展示对话框之前,获取所有角色的列表const { data: res } = await this.$http.get('roles')if (res.meta.status !== 200) {return this.$message.error('获取角色列表失败!')}this.rolesList = res.datathis.setRoleDialogVisible = true},// 点击按钮,分配角色async saveRoleInfo() {if (!this.selectedRoleId) {return this.$message.error('请选择要分配的角色!')}const { data: res } = await this.$http.put(`users/${this.userInfo.id}/role`,{rid: this.selectedRoleId})if (res.meta.status !== 200) {return this.$message.error('更新角色失败!')}this.$message.success('更新角色成功!')this.getUserList()this.setRoleDialogVisible = false},// 监听分配角色对话框的关闭事件setRoleDialogClosed() {this.selectedRoleId = ''this.userInfo = {}}}
}
</script><style lang="less" scoped>
</style>

权限管理

角色列表

角色列表中可以创建新的角色,创建的新的角色可以在用户管理中赋予用户,同时可以为已有的角色赋予权限

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>权限管理</el-breadcrumb-item><el-breadcrumb-item>角色列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图 --><el-card><!-- 添加角色按钮区域 --><el-row><el-col><el-button type="primary">添加角色</el-button></el-col></el-row><!-- 角色列表区域 --><el-table :data="rolelist" border stripe><!-- 展开列 --><el-table-column type="expand"><template slot-scope="scope"><el-row :class="['bdbottom', i1 === 0 ? 'bdtop' : '', 'vcenter']" v-for="(item1, i1) in scope.row.children" :key="item1.id"><!-- 渲染一级权限 --><el-col :span="5"><el-tag closable @close="removeRightById(scope.row, item1.id)">{{item1.authName}}</el-tag><i class="el-icon-caret-right"></i></el-col><!-- 渲染二级和三级权限 --><el-col :span="19"><!-- 通过 for 循环 嵌套渲染二级权限 --><el-row :class="[i2 === 0 ? '' : 'bdtop', 'vcenter']" v-for="(item2, i2) in item1.children" :key="item2.id"><el-col :span="6"><el-tag type="success" closable @close="removeRightById(scope.row, item2.id)">{{item2.authName}}</el-tag><i class="el-icon-caret-right"></i></el-col><el-col :span="18"><el-tag type="warning" v-for="item3 in item2.children" :key="item3.id" closable @close="removeRightById(scope.row, item3.id)">{{item3.authName}}</el-tag></el-col></el-row></el-col></el-row><!-- <pre>{{scope.row}}</pre> --></template></el-table-column><!-- 索引列 --><el-table-column type="index"></el-table-column><el-table-column label="角色名称" prop="roleName"></el-table-column><el-table-column label="角色描述" prop="roleDesc"></el-table-column><el-table-column label="操作" width="300px"><template slot-scope="scope"><el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button><el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限</el-button></template></el-table-column></el-table></el-card><!-- 分配权限的对话框 --><el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClosed"><!-- 树形控件 --><el-tree :data="rightslist" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree><span slot="footer" class="dialog-footer"><el-button @click="setRightDialogVisible = false">取 消</el-button><el-button type="primary" @click="allotRights">确 定</el-button></span></el-dialog></div>
</template><script>
export default {data() {return {// 所有角色列表数据rolelist: [],// 控制分配权限对话框的显示与隐藏setRightDialogVisible: false,// 所有权限的数据rightslist: [],// 树形控件的属性绑定对象treeProps: {label: 'authName',children: 'children'},// 默认选中的节点Id值数组defKeys: [],// 当前即将分配权限的角色idroleId: ''}},created() {this.getRolesList()},methods: {// 获取所有角色的列表async getRolesList() {const { data: res } = await this.$http.get('roles')if (res.meta.status !== 200) {return this.$message.error('获取角色列表失败!')}this.rolelist = res.dataconsole.log(this.rolelist)},// 根据Id删除对应的权限async removeRightById(role, rightId) {// 弹框提示用户是否要删除const confirmResult = await this.$confirm('此操作将永久删除该文件, 是否继续?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(err => err)if (confirmResult !== 'confirm') {return this.$message.info('取消了删除!')}const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)if (res.meta.status !== 200) {return this.$message.error('删除权限失败!')}// this.getRolesList()role.children = res.data},// 展示分配权限的对话框async showSetRightDialog(role) {this.roleId = role.id// 获取所有权限的数据const { data: res } = await this.$http.get('rights/tree')if (res.meta.status !== 200) {return this.$message.error('获取权限数据失败!')}// 把获取到的权限数据保存到 data 中this.rightslist = res.dataconsole.log(this.rightslist)// 递归获取三级节点的Idthis.getLeafKeys(role, this.defKeys)this.setRightDialogVisible = true},// 通过递归的形式,获取角色下所有三级权限的id,并保存到 defKeys 数组中getLeafKeys(node, arr) {// 如果当前 node 节点不包含 children 属性,则是三级节点if (!node.children) {return arr.push(node.id)}node.children.forEach(item => this.getLeafKeys(item, arr))},// 监听分配权限对话框的关闭事件setRightDialogClosed() {this.defKeys = []},// 点击为角色分配权限async allotRights() {const keys = [...this.$refs.treeRef.getCheckedKeys(),...this.$refs.treeRef.getHalfCheckedKeys()]const idStr = keys.join(',')const { data: res } = await this.$http.post(`roles/${this.roleId}/rights`,{ rids: idStr })if (res.meta.status !== 200) {return this.$message.error('分配权限失败!')}this.$message.success('分配权限成功!')this.getRolesList()this.setRightDialogVisible = false}}
}
</script><style lang="less" scoped>
.el-tag {margin: 7px;
}.bdtop {border-top: 1px solid #eee;
}.bdbottom {border-bottom: 1px solid #eee;
}.vcenter {display: flex;align-items: center;
}
</style>

权限列表

权限列表对不同的权限做出展示,只发送一个请求即可获取所有需要的数据

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>权限管理</el-breadcrumb-item><el-breadcrumb-item>权限列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图 --><el-card><el-table :data="rightsList" border stripe><el-table-column type="index"></el-table-column><el-table-column label="权限名称" prop="authName"></el-table-column><el-table-column label="路径" prop="path"></el-table-column><el-table-column label="权限等级" prop="level"><template slot-scope="scope"><el-tag v-if="scope.row.level === '0'">一级</el-tag><el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag><el-tag type="warning" v-else>三级</el-tag></template></el-table-column></el-table></el-card></div>
</template><script>
export default {data() {return {// 权限列表rightsList: []}},created() {// 获取所有的权限this.getRightsList()},methods: {// 获取权限列表async getRightsList() {const { data: res } = await this.$http.get('rights/list')if (res.meta.status !== 200) {return this.$message.error('获取权限列表失败!')}this.rightsList = res.dataconsole.log(this.rightsList)}}
}
</script><style lang="less" scoped>
</style>

商品管理

商品分类

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>商品分类</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><el-row><el-col><el-button type="primary" @click="showAddCateDialog">添加分类</el-button></el-col></el-row><!-- 表格 --><tree-table class="treeTable" :data="catelist" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border :show-row-hover="false"><!-- 是否有效 --><template slot="isok" slot-scope="scope"><i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color: lightgreen;"></i><i class="el-icon-error" v-else style="color: red;"></i></template><!-- 排序 --><template slot="order" slot-scope="scope"><el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag><el-tag type="success" size="mini" v-else-if="scope.row.cat_level===1">二级</el-tag><el-tag type="warning" size="mini" v-else>三级</el-tag></template><!-- 操作 --><template slot="opt"><el-button type="primary" icon="el-icon-edit" size="mini">编辑</el-button><el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button></template></tree-table><!-- 分页区域 --><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="querInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="querInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination></el-card><!-- 添加分类的对话框 --><el-dialog title="添加分类" :visible.sync="addCateDialogVisible" width="50%" @close="addCateDialogClosed"><!-- 添加分类的表单 --><el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRef" label-width="100px"><el-form-item label="分类名称:" prop="cat_name"><el-input v-model="addCateForm.cat_name"></el-input></el-form-item><el-form-item label="父级分类:"><!-- options 用来指定数据源 --><!-- props 用来指定配置对象 --><el-cascader expand-trigger="hover" :options="parentCateList" :props="cascaderProps" v-model="selectedKeys" @change="parentCateChanged" clearable change-on-select></el-cascader></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addCateDialogVisible = false">取 消</el-button><el-button type="primary" @click="addCate">确 定</el-button></span></el-dialog></div>
</template><script>
export default {data() {return {// 查询条件querInfo: {type: 3,pagenum: 1,pagesize: 5},// 商品分类的数据列表,默认为空catelist: [],// 总数据条数total: 0,// 为table指定列的定义columns: [{label: '分类名称',prop: 'cat_name'},{label: '是否有效',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'isok'},{label: '排序',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'order'},{label: '操作',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'opt'}],// 控制添加分类对话框的显示与隐藏addCateDialogVisible: false,// 添加分类的表单数据对象addCateForm: {// 将要添加的分类的名称cat_name: '',// 父级分类的Idcat_pid: 0,// 分类的等级,默认要添加的是1级分类cat_level: 0},// 添加分类表单的验证规则对象addCateFormRules: {cat_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]},// 父级分类的列表parentCateList: [],// 指定级联选择器的配置对象cascaderProps: {value: 'cat_id',label: 'cat_name',children: 'children'},// 选中的父级分类的Id数组selectedKeys: []}},created() {this.getCateList()},methods: {// 获取商品分类数据async getCateList() {const { data: res } = await this.$http.get('categories', {params: this.querInfo})if (res.meta.status !== 200) {return this.$message.error('获取商品分类失败!')}console.log(res.data)// 把数据列表,赋值给 catelistthis.catelist = res.data.result// 为总数据条数赋值this.total = res.data.total},// 监听 pagesize 改变handleSizeChange(newSize) {this.querInfo.pagesize = newSizethis.getCateList()},// 监听 pagenum 改变handleCurrentChange(newPage) {this.querInfo.pagenum = newPagethis.getCateList()},// 点击按钮,展示添加分类的对话框showAddCateDialog() {// 先获取父级分类的数据列表this.getParentCateList()// 再展示出对话框this.addCateDialogVisible = true},// 获取父级分类的数据列表async getParentCateList() {const { data: res } = await this.$http.get('categories', {params: { type: 2 }})if (res.meta.status !== 200) {return this.$message.error('获取父级分类数据失败!')}console.log(res.data)this.parentCateList = res.data},// 选择项发生变化触发这个函数parentCateChanged() {console.log(this.selectedKeys)// 如果 selectedKeys 数组中的 length 大于0,证明选中的父级分类// 反之,就说明没有选中任何父级分类if (this.selectedKeys.length > 0) {// 父级分类的Idthis.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]// 为当前分类的等级赋值this.addCateForm.cat_level = this.selectedKeys.length} else {// 父级分类的Idthis.addCateForm.cat_pid = 0// 为当前分类的等级赋值this.addCateForm.cat_level = 0}},// 点击按钮,添加新的分类addCate() {this.$refs.addCateFormRef.validate(async valid => {if (!valid) returnconst { data: res } = await this.$http.post('categories', this.addCateForm)if (res.meta.status !== 201) {return this.$message.error('添加分类失败!')}this.$message.success('添加分类成功!')this.getCateList()this.addCateDialogVisible = false})},// 监听对话框的关闭事件,重置表单数据addCateDialogClosed() {this.$refs.addCateFormRef.resetFields()this.selectedKeys = []this.addCateForm.cat_level = 0this.addCateForm.cat_pid = 0}}
}
</script><style lang="less" scoped>
.treeTable {margin-top: 15px;
}.el-cascader {width: 100%;
}
</style>

商品列表

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>商品列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><el-row :gutter="20"><el-col :span="8"><el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getGoodsList"><el-button slot="append" icon="el-icon-search" @click="getGoodsList"></el-button></el-input></el-col><el-col :span="4"><el-button type="primary" @click="goAddpage">添加商品</el-button></el-col></el-row><!-- table表格区域 --><el-table :data="goodslist" border stripe><el-table-column type="index"></el-table-column><el-table-column label="商品名称" prop="goods_name"></el-table-column><el-table-column label="商品价格(元)" prop="goods_price" width="95px"></el-table-column><el-table-column label="商品重量" prop="goods_weight" width="70px"></el-table-column><el-table-column label="创建时间" prop="add_time" width="140px"><template slot-scope="scope">{{scope.row.add_time | dateFormat}}</template></el-table-column><el-table-column label="操作" width="130px"><template slot-scope="scope"><el-button type="primary" icon="el-icon-edit" size="mini"></el-button><el-button type="danger" icon="el-icon-delete" size="mini" @click="removeById(scope.row.goods_id)"></el-button></template></el-table-column></el-table><!-- 分页区域 --><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[5, 10, 15, 20]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total" background></el-pagination></el-card></div>
</template><script>
export default {data() {return {// 查询参数对象queryInfo: {query: '',pagenum: 1,pagesize: 10},// 商品列表goodslist: [],// 总数据条数total: 0}},created() {this.getGoodsList()},methods: {// 根据分页获取对应的商品列表async getGoodsList() {const { data: res } = await this.$http.get('goods', {params: this.queryInfo})if (res.meta.status !== 200) {return this.$message.error('获取商品列表失败!')}this.$message.success('获取商品列表成功!')console.log(res.data)this.goodslist = res.data.goodsthis.total = res.data.total},handleSizeChange(newSize) {this.queryInfo.pagesize = newSizethis.getGoodsList()},handleCurrentChange(newPage) {this.queryInfo.pagenum = newPagethis.getGoodsList()},async removeById(id) {const confirmResult = await this.$confirm('此操作将永久删除该商品, 是否继续?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(err => err)if (confirmResult !== 'confirm') {return this.$message.info('已经取消删除!')}const { data: res } = await this.$http.delete(`goods/${id}`)if (res.meta.status !== 200) {return this.$message.error('删除失败!')}this.$message.success('删除成功!')this.getGoodsList()},goAddpage() {this.$router.push('/goods/add')}}
}
</script><style lang="less" scoped>
</style>

增加商品

在商品分类中点击新增商品,则跳转到新增商品窗口

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>添加商品</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图 --><el-card><!-- 提示区域 --><el-alert title="添加商品信息" type="info" center show-icon :closable="false"></el-alert><!-- 步骤条区域 --><el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center><el-step title="基本信息"></el-step><el-step title="商品参数"></el-step><el-step title="商品属性"></el-step><el-step title="商品图片"></el-step><el-step title="商品内容"></el-step><el-step title="完成"></el-step></el-steps><!-- tab栏区域 --><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" label-position="top"><el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked"><el-tab-pane label="基本信息" name="0"><el-form-item label="商品名称" prop="goods_name"><el-input v-model="addForm.goods_name"></el-input></el-form-item><el-form-item label="商品价格" prop="goods_price"><el-input v-model="addForm.goods_price" type="number"></el-input></el-form-item><el-form-item label="商品重量" prop="goods_weight"><el-input v-model="addForm.goods_weight" type="number"></el-input></el-form-item><el-form-item label="商品数量" prop="goods_number"><el-input v-model="addForm.goods_number" type="number"></el-input></el-form-item><el-form-item label="商品分类" prop="goods_cat"><el-cascader expand-trigger="hover" :options="catelist" :props="cateProps" v-model="addForm.goods_cat" @change="handleChange"></el-cascader></el-form-item></el-tab-pane><el-tab-pane label="商品参数" name="1"><!-- 渲染表单的Item项 --><el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id"><!-- 复选框组 --><el-checkbox-group v-model="item.attr_vals"><el-checkbox :label="cb" v-for="(cb, i) in item.attr_vals" :key="i" border></el-checkbox></el-checkbox-group></el-form-item></el-tab-pane><el-tab-pane label="商品属性" name="2"><el-form-item :label="item.attr_name" v-for="item in onlyTableData" :key="item.attr_id"><el-input v-model="item.attr_vals"></el-input></el-form-item></el-tab-pane><el-tab-pane label="商品图片" name="3"><!-- action 表示图片要上传到的后台API地址 --><el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :headers="headerObj" :on-success="handleSuccess"><el-button size="small" type="primary">点击上传</el-button></el-upload></el-tab-pane><el-tab-pane label="商品内容" name="4"><!-- 富文本编辑器组件 --><quill-editor v-model="addForm.goods_introduce"></quill-editor><!-- 添加商品的按钮 --><el-button type="primary" class="btnAdd" @click="add">添加商品</el-button></el-tab-pane></el-tabs></el-form></el-card><!-- 图片预览 --><el-dialog title="图片预览" :visible.sync="previewVisible" width="50%"><img :src="previewPath" alt="" class="previewImg"></el-dialog></div>
</template><script>
import _ from 'lodash'export default {data() {return {activeIndex: '0',// 添加商品的表单数据对象addForm: {goods_name: '',goods_price: 0,goods_weight: 0,goods_number: 0,// 商品所属的分类数组goods_cat: [],// 图片的数组pics: [],// 商品的详情描述goods_introduce: '',attrs: []},addFormRules: {goods_name: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],goods_price: [{ required: true, message: '请输入商品价格', trigger: 'blur' }],goods_weight: [{ required: true, message: '请输入商品重量', trigger: 'blur' }],goods_number: [{ required: true, message: '请输入商品数量', trigger: 'blur' }],goods_cat: [{ required: true, message: '请选择商品分类', trigger: 'blur' }]},// 商品分类列表catelist: [],cateProps: {label: 'cat_name',value: 'cat_id',children: 'children'},// 动态参数列表数据manyTableData: [],// 静态属性列表数据onlyTableData: [],// 上传图片的URL地址uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',// 图片上传组件的headers请求头对象headerObj: {Authorization: window.sessionStorage.getItem('token')},previewPath: '',previewVisible: false}},created() {this.getCateList()},methods: {// 获取所有商品分类数据async getCateList() {const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('获取商品分类数据失败!')}this.catelist = res.dataconsole.log(this.catelist)},// 级联选择器选中项变化,会触发这个函数handleChange() {console.log(this.addForm.goods_cat)if (this.addForm.goods_cat.length !== 3) {this.addForm.goods_cat = []}},beforeTabLeave(activeName, oldActiveName) {// console.log('即将离开的标签页名字是:' + oldActiveName)// console.log('即将进入的标签页名字是:' + activeName)// return falseif (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) {this.$message.error('请先选择商品分类!')return false}},async tabClicked() {// console.log(this.activeIndex)// 证明访问的是动态参数面板if (this.activeIndex === '1') {const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: { sel: 'many' }})if (res.meta.status !== 200) {return this.$message.error('获取动态参数列表失败!')}console.log(res.data)res.data.forEach(item => {item.attr_vals =item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')})this.manyTableData = res.data} else if (this.activeIndex === '2') {const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: { sel: 'only' }})if (res.meta.status !== 200) {return this.$message.error('获取静态属性失败!')}console.log(res.data)this.onlyTableData = res.data}},// 处理图片预览效果handlePreview(file) {console.log(file)this.previewPath = file.response.data.urlthis.previewVisible = true},// 处理移除图片的操作handleRemove(file) {// console.log(file)// 1. 获取将要删除的图片的临时路径const filePath = file.response.data.tmp_path// 2. 从 pics 数组中,找到这个图片对应的索引值const i = this.addForm.pics.findIndex(x => x.pic === filePath)// 3. 调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除this.addForm.pics.splice(i, 1)console.log(this.addForm)},// 监听图片上传成功的事件handleSuccess(response) {console.log(response)// 1. 拼接得到一个图片信息对象const picInfo = { pic: response.data.tmp_path }// 2. 将图片信息对象,push 到pics数组中this.addForm.pics.push(picInfo)console.log(this.addForm)},// 添加商品add() {this.$refs.addFormRef.validate(async valid => {if (!valid) {return this.$message.error('请填写必要的表单项!')}// 执行添加的业务逻辑// lodash   cloneDeep(obj)const form = _.cloneDeep(this.addForm)form.goods_cat = form.goods_cat.join(',')// 处理动态参数this.manyTableData.forEach(item => {const newInfo = {attr_id: item.attr_id,attr_value: item.attr_vals.join(' ')}this.addForm.attrs.push(newInfo)})// 处理静态属性this.onlyTableData.forEach(item => {const newInfo = { attr_id: item.attr_id, attr_value: item.attr_vals }this.addForm.attrs.push(newInfo)})form.attrs = this.addForm.attrsconsole.log(form)// 发起请求添加商品// 商品的名称,必须是唯一的const { data: res } = await this.$http.post('goods', form)if (res.meta.status !== 201) {return this.$message.error('添加商品失败!')}this.$message.success('添加商品成功!')this.$router.push('/goods')})}},computed: {cateId() {if (this.addForm.goods_cat.length === 3) {return this.addForm.goods_cat[2]}return null}}
}
</script><style lang="less" scoped>
.el-checkbox {margin: 0 10px 0 0 !important;
}.previewImg {width: 100%;
}.btnAdd {margin-top: 15px;
}
</style>

分类参数

在分类参数中选择一件商品,可以为其添加静态或者动态参数,这个参数可以展示在移动端商品的属性中。

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>商品管理</el-breadcrumb-item><el-breadcrumb-item>参数列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><!-- 警告区域 --><el-alert show-icon title="注意:只允许为第三级分类设置相关参数!" type="warning" :closable="false"></el-alert><!-- 选择商品分类区域 --><el-row class="cat_opt"><el-col><span>选择商品分类:</span><!-- 选择商品分类的级联选择框 --><el-cascader expand-trigger="hover" :options="catelist" :props="cateProps" v-model="selectedCateKeys" @change="handleChange"></el-cascader></el-col></el-row><!-- tab 页签区域 --><el-tabs v-model="activeName" @tab-click="handleTabClick"><!-- 添加动态参数的面板 --><el-tab-pane label="动态参数" name="many"><!-- 添加参数的按钮 --><el-button type="primary" size="mini" :disabled="isBtnDisabled" @click="addDialogVisible=true">添加参数</el-button><!-- 动态参数表格 --><el-table :data="manyTableData" border stripe><!-- 展开行 --><el-table-column type="expand"><template slot-scope="scope"><!-- 循环渲染Tag标签 --><el-tag v-for="(item, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i, scope.row)">{{item}}</el-tag><!-- 输入的文本框 --><el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"></el-input><!-- 添加按钮 --><el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><!-- 索引列 --><el-table-column type="index"></el-table-column><el-table-column label="参数名称" prop="attr_name"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditDialog(scope.row.attr_id)">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane><!-- 添加静态属性的面板 --><el-tab-pane label="静态属性" name="only"><!-- 添加属性的按钮 --><el-button type="primary" size="mini" :disabled="isBtnDisabled" @click="addDialogVisible=true">添加属性</el-button><!-- 静态属性表格 --><el-table :data="onlyTableData" border stripe><!-- 展开行 --><el-table-column type="expand"><template slot-scope="scope"><!-- 循环渲染Tag标签 --><el-tag v-for="(item, i) in scope.row.attr_vals" :key="i" closable @close="handleClose(i, scope.row)">{{item}}</el-tag><!-- 输入的文本框 --><el-input class="input-new-tag" v-if="scope.row.inputVisible" v-model="scope.row.inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm(scope.row)" @blur="handleInputConfirm(scope.row)"></el-input><!-- 添加按钮 --><el-button v-else class="button-new-tag" size="small" @click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><!-- 索引列 --><el-table-column type="index"></el-table-column><el-table-column label="属性名称" prop="attr_name"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditDialog(scope.row.attr_id)">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane></el-tabs></el-card><!-- 添加参数的对话框 --><el-dialog :title="'添加' + titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"><!-- 添加参数的对话框 --><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="addForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">取 消</el-button><el-button type="primary" @click="addParams">确 定</el-button></span></el-dialog><!-- 修改参数的对话框 --><el-dialog :title="'修改' + titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"><!-- 添加参数的对话框 --><el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="editForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">取 消</el-button><el-button type="primary" @click="editParams">确 定</el-button></span></el-dialog></div>
</template><script>
export default {data() {return {// 商品分类列表catelist: [],// 级联选择框的配置对象cateProps: {value: 'cat_id',label: 'cat_name',children: 'children'},// 级联选择框双向绑定到的数组selectedCateKeys: [],// 被激活的页签的名称activeName: 'many',// 动态参数的数据manyTableData: [],// 静态属性的数据onlyTableData: [],// 控制添加对话框的显示与隐藏addDialogVisible: false,// 添加参数的表单数据对象addForm: {attr_name: ''},// 添加表单的验证规则对象addFormRules: {attr_name: [{ required: true, message: '请输入参数名称', trigger: 'blur' }]},// 控制修改对话框的显示与隐藏editDialogVisible: false,// 修改的表单数据对象editForm: {},// 修改表单的验证规则对象editFormRules: {attr_name: [{ required: true, message: '请输入参数名称', trigger: 'blur' }]}}},created() {this.getCateList()},methods: {// 获取所有的商品分类列表async getCateList() {const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('获取商品分类失败!')}this.catelist = res.dataconsole.log(this.catelist)},// 级联选择框选中项变化,会触发这个函数handleChange() {this.getParamsData()},// tab 页签点击事件的处理函数handleTabClick() {console.log(this.activeName)this.getParamsData()},// 获取参数的列表数据async getParamsData() {// 证明选中的不是三级分类if (this.selectedCateKeys.length !== 3) {this.selectedCateKeys = []this.manyTableData = []this.onlyTableData = []return}// 证明选中的是三级分类console.log(this.selectedCateKeys)// 根据所选分类的Id,和当前所处的面板,获取对应的参数const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: { sel: this.activeName }})if (res.meta.status !== 200) {return this.$message.error('获取参数列表失败!')}res.data.forEach(item => {item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []// 控制文本框的显示与隐藏item.inputVisible = false// 文本框中输入的值item.inputValue = ''})console.log(res.data)if (this.activeName === 'many') {this.manyTableData = res.data} else {this.onlyTableData = res.data}},// 监听添加对话框的关闭事件addDialogClosed() {this.$refs.addFormRef.resetFields()},// 点击按钮,添加参数addParams() {this.$refs.addFormRef.validate(async valid => {if (!valid) returnconst { data: res } = await this.$http.post(`categories/${this.cateId}/attributes`,{attr_name: this.addForm.attr_name,attr_sel: this.activeName})if (res.meta.status !== 201) {return this.$message.error('添加参数失败!')}this.$message.success('添加参数成功!')this.addDialogVisible = falsethis.getParamsData()})},// 点击按钮,展示修改的对话框async showEditDialog(attrId) {// 查询当前参数的信息const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes/${attrId}`,{params: { attr_sel: this.activeName }})if (res.meta.status !== 200) {return this.$message.error('获取参数信息失败!')}this.editForm = res.datathis.editDialogVisible = true},// 重置修改的表单editDialogClosed() {this.$refs.editFormRef.resetFields()},// 点击按钮,修改参数信息editParams() {this.$refs.editFormRef.validate(async valid => {if (!valid) returnconst { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`,{ attr_name: this.editForm.attr_name, attr_sel: this.activeName })if (res.meta.status !== 200) {return this.$message.error('修改参数失败!')}this.$message.success('修改参数成功!')this.getParamsData()this.editDialogVisible = false})},// 根据Id删除对应的参数项async removeParams(attrId) {const confirmResult = await this.$confirm('此操作将永久删除该参数, 是否继续?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).catch(err => err)// 用户取消了删除的操作if (confirmResult !== 'confirm') {return this.$message.info('已取消删除!')}// 删除的业务逻辑const { data: res } = await this.$http.delete(`categories/${this.cateId}/attributes/${attrId}`)if (res.meta.status !== 200) {return this.$message.error('删除参数失败!')}this.$message.success('删除参数成功!')this.getParamsData()},// 文本框失去焦点,或摁下了 Enter 都会触发async handleInputConfirm(row) {if (row.inputValue.trim().length === 0) {row.inputValue = ''row.inputVisible = falsereturn}// 如果没有return,则证明输入的内容,需要做后续处理row.attr_vals.push(row.inputValue.trim())row.inputValue = ''row.inputVisible = false// 需要发起请求,保存这次操作this.saveAttrVals(row)},// 将对 attr_vals 的操作,保存到数据库async saveAttrVals(row) {// 需要发起请求,保存这次操作const { data: res } = await this.$http.put(`categories/${this.cateId}/attributes/${row.attr_id}`,{attr_name: row.attr_name,attr_sel: row.attr_sel,attr_vals: row.attr_vals.join(' ')})if (res.meta.status !== 200) {return this.$message.error('修改参数项失败!')}this.$message.success('修改参数项成功!')},// 点击按钮,展示文本输入框showInput(row) {row.inputVisible = true// 让文本框自动获得焦点// $nextTick 方法的作用,就是当页面上元素被重新渲染之后,才会指定回调函数中的代码this.$nextTick(_ => {this.$refs.saveTagInput.$refs.input.focus()})},// 删除对应的参数可选项handleClose(i, row) {row.attr_vals.splice(i, 1)this.saveAttrVals(row)}},computed: {// 如果按钮需要被禁用,则返回true,否则返回falseisBtnDisabled() {if (this.selectedCateKeys.length !== 3) {return true}return false},// 当前选中的三级分类的IdcateId() {if (this.selectedCateKeys.length === 3) {return this.selectedCateKeys[2]}return null},// 动态计算标题的文本titleText() {if (this.activeName === 'many') {return '动态参数'}return '静态属性'}}
}
</script><style lang="less" scoped>
.cat_opt {margin: 15px 0;
}.el-tag {margin: 10px;
}.input-new-tag {width: 120px;
}
</style>

订单管理

订单列表

订单管理的实现和用户管理有很多类似的地方,都是向后端发送请求然后渲染到页面上

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>订单管理</el-breadcrumb-item><el-breadcrumb-item>订单列表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><el-row><el-col :span="8"><el-input placeholder="请输入内容"><el-button slot="append" icon="el-icon-search"></el-button></el-input></el-col></el-row><!-- 订单列表数据 --><el-table :data="orderlist" border stripe><el-table-column type="index"></el-table-column><el-table-column label="订单编号" prop="order_number"></el-table-column><el-table-column label="订单价格" prop="order_price"></el-table-column><el-table-column label="是否付款" prop="pay_status"><template slot-scope="scope"><el-tag type="success" v-if="scope.row.pay_status === '1'">已付款</el-tag><el-tag type="danger" v-else>未付款</el-tag></template></el-table-column><el-table-column label="是否发货" prop="is_send"><template slot-scope="scope"><template>{{scope.row.is_send}}</template></template></el-table-column><el-table-column label="下单时间" prop="create_time"><template slot-scope="scope">{{scope.row.create_time | dateFormat}}</template></el-table-column><el-table-column label="操作"><template><el-button size="mini" type="primary" icon="el-icon-edit" @click="showBox"></el-button><el-button size="mini" type="success" icon="el-icon-location" @click="showProgressBox"></el-button></template></el-table-column></el-table><!-- 分页区域 --><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination></el-card><!-- 修改地址的对话框 --><el-dialog title="修改地址" :visible.sync="addressVisible" width="50%" @close="addressDialogClosed"><el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="100px"><el-form-item label="省市区/县" prop="address1"><el-cascader :options="cityData" v-model="addressForm.address1"></el-cascader></el-form-item><el-form-item label="详细地址" prop="address2"><el-input v-model="addressForm.address2"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addressVisible = false">取 消</el-button><el-button type="primary" @click="addressVisible = false">确 定</el-button></span></el-dialog><!-- 展示物流进度的对话框 --><el-dialog title="物流进度" :visible.sync="progressVisible" width="50%"><!-- 时间线 --><el-timeline><el-timeline-item v-for="(activity, index) in progressInfo" :key="index" :timestamp="activity.time">{{activity.context}}</el-timeline-item></el-timeline></el-dialog></div>
</template><script>
import cityData from './citydata.js'export default {data() {return {queryInfo: {query: '',pagenum: 1,pagesize: 10},total: 0,orderlist: [],addressVisible: false,addressForm: {address1: [],address2: ''},addressFormRules: {address1: [{ required: true, message: '请选择省市区县', trigger: 'blur' }],address2: [{ required: true, message: '请填写详细地址', trigger: 'blur' }]},cityData,progressVisible: false,progressInfo: []}},created() {this.getOrderList()},methods: {async getOrderList() {const { data: res } = await this.$http.get('orders', {params: this.queryInfo})if (res.meta.status !== 200) {return this.$message.error('获取订单列表失败!')}console.log(res)this.total = res.data.totalthis.orderlist = res.data.goods},handleSizeChange(newSize) {this.queryInfo.pagesize = newSizethis.getOrderList()},handleCurrentChange(newPage) {this.queryInfo.pagenum = newPagethis.getOrderList()},// 展示修改地址的对话框showBox() {this.addressVisible = true},addressDialogClosed() {this.$refs.addressFormRef.resetFields()},async showProgressBox() {const { data: res } = await this.$http.get('/kuaidi/804909574412544580')if (res.meta.status !== 200) {return this.$message.error('获取物流进度失败!')}this.progressInfo = res.datathis.progressVisible = trueconsole.log(this.progressInfo)}}
}
</script><style lang="less" scoped>
@import '../../plugins/timeline/timeline.css';
@import '../../plugins/timeline-item/timeline-item.css';.el-cascader {width: 100%;
}
</style>

数据统计

数据报表

数据统计部分用到了echarts,从后端获得数据后通过 _.merge()将数据组合在一起,最后渲染在页面上

<template><div><!-- 面包屑导航区域 --><el-breadcrumb separator-class="el-icon-arrow-right"><el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item><el-breadcrumb-item>数据统计</el-breadcrumb-item><el-breadcrumb-item>数据报表</el-breadcrumb-item></el-breadcrumb><!-- 卡片视图区域 --><el-card><!-- 2. 为ECharts准备一个具备大小(宽高)的Dom --><div id="main" style="width: 750px;height:400px;"></div></el-card></div>
</template><script>
// 1. 导入 echarts
import echarts from 'echarts'
import _ from 'lodash'export default {data() {return {// 需要合并的数据options: {title: {text: '用户来源'},tooltip: {trigger: 'axis',axisPointer: {type: 'cross',label: {backgroundColor: '#E9EEF3'}}},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},xAxis: [{boundaryGap: false}],yAxis: [{type: 'value'}]}}},created() {},// 此时,页面上的元素,已经被渲染完毕了!async mounted() {// 3. 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'))const { data: res } = await this.$http.get('reports/type/1')if (res.meta.status !== 200) {return this.$message.error('获取折线图数据失败!')}// 4. 准备数据和配置项const result = _.merge(res.data, this.options)// 5. 展示数据myChart.setOption(result)},methods: {}
}
</script><style lang="less" scoped>
</style>

项目git地址

目录中有后端以及sql文件,按照说明运行即可

电商系统前端: 电商管理系统前端

学习资源

黑马程序员前端

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/178602.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验八-数据处理

目录 1.数据来源 2.对于All表&#xff1a; 3.对于sf 和sfweibo 表&#xff1a; 4. 对于base_info表&#xff1a; 5.导出最后结果到一个Excel文件中&#xff0c;完成数据处理。 1.数据来源 本次所分析的数据是通过爬虫抓取的微博数据。选取新浪微博为数据平台&#xff0c;选…

STM32CubeIDE(ADC)

学习链接&#xff1a;【HAL库详解】STM32 ADC HAL库使用_哔哩哔哩_bilibili 目录 一、概念 1、ADC介绍 2、ADC主要特征 二、ADC模式 1、查询模式 1.1 单通道采集软件配置 1.2 查询模式步奏 1.3 单通道采集主要代码 2、多通道采集 2.1 软件配置 2.2 主要代码 一、概…

【Linux】Linux第一个小程序 --- 进度条

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和Linux还有算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 …

电子学会C/C++编程等级考试2022年06月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:制作蛋糕 小A擅长制作香蕉蛋糕和巧克力蛋糕。制作一个香蕉蛋糕需要2个单位的香蕉,250个单位的面粉,75个单位的糖,100个单位的黄油。制作一个巧克力蛋糕需要75个单位的可可粉,200个单位的面粉,150个单位的糖,150个单位的黄…

jenkins 代码执行 (CVE-2017-1000353)漏洞复现

jenkins 代码执行 (CVE-2017-1000353)漏洞复现 名称: jenkins 代码执行 &#xff08;CVE-2017-1000353&#xff09; 描述: ​Jenkins 可以通过其网页界面轻松设置和配置,其中包括即时错误检查和内置帮助。 插件 通过更新中心中的 1000 多个插件,Jenkins 集成了持续集成和持续…

【C++初阶(九)】 priority_queue的使用与模拟实现

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

删除链表的倒数第N个节点,剑指offerII(21),力扣

目录 题目地址&#xff1a; 题目&#xff1a; 相似类型题&#xff1a; 我们直接看本题题解吧&#xff1a; 解题方法&#xff1a; 难度分析&#xff1a; 解题分析&#xff1a; 解题思路&#xff08;双指针&#xff09;&#xff1a; 代码实现&#xff1a; 代码说明&#xff1a; 代…

C++基础 -8- 函数重载

函数重载格式(图片代码段呈现) #include "iostream"using namespace std;void rlxy(int a) {cout << "int a"<< endl; }void rlxy(char a) {cout << "char a"<< endl; }int main() {rlxy(99);rlxy(c); }函数重载的依据…

从Android面试题目溯源-1、创建线程有那几种方式

概念 程序执行流的最小单位&#xff0c;处理器调度调度和分派的基本单位。 如何理解这个概念 如下图&#xff0c;可以简单类比吉他&#xff0c;六根弦代表六个线程&#xff0c;每个线程独立且单独运行&#xff0c;且持有上一个音的状态&#xff0c;每根手指可类比为一个CPU的…

matlab绘图函数plot和fplot的区别

一、背景 有的函数用plot画就会报错&#xff0c;显示数据必须为可转换为双精度值的数值、日期时间、持续时间、分类或数组。 如下图所示&#xff1a; 但用fplot函数就没有问题&#xff0c;因此这里记录一下两者的区别&#xff0c;如果使用不当&#xff0c;画出的图可能就是下…

23种设计模式之C++实践

23种设计模式之C++实践 1. 简介2. 基础知识3. 设计模式(一)创建型模式1. 单例模式1.2 饿汉式单例模式1.3 懒汉式单例模式比较IoDH单例模式总结2. 简单工厂模式简单工厂模式总结3. 工厂方法模式工厂方法模式总结4. 抽象工厂模式抽象工厂模式总结5. 原型模式原型模式总结6. 建造…

Leetcode算法之哈希表

目录 1.两数之和2.判定是否互为字符重排3.存在重复元素I4.存在重复元素II5.字母异位词分组 1.两数之和 两数之和 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int,int> hash;for(int i0;i<nums.si…

2020年2月25日 Go生态洞察:Go 1.14版本发布

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

linux进入telnet和推出telnet

安装telnet centos7 yum install -y telnet ubuntu apt install -y telnet 进入telnet telnet ip port 退出telnet 1. 按下下面的组合键 ctrl] 2. 输入下面命令推出 quit

电荷泵升压/降压电路

一、升压\降压电路原理分析 1、升压电路 电荷泵升压电路 VoutVa5V 5V_PLUS0V时&#xff0c;Va给C2充电&#xff0c;C2上节点电压比C2下节点电压高Va&#xff1b; 5V_PLUS5V时&#xff0c;C2电压不能突变&#xff0c;C2上节点电压依然比C2下节点电压高Va&#xff0c;但C2下节点…

关于AM5系列微机保护装置在某产业园配电工程中的应用-安科瑞 蒋静

1 摘要 目前&#xff0c;微机保护装置广泛应用于电力系统中&#xff0c;该类装置能够合理监测电力系统的运行状况&#xff0c;并实时记录电力系统出现故障的位置及性质&#xff0c;从而为故障的快速处理提供合理的参考信息。本文介绍的AM5系列微机保护装置&#xff0c;可以针对…

东用科技智能公交识别系统无线传输方案

在科技不断进步和人工智能快速发展的当下&#xff0c;人脸识别技术已逐渐应用于各个领域。其中&#xff0c;公共交通领域便是重要的应用场景之一。人脸识别技术的引入可以提高交通的安全性、效率及便利性。 为了实现公交公司对乘客的身份识别和安全管理的需求&#xff0c;提高运…

【Linux系统编程】进程概念详解(什么是进程?如何查看进程?)

目录 一、前言 二、 什么是进程&#xff1f; &#x1f4a6;引出进程 &#x1f4a6;进程的基本概念 &#x1f4a6;理解进程 ⭐描述进程--PCB&#xff08;进程控制块&#xff09; ⭐组织进程 三、查看进程 &#x1f4a6; 通过 ps 命令查看进程 &#x1f4a6; 通过 l…

口袋参谋:如何识别买家旺旺号?这招超简单!

​想要不被骗钱、跑路&#xff01;那商家在销量递增之前&#xff0c;一定要验买家旺旺号&#xff01;那如何快速验出买家是人还是“鬼”&#xff0c;我们就需要借助验号工具了。 说到这个验号工具&#xff0c;我不得不说&#xff0c;口袋参谋照妖镜查号功能&#xff0c;一键快速…

2021年12月 Scratch图形化(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共15题,每题2分,共30分) 第1题 下图两个积木的值分别是? A:false true B:false false C:true true D:true false 答案:A 第2题 小猫和小狗是非常好的朋友,他们发明了一种加密方法:用两位数字代表字母。…