电商后台管理系统
电商后台管理系统功能模块前端技术栈项目初始化安装vue-cli脚手架安装Vue Route路由安装Axios安装Echarts项目结构登录组件templatescriptCSShome组件Welcome组件User组件User--templateUser--scriptUser--CSS功能模块具体实现管理用户账号,即登录、退出、用户管理、权限管理等,商品管理,即商品分类、分类参数、商品信息,订单
电商后台管理系统
功能模块
具体实现管理用户账号,即登录、退出、用户管理、权限管理等,商品管理,即商品分类、分类参数、商品信息,订单信息等以及数据统计。
前端技术栈
项目初始化
安装vue-cli脚手架
通过命令行全局安装vue-cli3.0以上版本脚手架。
npm install vue-cli -g
安装Vue Route路由
NPM
npm install vue-router
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
import Vue from ‘vue’
import VueRouter from ‘vue-router’
Vue.use(VueRouter)
##安装Element-UI
NPM
npm i element-ui -S
安装Axios
NPM
npm install axios
安装Echarts
NPM
npm install echarts --save
注意
在用引用echarts的时候可能会出现一下错误
报错原因
通过npm安装的是最新版echarts依赖,5.x版本,但是网上大部分教程的引入方式是按照4.x的方式引入,而5.x版本的引入方式有所改变,所以造成报错;
解决方案
将main.js中引入echarts的方式由4.x引入方式:
import echarts from ‘echarts’
换为5.x引入方式:
import * as echarts from ‘echarts’
项目结构
- node_modules:用于存放用包管理工具下载安装了的包
- public:用于存放静态资源文件(不会经过webpack的打包处理)
- src:用于存放项目的源码文件
-assets:用于存放静态资源文件(会经过webpack的打包处理)- components:
- plugins:用于存放element.js
- router:用于存放路由文件
- views:用于存放视图文件和Vue组件
- APP.vue:项目入口文件
- main-dev.js:开发模式项目入口文件
- main-prodjs:上线打包,发布模式项目入口文件
- editorconfig:代码规范配置文件
- .gitignore:git忽略配置文件
- babel.config.js:babel配置文件
- package.json:项目配置文件
- package-lock.json:项目包管控文件
- README.md:说明文件
- vue.config.js:配置文件
登录组件
- 设计思路:
一个表单内包含一个显示用户名的文本输入框,和一个密码输入框,有登录按钮,和表单重置按钮。 - 功能需求分析:
两个输入框,应当要有表单规则,用户名和密码必须要符合自定的验证规则,密码输出框需要隐藏密码,登录按钮被点击后通过路由跳转到home主页面,重置按钮被点击后要对表单清空重置。 - 具体实现;
- 创建Login组件
- 导入路由
- 创建Login组件
import Login from "../views/Login";
{
path: '/login',
name: 'Login',
component: Login
},
template
<el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form">
<!-- username-->
<el-form-item prop="username">
<!-- prop必须在<el-form-item>中 prop指定不同的验证规则 验证规则中的username-->
<el-input v-model="loginForm.username" placeholder="用户名"
prefix-icon="el-icon-user-solid"></el-input>
</el-form-item>
<!-- password-->
<el-form-item prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码"
prefix-icon="el-icon-s-goods" type="password" show-password></el-input>
</el-form-item>
<!-- 按钮区-->
<el-form-item class="btns">
<el-button type="primary" :plain="true" @click="login">登录</el-button>
<el-button type="info" @click="resetloginForm">重置</el-button>
</el-form-item>
</el-form>
:model=“loginForm”:是双向绑定的数据对象,loginForm包含username,password两个属性。
在data()中定义loginForm对象,loginForm对象使用来接收请求到的用户名和密码的
//(1)登录表单的数据绑定对象
loginForm:{
username:'admin',
password:'123456'
},
** ref=“loginFormRef”**:是表单的引用对象,一般这个引用对象在表单中有两个用法
用法一
需要重置表单的时候可以用到loginFormRef引用对象。
// resetFields()对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
this.$refs.loginFormRef.resetFields();
// this.$refs这个是<el-form ref="loginFormRef" 中自带的属性,
// this.$refs获取表单的引用对象
用法二
提交表单的时候,需要表单预验证需要用到loginFormRef引用对象。
this.$refs.loginFormRef.validate(async valid=>{
// valid回调函数里面的Boolean值,实验的结果
if(!valid)// valid为false不发起请求,valid为true时才能发出请求
return ;
**:rules=“loginFormRules”**表示给表单绑定一个规则。
在data()中对表单的规则作出具体规定
// (2) 表单的验证规则
loginFormRules:{
// 验证用户名是否合法,通过<el-form-item prop="username"> prop来绑定
username:[
{ required: true, message: '请输入用户名称', trigger: 'blur' },
{ min: 3, max: 16, message: '用户名长度在 3 到 16 个字符', trigger: 'blur' }
],
// 验证密码是否合法
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码长度在 6 到 16 个字符', trigger: 'blur' }
]
}
prop=“username” prop属性是给定的文本输入框绑定值
**v-model=“loginForm.username” ** 这里是给用户名输入文本框绑定loginForm对象的username用户名。密码同上。
@click=“login” 登录按钮绑定点击事件。
在methods方法函数中定义login()函数,login点击事件的作用是 提交表单,跳转路由,提交表单就要表单预验证,验证通过那么向后端发起请求,const {data:res}=awai tthis.$http.post(‘Login’,this.loginForm)这里用到个Axios异步通信中的get方法,get方法需要的参数有请求的地址url和传递的参数this.loginForm。
值得注意的是get返回的是一个promise对象,而promise对象不是我们想要得到的数据,我们需用同async 和 await 来简化这次请求操作
请求的结果处理:
如果后端返回的状态码status !== 200 那么说明这次请求失败,提示用户错误信息,status== 200说明发起请求成功,也需要提示用户登录成功。
将登录成功之后token,保存到客户端的sessionStorage中
项目中除了登录页面之外的其他API接口,必须在登录之后才能访问,token只应在当前网站打开期间生效,所以将token 保存在sessionStorage 中
路由跳转
登录成功后需要通过路由来跳转到home页面
login(){
// 对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,
// 并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个 promise
this.$refs.loginFormRef.validate(async valid=>{
// valid回调函数里面的Boolean值,实验的结果
if(!valid)// valid为false不发起请求,valid为true时才能发出请求
return ;
//前后端交互
// 结构放回一个promise 可以用await 和async
const {data:res}= await this.$http.post('Login',this.loginForm)
if(res.meta.status !==200)
return this.$message.error(res.meta.msg);
this.$message.success(res.meta.msg)
// 1.将登录成功之后token,保存到客户端的sessionStorage中
// 1.1 项目中除了登录页面之外的其他API接口,必须在登录之后才能访问
// 1.2 token只应在当前网站打开期间生效,所以将token 保存在sessionStorage 中
window.sessionStorage.setItem("token",res.data.token);
// 2.通过编程式导航跳转到后台主页,路由地址是/home
await this.$router.push("/home");
@click=“resetloginForm” 重置按钮绑定点击事件
清除内容表单。调用resetFields函数就行,这个是一个固定的方法。
// 点击重置按钮,重置登录表单
resetloginForm(){
// console.log(this)
// resetFields()对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
this.$refs.loginFormRef.resetFields();
// this.$refs这个是<el-form ref="loginFormRef" 中自带的属性,
// this.$refs获取表单的引用对象
},
script
<script>
export default {
name: "Login",
data() {
return {
//(1)登录表单的数据绑定对象
loginForm:{
username:'admin',
password:'123456'
},
// (2) 表单的验证规则
loginFormRules:{
// 验证用户名是否合法,通过<el-form-item prop="username"> prop来绑定
username:[
{ required: true, message: '请输入用户名称', trigger: 'blur' },
{ min: 3, max: 16, message: '用户名长度在 3 到 16 个字符', trigger: 'blur' }
],
// 验证密码是否合法
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码长度在 6 到 16 个字符', trigger: 'blur' }
]
}
}
},
methods:{
// 点击重置按钮,重置登录表单
resetloginForm(){
// console.log(this)
// resetFields()对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
this.$refs.loginFormRef.resetFields();
// this.$refs这个是<el-form ref="loginFormRef" 中自带的属性,
// this.$refs获取表单的引用对象
},
// 登录预验证
login(){
// 对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,
// 并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个 promise
this.$refs.loginFormRef.validate(async valid=>{
// valid回调函数里面的Boolean值,实验的结果
if(!valid)// valid为false不发起请求,valid为true时才能发出请求
return ;
//前后端交互
// 结构放回一个promise 可以用await 和async
const {data:res}= await this.$http.post('Login',this.loginForm)
// console.log(result);
if(res.meta.status !==200)
return this.$message.error(res.meta.msg);
this.$message.success(res.meta.msg)
// if(result.data.meta.status !==200) return console.log('登录成功');
// else return console.log('登录失败');
// console.log(result.data);
// 1.将登录成功之后token,保存到客户端的sessionStorage中
// 1.1 项目中除了登录页面之外的其他API接口,必须在登录之后才能访问
// 1.2 token只应在当前网站打开期间生效,所以将token 保存在sessionStorage 中
window.sessionStorage.setItem("token",res.data.token);
// 2.通过编程式导航跳转到后台主页,路由地址是/home
// await this.$router.push("/home/" + this.loginForm.username);
// this.loginForm.username传递的参数
await this.$router.push("/home");
});
}
}
}
</script>
CSS
<style scoped>
.login_container{
background-image: url("../assets/loginback.jpg");
height: 100%;
}
.login_box{
/*display: flex;*/
/*justify-content: center;*/
/*align-items: center;*/
width: 450px;
height: 300px;
background-image: url("../assets/1.jpg");
background-size:100% 100% ;
border-radius: 8px;
position: absolute;
top:50%;
left: 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 #fff;*/
/* position: absolute;*/
/* left: 50%;*/
/* transform: translate(-50%,-50%);*/
/* background-color: #fff;*/
/*}*/
/*.avatar_box 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: center;
}
</style>
home组件
-
设计思路:
home组件是整个项目最重要的部分,其他的功能模块都通过路由的渲染home组件上,home组件主要的实现的是对整个项目布局的设计,这里采用的是Element-UI中的Header、Aside、Main、Footer布局。 -
功能需求分析:
Header可以选择放置项目的logo,用户头像等,Aside主要是侧边的绘制,Main主要是显示数据渲染的结果视图。 -
具体实现
** 导入路由**
import Home from "@/views/Home";
{
path:'/home',
props:true, //获取name
name:'Home',
}
Header
头部区域以栅格布局的方式平均分为了三格,第三格设置了退出按钮,用户点击退出按钮后就触发logout事件,路由跳转到Login页面。
<el-header>
<el-row>
<el-col :span="4"><div class="grid-content bg-purple vcenter">西瓜心红</div></el-col>
<el-col :span="12"><div class="grid-content bg-purple-light vcenter">我爱大西瓜</div></el-col>
<el-col :span="8">
<div class="grid-content bg-purple">
<el-menu
class="el-menu-demo"
mode="horizontal"
text-color="#545c64"
active-text-color="#ffd04b">
<el-submenu index="1" style="position: absolute;right:100px;" >
<template slot="title"><i class="el-icon-user-solid"></i></template>
<el-menu-item index="1-1">个人中心</el-menu-item>
<el-menu-item index="1-2">安全中心</el-menu-item>
<el-menu-item index="1-3">设置</el-menu-item>
<el-menu-item index="1-4" @click="logout">退出</el-menu-item>
</el-submenu>
</el-menu>
</div>
</el-col>
</el-row>
</el-header>
login退出事件:
用户点击退出按钮后就触发logout事件,路由跳转到Login页面。
//退出功能
logout(){
window.sessionStorage.clear();
this.$router.push('/login')
},
Aside–HTML
侧边导航栏。
<!-- 侧边栏-->
<el-aside :width="isCollpase?'64px':'300px'">
<!-- 折叠与展开-->
<div class="toggle_button" @click="toggleCollpase">|||</div>
<el-menu
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
:unique-opened="true"
:collapse="isCollpase"
:collapse-transition="false"
active-text-color="rgb(254,205,67)"
router>
<!-- unique-opened 是否只保持一个子菜单的展开 boolean-->
<el-submenu :index="item.id+''"
v-for="item in menuList"
:key="item.id">
<template slot="title">
<i :class="iconObject[item.id]"></i>
<span>{{ item.authName }}</span>
</template>
<!-- :index="'/'+subItem.path"绑定路由 -->
<el-menu-item :index="'/'+subItem.path"
v-for="subItem in item.children"
:key="subItem.id">
<template>
<i :class="iconObject_children[subItem.id]"></i>
<span>{{ subItem.authName}}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
toggleCollpase
折叠事件,“|||”被点击后会触发toggleCollpase这个事件,isCollpase是一个Boolean量默认为false, :width="isCollpase?‘64px’:‘300px’ isCollpase=true说明触发了toggleCollpase,导航栏需要折叠起来,折叠起来的导航栏的宽度为64px,isCollpase=false说明导航栏展开,宽度为300px。
// 折叠
isCollpase:false,
// 折叠功能
toggleCollpase(){
this.isCollpase = !this.isCollpase
},
menu参数说明
- unique-opened 是否只保持一个子菜单的展开 boolean
- collapse是否水平折叠收起菜单(仅在 mode 为 vertical 时可用)
- collapse-transition 是否开启折叠动画
- router,需要给menu绑定路由,不然不能通过路由来访问menu
一级
循环接收到的数组,然后把该数组的元素名称渲染到页面上
- index参数使用用来绑定数据的,:index=“item.id+’’”,这里是绑定了menuList数组中元素的id,(这里的menuList数组是发起请求后端传过来的数组)通过id来绑定到数组元素。
- 遍历数组:index=“item.id+’’” v-for=“item in menuList” :key=“item.id”
- 各个元素的图标
- {{ item.authName }} 各个数组元素的名称
二级菜单
循环 item.children
<!-- :index="'/'+subItem.path"绑定路由 -->
<el-menu-item :index="'/'+subItem.path"
v-for="subItem in item.children"
:key="subItem.id">
<template>
<i :class="iconObject_children[subItem.id]"></i>
<span>{{ subItem.authName}}</span>
</template>
</el-menu-item>
发起请求
// 获取所有导航栏的菜单
async getMenuList(){
const {data:res} = await this.$http.get('menus')
//get是返回的是promise数据,为了简化promise操作 ,需要用async和await来简化
if(res.meta.status !== 200)
return this.$message.error(res.meta.msg)
this.menuList = res.data
// console.log(res)
},
Aside–script
<script>
export default {
name: "Home",
//props:['name'],home页面接受到离着name数据
data(){
return {
// 导航栏菜单数据
menuList:[],
// 处理导航栏一级菜单图标
iconObject:{
'125':'el-icon-user-solid',
'103':'el-icon-s-cooperation',
'101':'el-icon-shopping-bag-1',
'102':'el-icon-tickets',
'145':'el-icon-s-data'
},
// 导航栏二级菜单图标处理:
iconObject_children:{
'110':'el-icon-camera',
'111':'el-icon-picture-outline-round',
'104':'el-icon-s-promotion',
'115':'el-icon-help',
'121':'el-icon-s-platform',
'112':'el-icon-s-home',
'107':'el-icon-s-opportunity',
'146':'el-icon-s-grid',
},
// 折叠
isCollpase: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')
//get是返回的是promise数据,为了简化promise操作 ,需要用async和await来简化
if(res.meta.status !== 200) return this.$message.error(res.meta.msg)
this.menuList = res.data
// console.log(res)
},
// 折叠功能
toggleCollpase(){
this.isCollpase = !this.isCollpase
},
// 保存导航栏链接的激活状态
// saveNavState(activePath){
// // setItem向sessionStorage添加activePath
// window.sessionStorage.setItem('activePath',activePath)
// this.activePath = activePath
// }
}
}
Aside–CSS
<style scoped>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #545c64;
color: #333;
text-align: center;
line-height: 10px;
}
.el-aside .el-menu{
border-right: none;
}
.el-main {
background-color: #E9EEF3;
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
.container{
height: 100%;
}
Welcome组件
- 设计思路:
登录登录成功后就会直接跳转到这个欢迎页面,该页面不需要做什么内容,只要显示欢迎用户即可。 - 功能需求分析:
显示欢迎用户界面。 - 具体实现:
创建组件
Welcome组件内容
这里我是插入一张图片
<div class="welcome">
<img src="../../assets/welcome.png" alt="">
</div>
导入路由
import Welcome from "../views/Welcome/Welcome";
//路由重定向
redirect:'/welcome',
component: Home,
用户列表
- 设计思路;
一个头部面包屑,搜索文本框,添加用户按钮,一个表格显示用户信息,可编辑用户信息,分配角色和删除用户信息。 - 功能需求分析:
面包屑点击某个选项可跳转到相应的页面,文本输入框可以通过用户名来查询现有的用户的信息,添加用户,填写用户的基本信息后提交到服务器,刷新表格,编辑就是对用户信息进行修改,删除将对应的用户从表格中删除。 - 具体实现:
导入路由
值得注意的是User组件应该添加到home的子路由中
import User from "../views/user/User";
children:[
{
path:'/welcome',
name:'Welcome',
component:Welcome
},
{
path:'/users',
name:'User',
component: User
},
User–template
面包屑
<!-- 面包屑-->
<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-input 属性及其参数梳理
-
v-model="queryInfo.query"绑定搜索的值
- clearable 搜索内容可点击X号删除
- @clear=“getUserList” 搜索完成后清除输入文本框内容
- @click=“addDialogVisible = true” 添加用户按钮的点击事件,被点击后显示添加用户对话框
-
- el-input 属性及其参数梳理
<el-row :gutter="10">
<el-col :span="8">
<el-input placeholder="请输入内容"
v-model="queryInfo.query"
clearable
@clear="getUserList">
<!-- clearable带X号,可清空按钮,@clear="getUserList",点击后触发clear清空事件,-->
<!-- 恢复原来的用户列表数据-->
<el-button slot="append" icon="el-icon-search"
@click="getUserList"></el-button>
<!-- @click="getUserList"查询的是用户列表中的数据,所以绑定获取用户列表数据getUserList()就可以了;-->
</el-input>
</el-col>
<el-col :span="4" style="width: 0;">
<el-button type="success" @click="addDialogVisible = true">添加用户</el-button>
</el-col>
</el-row>
- 用户列表区
- el-table表格参数及其属性梳理
- :data=“userlist” 绑定表格数据数组
- border 边框
- stripe 隔行显色
- el-table-column 参数及其属性梳理
- label=“姓名” 表头
- prop=“username” 绑定的具体数据
- 定义域插槽的用法
- 定义
template slot-scope=“scope” /template - {{scope.row}}可以拿到状态的数据 scope.row这行所对应的数据
- v-model="scope.row.mg_state"获取并绑定了用户状态
- scope.row.id 获取到了这一行的id值
- 定义
- el-table表格参数及其属性梳理
<!-- 用户列表区-->
<el-table :data="userlist" border stripe>
<!-- 索引列-->
<el-table-column label="#" 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="状态">
<!-- 作用域插槽slot-scope="scope"-->
<template slot-scope="scope">
<!-- {{scope.row}}可以拿到状态的数据-->
<!-- scope.row这行所对应的数据-->
<!-- v-model="scope.row.mg_state"获取到了后端的状态-->
<el-switch
v-model="scope.row.mg_state"
active-color="#13ce66"
inactive-color="#ff4949"
@change="userStateChanged(scope.row)">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-row>
<!-- 修改按钮-->
<el-tooltip effect="dark" content="修改" placement="top-start" :enterable="false">
<el-button type="primary"
size="min"
icon="el-icon-edit"
circle
@click="showEditDialog(scope.row.id)"></el-button>
</el-tooltip>
<el-tooltip effect="dark" content="删除" placement="top-end" :enterable="false">
<el-button type="danger" size="min" icon="el-icon-delete" circle
@click="removeUserById(scope.row.id)"></el-button>
</el-tooltip>
<el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">
<el-button type="success" size="min" icon="el-icon-s-tools" circle @click="setRole(scope.row)"></el-button>
</el-tooltip>
</el-row>
</template>
</el-table-column>
</el-table>
- 分页区
- el-pagination 参数及其属性梳理
- @size-change=“handleSizeChange” pageSize 改变时会触发
- @current-change=“handleCurrentChange” currentPage 改变时会触发
- :current-page=“queryInfo.pagenum” 当前页数
- :page-sizes="[2,5,10,15]" 页面显示的信息条数
- :page-size=“queryInfo.pagesize” 每页显示个数选择器的选项设置
- :total=“total” 总条数
- el-pagination 参数及其属性梳理
<!-- 分页区域-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pagenum"
:page-sizes="[2,5,10,15]"
:page-size="queryInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
卡片区结束
添加用户对话框
- el-dialog对话框参数及其属性梳理
- :visible.sync=“addDialogVisible” 决定对话框是否显示
- @close=“addDialogClose” 对话框关闭之后,需要清空对话框中表单的信息。
- el-form 表单参数及其属性梳理
-
:model="addForm 表单绑定的数据
- :rules=“addFormRules” 表单的规则对象
- ref=“addFormRef” l表单的引用对象,
两个地方用到了表单引用对象,表单重置和提交数据预验证
-
- el-button 参数及其属性梳理
- " @click=“addUser” 添加用户确定按钮,绑定点击事件,点击事件触发后需要提交用户信息,然后再显示新添加的用户信息显示到表格中。
<!-- 添加用户对话框-->
<el-dialog
title="添加用户"
:visible.sync="addDialogVisible"
width="50%"
@close="addDialogClose">
<!-- 内容主体区-->
<!-- ref="addFromRef"是表单的引用对象 表单的重置需要用到 -->
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px" class="demo-ruleForm">
<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="editDialogClose">
<!-- // 展示编辑修改对话框-->
<!-- 修改用户的主体部分-->
<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="username">
<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="editUser">确 定</el-button>
</span>
</el-dialog>
分配角色对话框
- el-select 参数及其属性梳理
- v-model=“selectRoleId” 绑定选择的值
- v-for=“item in rolesList” 循环用户数组
- :label="item.roleName"选择分配角色的名称
- :value=“item.id” 选项的值绑定给选择的选项的id
<!-- 分配角色对话框-->
<el-dialog
title="分配角色"
:visible.sync="setRoleDialogVisible"
width="50%"
@close="setRoleDialogClose"
class="dailog_role">
<div>
<p class="user">当前的用户:{{userInfo.username}}</p>
<p class="user">当前的角色:{{userInfo.role_name}}</p>
<P class="user">分配角色:
<el-select v-model="selectRoleId" 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>
User页面整体效果
User–script
数据分析
- 邮箱验证和手机验证的方法
// 邮箱验证
var checkEmail = (rule,value,callback)=>{
// 验证邮箱的正则表达式
const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/
if(regEmail.test(value)){
// 合法的邮箱
callback()
}
callback(new Error('请输入合法的邮箱'))
}
// 手机号验证规则
var checkMobile = (rule,value,callback)=>{
// 手机正则表达式
const regMobile = /^(0|86|17951)?(13[0-9]|15[0123456789]|17[678]|18[0-9]|14[57])[0-9]{8}$/
if(regMobile.test(value)){
// 合法手机号
callback()
}
callback(new Error('请输入合法的手机号'))
}
- queryInfo 获取用户列表的参数
- userlist:[],用户裂变数据
- total: 0, 用户列表的总条数
- addForm 添加用户的表单数据
- addFormRules 添加表单的验证规则对象
- editForm 编辑用户的表单数据
- editFormRules
- userInfo 需要被分配角色的用户信息
- rolesList 所有角色的数据列表
- selectRoleId 已选中的角色Id值
函数分析
- getUserList() 获取用户列表数据,发起请求
- this.userlist = res.data.users 获取用户信息,保存在userlist 数组中
- this.total = res.data.total 获取到总条数,保存到total中
- handleSizeChange(newSize) 分页事件 监听pageSize改变的事件
- his.queryInfo.pagesize = newSize;
- this.getUserList(); 重新获取数据,像后端发起请求
- handleCurrentChange(newSize) 监听页码值改变的事件
- his.queryInfo.pagenum= newSize;
- this.getUserList(); 重新获取数据,像后端发起请求
- userStateChanged(userInfo) 监听switch按钮状态的变化
users/${userInfo.id}/state/${userInfo.mg_state}
- 特别注意的是:ulr中要是带有参数 这个时候不能用单引号,应该使用反引号来写路径,反引号:在英文输入法的情况下,连续按两下 Esc 下面的键 ``
- addUser( )点击确定按钮添加用户
- this.$refs.addFormRef.validate 表单预验证
- this.$http.post(‘users’,this.addForm) 发起请求
- removeUserById(id) 删除事件
- this.$confirm 提示 弹框
- this.$http.delete(‘users/’+id)发起请求
- setRole(userInfo) 发起请求获取到角色信息
-saveRoleInfo()分配角色,添加到列表中
<script>
export default {
name: "User",
props:['name'],
data(){
// 自定义验证规则(手机号和邮箱)
// 邮箱验证
var checkEmail = (rule,value,callback)=>{
// 验证邮箱的正则表达式
const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/
if(regEmail.test(value)){
// 合法的邮箱
callback()
}
callback(new Error('请输入合法的邮箱'))
}
// 手机号验证规则
var checkMobile = (rule,value,callback)=>{
// 手机正则表达式
const regMobile = /^(0|86|17951)?(13[0-9]|15[0123456789]|17[678]|18[0-9]|14[57])[0-9]{8}$/
if(regMobile.test(value)){
// 合法手机号
callback()
}
callback(new Error('请输入合法的手机号'))
}
return{
// 以下是接受后端数据用的数据,
// 获取用户列表的参数
queryInfo:{
//查询参数
query: '',
// 当前页码n
pagenum: 1,
// 当前每页显示条数
pagesize:5
},
//用户列表数据
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:[
{require:true,message:'请输入手机号',trigger:'blur'},
{validator:checkMobile,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'}
// 自定义校验规则的使用{validator:checkMobile,trigger: 'blur'}
]
},
// 分配角色
setRoleDialogVisible:false,
//需要被分配角色的用户信息
userInfo: {},
//所有角色的数据列表
rolesList:[],
// 已选中的角色Id值
selectRoleId:''
}
},
created() {
this.getUserList()
},
methods: {
// 获取用户列表数据
async getUserList() {
//get参数,params来接收传过来的数据
const {data: res} = await this.$http.get('users',
{params: this.queryInfo})
if (res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
//获取用户信息
this.userlist = res.data.users
//获取总条数
this.total = res.data.total
},
// 分页事件
// 监听pageSize改变的事件
handleSizeChange(newSize) {
// console.log(newSize)
this.queryInfo.pagesize = newSize;
//重新获取数据,像后端发起请求
this.getUserList();
},
//监听页码值改变的事件
handleCurrentChange(newSize) {
// console.log(newNum);
this.queryInfo.pagenum = newSize;
//重新获取数据
this.getUserList();
},
// 监听switch按钮状态的变化
async userStateChanged(userInfo){
// console.log(userInfo)
// 调用API接口保存状态量
// 特别注意的是:ulr中要是带有参数 这个时候不能用单引号,应该使用反引号来写路径,反引号:在英文输入法的情况下,连续按两下 Esc 下面的键 ``
const {data:res} =await this.$http.put(
`users/${userInfo.id}/state/${userInfo.mg_state}`
)
if(res.meta.status !== 200){
//更新用户状态失败,并没有修改数据库中的状态,而是改变了页面的变化,这个时候需要把用户状态还原
userInfo.mg_state = !userInfo.mg_state
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
},
// 监听添加用户对话框的关闭事件 添加用户对话框表单重置
addDialogClose(){
// 通过引用ref调用resetFields()
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(res.meta.msg)
}
// 添加用户成功
this.$message.success(res.meta.msg)
// 添加成功之后, 隐藏添加用户的对话框
this.addDialogVisible = false
// 刷新用户列表 重新获取用户列表数据
await this.getUserList()
})
},
//修改用户信息
//修改按钮点击事件 显示与隐藏修改对话框
//点击修改按钮时,显示对话框并且对话框内要有用户的基本信息
//通过id来查询用户的信息
async showEditDialog(id){
const {data:res}= await this.$http.get('users/'+id)
if(res.meta.status !==200){
return this.$message.error(res.meta.msg)
}
this.editForm = res.data
this.editDialogVisible =true
},
//修改用户信息对话框的,表单重置
editDialogClose(){
// 通过引用ref调用resetFields()
this.$refs.editFormRef.resetFields()
},
//修改用户信息
editUser(){
//修改表单预验证
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(res.meta.msg)
}
// 添加成功之后, 隐藏添加用户的对话框
this.editDialogVisible = false
// 刷新用户列表 重新获取用户列表数据
await this.getUserList()
this.$message.success(res.meta.msg)
})
},
// 通过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('取消删除')
}
// console.log('确认删除')
const {data:res } = await this.$http.delete('users/'+id)
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
// 删除成功
this.$message.success(res.meta.msg)
await 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(res.meta.msg)
}
this.rolesList = res.data
this.setRoleDialogVisible = true
},
// 点击分配角色 确定按钮触发的事件 分配角色
async saveRoleInfo(){
if(!this.selectRoleId){
return this.$message.error('请选择要分配的角色')
}
// 选择新角色,发起请求
const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{
rid:this.selectRoleId
})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
await this.getUserList()
this.setRoleDialogVisible = false
},
//监听分配对话框的关闭事件,关闭对话框后,选择的角色清空,用户也清空
setRoleDialogClose(){
this.selectRoleId = '',
this.rolesList = []
}
}
}
</script>
User–CSS
<style scoped>
.user{
line-height: 100%;
margin-top: 20px;
display: flex;
justify-content: left;
}
.dailog_role{
line-height: 100%;
padding: 0;
}
权限列表
面包屑
<!-- 面包屑-->
<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>
卡片视图区
主要功能是显示用户权限
- 权限等级这列用到了作用域插槽
v-if=“scope.row.level ==0” 判断权限的等级,level 为0 则是一级,为1是二级,为2是三级
<el-tag type="success" v-if="scope.row.level ==0">一级</el-tag>
<el-tag type="info" v-else-if="scope.row.level ==1">二级</el-tag>
<el-card>
<!-- 权限表单区域-->
<el-table
:data="rightsList"
border
stripe>
<el-table-column
label="#"
type="index">
</el-table-column>
<el-table-column
prop="authName"
label="权限名称">
</el-table-column>
<el-table-column
prop="path"
label="路径">
</el-table-column>
<el-table-column
label="权限等级">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.level ==0">一级</el-tag>
<el-tag type="info" 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>
获取到的权限数据列表存放在rightsList数组中
data(){
return{
rightsList:[],
}
},
getRightsList函数是获取权限数据,发起get请求,获取到的数据保存在rightsList 数组中,然后就可以通过数组将数据渲染到页面了。
methods:{
async getRightsList(){
// rights/:type
const {data:res} = await this.$http.get('rights/list')
if(res.meta.status !==200){
return this.$message.error(res.meta.msg)
}
this.rightsList = res.data
// this.$message.success(res.meta.msg)
}
}
角色列表
面包屑
<!-- 面包屑-->
<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-row>
<el-col>
<el-button type="success" @click="addRoleDialogVisible = true">添加角色</el-button>
</el-col>
</el-row>
- 添加角色对话框
<el-dialog
title="添加角色提示"
:visible.sync="addRoleDialogVisible"
width="50%"
@close="addDialogClose">
<el-form :model="addRoleForm"
:rules="addRoleFormRules"
ref="addRoleFormRef"
label-width="100px">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="addRoleForm.roleName"></el-input>
</el-form-item>
<el-form-item label="角色描述" prop="roleDesc">
<el-input v-model="addRoleForm.roleDesc"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addRoleDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addRoles">确 定</el-button>
</span>
</el-dialog>
- addRoleDialogVisible是一个Boolean值,用来确定对话框是否显示
addRoleDialogVisible : false,
- @close="addDialogClose"关闭对话框触发的事件,将添加对话框的内容清空,确保下次打开的对话框是空白的。
//关闭对话框后,重置清空对话框
addDialogClose(){
this.$refs.addRoleFormRef.resetFields()
},
- :model=“addRoleForm” 绑定表单数据的对象
addRoleForm:{},
- :rules=“addRoleFormRules” 表单的验证规则
addRoleFormRules:{
roleName: [
{ required: true, message: ‘请输入角色名称’, trigger: ‘blur’ },
{min:2 , max:10 , message: ‘角色名称的长度在3~10个字符之间’,trigger: ‘blur’}
],
roleDesc:[
{required:true,message:‘请输入角色描述’,trigger:‘blur’},
{min:2, max:10 , message: ‘角色描述的长度在3~10个字符之间’,trigger: ‘blur’}
]
},
- ref=“addRoleFormRef” 表单的引用对象,两次用到,预验证表单和清空表单
//表单预验证
this.$refs.addRoleFormRef.validate(async valid=> {
if(!valid){
return
}
//关闭对话框后,重置清空对话框
addDialogClose(){
this.$refs.addRoleFormRef.resetFields()
},
- prop=“roleName” 绑定数据具体属性
- 效果展示
展开列
<!-- 展开列-->
<el-table-column
type="expand">
<template slot-scope="scope">
<!-- {{scope.row}}-->
<el-row v-for="(item1 , index1) in scope.row.children"
:key="item1.id"
:class="['bdbottom',index1===0?'bdtop':'','vcenter']">
<!-- 一级权限-->
<el-col :span="5">
<!-- {{item1.authName}}-->
<el-tag closable
@close="removeRightsById(scope.row,item1.id)">{{item1.authName}}</el-tag>
<i class="el-icon-caret-right"></i>
</el-col>
<!-- 渲染二级和三级权限-->
<el-col :span="19">
<!-- 通过for循环嵌套 渲染二级权限-->
<el-row v-for="(item2,index2) in item1.children"
:key="item2.id"
:class="['bdbottom',index2===0?'bdtop':'','vcenter']">
<!-- 二级权限-->
<el-col :span="6">
<el-tag type="success"
closable
@close="removeRightsById(scope.row,item2.id)">{{item2.authName}}</el-tag>
<i class="el-icon-caret-right"></i>
</el-col>
<!-- 三级权限-->
<el-col :span="18">
<el-tag type="danger"
v-for="(item3 , index3) in item2.children"
:key="item3.id"
closable
@close="removeRightsById(scope.row,item3.id)">
{{item3.authName}}
</el-tag>
</el-col>
</el-row>
</el-col>
</el-row>
</template>
</el-table-column>
- v-for="(item1 , index1) in scope.row.children" 循环编列该行的children,因为children内包含了角色的权限,需要渲染到页面上。
- 一级权限
- closable表示可删除
- @close="removeRightsById(scope.row,item1.id)"通过id来删除权限
注意这里用到了MessageBox弹框,需要在main.js添加
import { MessageBox } from ‘element-ui’
<el-col :span="5">
<el-tag closable
@close="removeRightsById(scope.row,item1.id)">{{item1.authName}}</el-tag>
<i class="el-icon-caret-right"></i>
</el-col>
removeRightsById(scope.row,item1.id)函数,确定删除,发起delete请求,参数(roles/${role.id}/rights/${rightId}
),role.id表示该角色的ID,rightId表示该权限的ID
//根据Id删除对应的权限
async removeRightsById(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(res.meta.msg)
}
this.$message.success(res.meta.msg)
// await this.getRolesList()
role.children = res.data
},
- 二级和三级权限
- v-for="(item2,index2) in item1.children" 遍历一级权限的 children。就可以得到二级权限和三级权限
- 二级权限,同样调用removeRightsById(scope.row,item2.id)函数对权限进行删除操作
二级权限--> <el-col :span="6"> <el-tag type="success" closable @close="removeRightsById(scope.row,item2.id)">{{item2.authName}}</el-tag> <i class="el-icon-caret-right"></i> </el-col>
- 三级权限
- v-for="(item3 , index3) in item2.children"遍历二级权限的children得到三级权限
- @close="removeRightsById(scope.row,item3.id)"调用删除权限函数
三级权限--> <el-col :span="18"> <el-tag type="danger" v-for="(item3 , index3) in item2.children" :key="item3.id" closable @close="removeRightsById(scope.row,item3.id)"> {{item3.authName}} </el-tag> </el-col>
操作列
<el-table-column
label="操作">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-edit" @click="showEditDialog(scope.row.id)">编辑</el-button>
<el-button type="warning" icon="el-icon-share" @click="removeRolesDialog(scope.row.id)">删除</el-button>
<el-button type="danger" icon="el-icon-delete" @click="showSetRightDialog(scope.row)">分配权限</el-button>
</template>
</el-table-column>
- 编辑按钮
- 编辑对话框
- v-model="editRoleForm.roleName"和 v-model="editRoleForm.roleDesc"双向绑定要修改的数据
<el-dialog title="编辑角色" :visible.sync="editDialogVisible" width="50%" @close="editDialogClose"> <el-form :model="editRoleForm" :rules="editRoleFormRules" ref="editRoleFormRef" label-width="100px"> <el-form-item label="角色名称" prop="roleName"> <el-input v-model="editRoleForm.roleName" disabled></el-input> </el-form-item> <el-form-item label="角色描述" prop="roleDesc"> <el-input v-model="editRoleForm.roleDesc"></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="editRoles">确 定</el-button> </span> </el-dialog>
- @click="showEditDialog(scope.row.id)"编辑按钮的点击事件,显示编辑对话框内容,传递参数scope.row.id该行的ID,通过get请求来获取该行的数据,获取到的数据保存到editRoleForm 对象中,方便渲染到表单上。
// 编辑角色按钮 async showEditDialog(id){ //编辑角色请求 const {data:res} = await this.$http.get('roles/'+id) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } //编辑成功 this.editRoleForm = res.data this.editDialogVisible = true // this.$message.success(res.meta.msg) },
- @click="editRoles"点击编辑对话框的确定按钮触发的事件,将修改好的数据提交到数据库中,在更新用户列表内容。
// 编辑对话框确定按钮,点击提交表单 editRoles(){ //表单预验证 this.$refs.editRoleFormRef.validate( async valid =>{ if(!valid){ return } //编辑,发起网络请求 const {data:res} = await this.$http.put('roles/'+this.editRoleForm.roleId,{roleName:this.editRoleForm.roleName,roleDesc:this.editRoleForm.roleDesc}) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } //请求成功 this.editDialogVisible = false await this.getRolesList() this.$message.success(res.meta.msg) }) },
- 删除按钮
- @click="removeRolesDialog(scope.row.id)"绑定点击事件。删除按钮被点击后,该用户被删除。
async removeRolesDialog(id){ const rolesResult = await this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).catch(err => err) // 如果用户确认删除,则返回值为字符串confirm // 如果用户点击取消,则返回值为cancel if(rolesResult !== 'confirm'){ return this.$message.info('取消删除操作') } // 删除请求 const {data : res} = await this.$http.delete('roles/'+id) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } await this.getRolesList() this.$message.success(res.meta.msg) },
- 分配权限
- 分配权限对话框
- 注意:
这里用到了Tree 树形控件 - :data=“rightList” 绑定数据
- show-checkbox 节点是否可被选择
- :props=“treeProps” 配置选项
// 树形控件的属性绑定对象
treeProps:{
label:‘authName’,
children:‘children’
},- node-key="id"每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
- default-expand-all 是否默认展开所有节点
- :default-checked-keys=“defaultKeys” 默认展开的节点的 key 的数组
//默认选中的节点的Id值的数组
defaultKeys:[], - 注意:
<el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="showSetRightDialogClose"> <el-tree :data="rightList" show-checkbox :props="treeProps" node-key="id" default-expand-all :default-checked-keys="defaultKeys" ref="treeRightRef"> </el-tree> <span slot="footer" class="dialog-footer"> <el-button @click="setRightDialogVisible = false">取 消</el-button> <el-button type="primary" @click="allotRight">确 定</el-button> </span> </el-dialog>
- showSetRightDialog(role)展示分配权限
this.roleId = role.id 将角色的ID保存起来,在分配权限的时候还要用到
// 展示分配权限 async showSetRightDialog(role){ //当前权限的id this.roleId = role.id // 请求获取所有权限数据 const {data:res} = await this.$http.get('rights/tree') if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } this.rightList = res.data //获取三级节点的id this.getLeaKeys(role,this.defaultKeys) this.setRightDialogVisible = true },
- @click="allotRight"确定给角色分配权限
// 点击确定按钮,通过Id来分配权限 async allotRight(){ //获取半选和全选下的id const keys = [ ...this.$refs.treeRightRef.getCheckedKeys(), ...this.$refs.treeRightRef.getHalfCheckedKeys() ] // console.log(keys) const keysStr = keys.join(',') // 发起请求 const {data:res} = await this.$http.post(`roles/${this.roleId}/rights`, {rids:keysStr}) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } this.$message.success(res.meta.msg) await this.getRolesList() this.setRightDialogVisible = false }
- 分配权限对话框
商品列表
面包屑
<!-- 面包屑-->
<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-row>
<!-- 搜索框-->
<el-col :span="6">
<el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="searchGoods">
<el-button slot="append" icon="el-icon-search" @click="searchGoods"></el-button>
</el-input>
</el-col>
<!-- 添加商品按钮 -->
<el-col :span="18">
<!-- 通过路由跳转到添加商品页面-->
<el-button type="success" class="btn" @click="goAddGoods">添加商品</el-button>
</el-col>
</el-row>
- v-model=“queryInfo.query”
// 获取商品列表的参数
queryInfo: {
query:’’,
pagenum:1,
pagesize:10
},
- searchGoods事件
//搜索框的按钮点击事件
searchGoods(){
this.getGoodsList()
},
- 添加按钮 @click=“goAddGoods”,跳转到add页面
// 添加商品事件
goAddGoods(){
// 通过路由跳转到添加商品页面
this.$router.push('/goods/add')
},
-
add组件
- 注册步骤
- :active=“activeIndex-0” 注册步骤索引,
<!-- 注册步骤区域--> <el-steps :space="200" :active="activeIndex-0" finish-status="success" class="line-height " 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>
- table标签页
- v-model=“activeIndex” 数据联动效果
- :before-leave=“beforeTabsLeave” 切换标签之前的钩子,若返回 false 或者返回 Promise 且被 reject,则阻止切换。
//处理阻止Tabs标签切换的函数
beforeTabsLeave(activeName,oldActiveName){
if(oldActiveName === ‘0’ && this.addForm.goods_cat.length !== 3){
this.$message.error(‘请选择三级商品分类’)
return false
}
// 可以跳转
},- @tab-click=“tabsClick” tab-click tab 被选中时触发
//被选中的标签 tab 实例 async tabsClick(){ // console.log(this.activeIndex) //选中的是动态参数面板 if(this.activeIndex === '1'){ // 获取商品参数 const {data:res} = await this.$http.get(`categories/${this.getCateId}/attributes`,{ params: { sel: 'many' } }) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } //遍历 res.data 中attr_vals,将res.data 中attr_vals 字符变为数组 res.data.forEach(item => { item.attr_vals=item.attr_vals.length===0?[]:item.attr_vals.split(' ') }) // 获取商品参数成功,并保存到manyCateList数组中 this.manyTableData = res.data }else if(this.activeIndex === '2'){ // 商品属性列表 const {data:res} = await this.$http.get(`categories/${this.getCateId}/attributes`,{ params:{sel:'only'} }) if(res.meta.status !== 200){ return this.$message.error(res.meta.msg) } this.onlyTableData = res.data console.log(this.onlyTableData) } },
<!-- table栏区域--> <el-form :model="addForm" :rules="addFormRule" ref="addFormRef" label-width="100px" label-position="top" class="from_items"> <el-tabs v-model="activeIndex" :tab-position="`left`" style="height: 100%;" :before-leave="beforeTabsLeave" @tab-click="tabsClick"> <!-- v-model="activeIndex" 数据联动效果--> ......................... </el-tabs> </ <el-form>
- 商品参数
- v-for="item in manyTableData"循环获取到的动态参数
<el-tab-pane label="商品参数"> <el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id" border> <el-checkbox-group v-model="item.attr_vals"> <el-checkbox :label="itemParams" v-for="(itemParams,index) in item.attr_vals" :key="index" border></el-checkbox> </el-checkbox-group> </el-form-item> </el-tab-pane>
- 商品图片
- action 必选参数,上传的地址
- :on-preview="handlePreview"点击文件列表中已上传的文件时的钩子
- :on-remove=“handleRemove” 删除图片时的钩子
- :on-success="handleSuccess"文件上传成功时的钩子
//处理图片预览操作
handlePreview(file){
// file将预览的图片信息
console.log(file)
// 获取图片的临时路径
this. previewPath= file.response.data.url
// 弹出对话框, 放大图片,
this.imageDialogVisible = true
},
// 处理图片删除操作
handleRemove(file){
// file将要被移除的图片信息
// 1.获取图片的临时路径
// console.log(file)
const filePath = file.response.data.tmp_path
// 从pics数组中,查找到这个图片对应的索引,findIndex这个函数可以查找到数组的索引
// x表示数组中的每一项
const i = this.addForm.pics.findIndex(x=>x.pic === filePath)
// 调用数组中的splice函数来删除该图片
this.addForm.pics.splice(i,1)
// console.log(this.addForm)
},
//图片上传成功之后会触发的事件,事件会返回图片的临时保存地址,我们需要接收这个地址
handleSuccess(response){
// 获取到一个图片的对象。
const picsInfo = {pic:response.data.tmp_path}
// 将获取到的图片地址,保存添加到pics数组中
this.addForm.pics.push(picsInfo)
},
<el-tab-pane label="商品照片"> <el-upload :action="actionURL" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :headers="headersImg" :on-success="handleSuccess"> <el-button size="small" type="primary">点击上传</el-button> </el-upload> </el-tab-pane> <!-- :on-success="handleSuccess"文件上传成功时的钩子-->
- add组件源码
- 注册步骤
<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"
show-icon
center
class="line-height">
</el-alert>
<!-- 注册步骤区域-->
<el-steps :space="200" :active="activeIndex-0" finish-status="success" class="line-height " align-center>
<!-- active是注册步骤激活项的索引-->
<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>
<!-- table栏区域-->
<el-form :model="addForm" :rules="addFormRule" ref="addFormRef" label-width="100px" label-position="top" class="from_items">
<el-tabs v-model="activeIndex" :tab-position="`left`" style="height: 100%;"
:before-leave="beforeTabsLeave" @tab-click="tabsClick">
<!-- v-model="activeIndex" 数据联动效果-->
<el-tab-pane label="基本信息">
<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_number">
<el-input v-model="addForm.goods_number" type="number"></el-input>
</el-form-item>
<el-form-item label="重量" prop="goods_weight" type="number">
<el-input v-model="addForm.goods_weight"></el-input>
</el-form-item>
<!-- 商品分类级联选择器-->
<el-form-item label="商品分类" prop="goods_cat">
<el-cascader
expand-trigger="hover"
v-model="addForm.goods_cat"
:options="cateList"
:props="cateParams"
@change="handleChange">
</el-cascader>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="商品参数">
<!-- 遍历参数-->
<el-form-item :label="item.attr_name" v-for="item in manyTableData" :key="item.attr_id" border>
<el-checkbox-group v-model="item.attr_vals">
<el-checkbox :label="itemParams" v-for="(itemParams,index) in item.attr_vals" :key="index" border></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="商品属性">
<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="商品照片">
<!-- :on-success="handleSuccess"文件上传成功时的钩子-->
<el-upload
:action="actionURL"
:on-preview="handlePreview"
:on-remove="handleRemove"
list-type="picture"
:headers="headersImg"
:on-success="handleSuccess">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-tab-pane>
<el-tab-pane label="商品内容">
<quill-editor v-model="addForm.goods_introduce">
</quill-editor>
<el-button type="primary" style="margin-top: 20px" @click="addGoods">添加商品</el-button>
</el-tab-pane>
</el-tabs>
</el-form>
</el-card>
<!--图片预览对话框-->
<el-dialog
title="图片预览"
:visible.sync="imageDialogVisible"
width="70%"
@close="imageHandleClose">
<img :src="previewPath" alt="" style="width: 100%;">
</el-dialog>
</div>
</template>
<script>
// <!--导入lodash-->
import _ from 'lodash'
export default {
name: "Add",
data(){
return{
// 注册步骤的的索引标识
activeIndex : '0',
//添加商品的表单数据对象
addForm:{
goods_name:'',
goods_price:0,
goods_number:0,
goods_weight:0,
// 级联选择框双向绑定到的数组
goods_cat:[],
//图片上传都保存到pics数组中
pics:[],
//商品内容
goods_introduce:'',
//商品的参数(数组),包含 `动态参数` 和 `静态属性`
attrs:[]
},
addFormRule:{
goods_name:[
{required:true,message:'请输入商品名',trigger:'blur'},
],
goods_price:[
{required:true,message:'请输入商品价格',trigger:'blur'},
],
goods_number:[
{required:true,message:'请输入商品数量',trigger:'blur'},
],
goods_weight:[
{required:true,message:'请输入商品重量',trigger:'blur'},
],
goods_cat:[
{required:true,message:'请选择商品分类',trigger:'blur'},
]
},
// 获取到的商品分类列表的数据数组
cateList:[],
// // 级联选择框的配置对象
cateParams:{
label:'cat_name',
value:'cat_id',
children:'children'
},
//获取到的商品的动态属性,保存在manyCateList数组中
manyTableData:[],
//获取到的商品的动态属性,保存在onlyTableData对象中
onlyTableData:[],
//上传图片的目标地址
actionURL:'http://127.0.0.1:8888/api/private/v1/upload',
//设置上传的请求头部headersImg
headersImg: {
Authorization:window.sessionStorage.getItem('token')
},
//图片预览
imageDialogVisible:false,
//获取到的图片的保存路径url
previewPath:'',
}
},
created() {
this.getCateList()
},
methods:{
//获取商品分类数据
async getCateList(){
const {data:res} = await this.$http.get('categories')
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.cateList = res.data;
},
// 级联选择框选中项变化会触发这个函数
handleChange(){
//只能选择三级分类
if(this.addForm.goods_cat.length !== 3){
this.addForm.goods_cat = []
}
},
//处理阻止Tabs标签切换的函数
beforeTabsLeave(activeName,oldActiveName){
if(oldActiveName === '0' && this.addForm.goods_cat.length !== 3){
this.$message.error('请选择三级商品分类')
return false
}
// 可以跳转
},
//被选中的标签 tab 实例
async tabsClick(){
// console.log(this.activeIndex)
//选中的是动态参数面板
if(this.activeIndex === '1'){
// 获取商品参数
const {data:res} = await this.$http.get(`categories/${this.getCateId}/attributes`,{
params: { sel: 'many' }
})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
//遍历 res.data 中attr_vals,将res.data 中attr_vals 字符变为数组
res.data.forEach(item => {
item.attr_vals=item.attr_vals.length===0?[]:item.attr_vals.split(' ')
})
// 获取商品参数成功,并保存到manyCateList数组中
this.manyTableData = res.data
}else if(this.activeIndex === '2'){
// 商品属性列表
const {data:res} = await this.$http.get(`categories/${this.getCateId}/attributes`,{
params:{sel:'only'}
})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.onlyTableData = res.data
console.log(this.onlyTableData)
}
},
//处理图片预览操作
handlePreview(file){
// file将预览的图片信息
console.log(file)
// 获取图片的临时路径
this. previewPath= file.response.data.url
// 弹出对话框, 放大图片,
this.imageDialogVisible = true
},
// 处理图片删除操作
handleRemove(file){
// file将要被移除的图片信息
// 1.获取图片的临时路径
// console.log(file)
const filePath = file.response.data.tmp_path
// 从pics数组中,查找到这个图片对应的索引,findIndex这个函数可以查找到数组的索引
// x表示数组中的每一项
const i = this.addForm.pics.findIndex(x=>x.pic === filePath)
// 调用数组中的splice函数来删除该图片
this.addForm.pics.splice(i,1)
// console.log(this.addForm)
},
//图片上传成功之后会触发的事件,事件会返回图片的临时保存地址,我们需要接收这个地址
handleSuccess(response){
// 获取到一个图片的对象。
const picsInfo = {pic:response.data.tmp_path}
// 将获取到的图片地址,保存添加到pics数组中
this.addForm.pics.push(picsInfo)
},
//关闭预览对话框
imageHandleClose(){
this.imageDialogVisible = false
},
//编辑好商品的所有属性和参数后,提交到服务器上
addGoods(){
//表单预验证
this.$refs.addFormRef.validate(async valid=>{
if (!valid){
return this.$message.error('请填写完整的商品参数')
}
//将goods_cat分割为字符串
//安装lodash插件中的cloneDeep方法用来深拷贝goods_cat
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)
})
//将addForm对象的attrs数组,赋值给form对象
form.attrs = this.addForm.attrs
//发起请求添加商品
const {data:res} = await this.$http.post('goods',form)
if(res.meta.status !== 201){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
//添加商品完成后,路由跳转到商品列表页面
await this.$router.push('/goods')
})
}
},
computed:{
//获取三级分类的索引函数
getCateId(){
if(this.addForm.goods_cat.length === 3 ){
//三级分类的索引
return this.addForm.goods_cat[2]
}
return null
}
}
}
</script>
<style scoped>
.from_items{
text-align: left;
line-height: 100%;
}
.el-checkbox{
margin:0 10px 0 0 !important; ;
}
</style>
- 商品列表对时间的处理
<el-table-column
label="创建时间"
prop="add_time"
width="200px">
<template slot-scope="scope">
<!-- 时间格式 通过时间过滤器来装换-->
{{scope.row.add_time | dataFormat}}
</template>
</el-table-column>
在main.js入口文件中添加时间过滤器
//格式化时间,时间过滤器
Vue.filter('dataFormat', function (originVal) {
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
// yyyy-mm-dd hh:mm:ss
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})
- 商品列表源码
<template xmlns:el-col="http://www.w3.org/1999/html">
<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="6">
<el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="searchGoods">
<el-button slot="append" icon="el-icon-search" @click="searchGoods"></el-button>
</el-input>
</el-col>
<!-- 添加商品按钮 -->
<el-col :span="18">
<!-- 通过路由跳转到添加商品页面-->
<el-button type="success" class="btn" @click="goAddGoods">添加商品</el-button>
</el-col>
</el-row>
<!-- 表格区-->
<el-table
:data="goodsList"
border
stripe>
<el-table-column
type="index"
label="#">
</el-table-column>
<el-table-column
prop="goods_name"
label="商品名称">
</el-table-column>
<el-table-column
prop="goods_price"
label="商品价格"
width="100px">
</el-table-column>
<el-table-column
prop="goods_weight"
label="商品重量"
width="70px">
</el-table-column>
<el-table-column
prop="goods_number"
label="商品数量"
width="70px">
</el-table-column>
<el-table-column
label="创建时间"
prop="add_time"
width="200px">
<template slot-scope="scope">
<!-- 时间格式 通过时间过滤器来装换-->
{{scope.row.add_time | dataFormat}}
</template>
</el-table-column>
<el-table-column
label="操作"
width="300px">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-edit" size="min" @click="showEditGoodsById(scope.row.goods_id)">编辑</el-button>
<el-button type="danger" icon="el-icon-delete" size="min" @click="removeGoodsById(scope.row.goods_id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pagesize"
:page-sizes="[1, 5, 10, 20,40]"
:page-size="queryInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!-- 编辑对话框-->
<el-dialog
title="添加商品"
:visible.sync="editGoodsDialogVisible"
width="50%"
@close="editGoodsDialogClose">
<el-form :model="editGoodsForm" :rules="editGoodsFormRules" ref="editGoodsFormRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="商品名称" prop="goods_name">
<el-input v-model="editGoodsForm.goods_name"></el-input>
</el-form-item>
<el-form-item label="商品价格" prop="goods_price">
<el-input v-model="editGoodsForm.goods_price"></el-input>
</el-form-item>
<el-form-item label="商品数量" prop="goods_number">
<el-input v-model="editGoodsForm.goods_number"></el-input>
</el-form-item>
<el-form-item label="商品重量" prop="goods_weight">
<el-input v-model="editGoodsForm.goods_weight"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editGoodsDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="editGoods">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "List",
data(){
return{
// 商品列表数据
goodsList:[],
// 获取商品列表的参数
queryInfo: {
query:'',
pagenum:1,
pagesize:10
},
// 总数据条数
total : 0,
//编辑
editGoodsDialogVisible:false,
//编辑对话框的数据对象
editGoodsForm:{
goods_name:'',
goods_price:0,
goods_number:0,
goods_weight:0
},
//商品的Id
goodsId:0,
editGoodsFormRules:{
goods_name:[
{required:true,message:'请输入商品名',trigger:'blur'}
],
goods_price:[
{required:true,message:'请输入商品价格',trigger:'blur'}
],
goods_number:[
{required:true,message:'请输入商品数量',trigger:'blur'}
],
goods_weight:[
{required:true,message:'请输入商品重量',trigger:'blur'}
]
}
}
},
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(res.meta.msg)
}
// console.log(res)
this.goodsList = res.data.goods
this.total = res.data.total
},
//分页事件操作
handleSizeChange(newSize){
this.queryInfo.pagesize = newSize
this.getGoodsList()
},
handleCurrentChange(newNum){
this.queryInfo.pagesize = newNum
this.getGoodsList()
},
//搜索框的按钮点击事件
searchGoods(){
this.getGoodsList()
},
// 添加商品事件
goAddGoods(){
// 通过路由跳转到添加商品页面
this.$router.push('/goods/add')
},
addGoodsDialogClose(){
//关闭添加商品对话框,则情况输入框内的数据
this.$refs.addGoodsFormRef.resetFields()
},
//删除操作
async removeGoodsById(goodId){
const confirmResult = await this.$confirm('此操作将永久删除该商品, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(err => err)
// 如果用户确认删除,则返回值为字符串confirm
// 如果用户点击取消,则返回值为cancel
if(confirmResult !== 'confirm'){
return this.$message.info('取消删除')
}
// 确定删除
const {data:res} = await this.$http.delete('goods/'+goodId)
if (res.meta.status !==200){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
await this.getGoodsList()
},
//编辑按钮事件
async showEditGoodsById(id){
this.goodsId = id
const {data:res} = await this.$http.get('goods/'+this.goodsId)
if(res.meta.status !== 201){
return this.$message.error(res.meta.msg)
}
this.editGoodsDialogVisible =true
},
editGoods(){
// 表单预验证
this.$refs.editGoodsFormRef.validate( async valid=>{
if(!valid){
return
}
// 发起请求
const {data:res} = await this.$http.put('goods/'+this.goodsId, {params:this.editGoodsForm})
if(res.meta.status !== 201){
return this.$message.error(res.meta.msg)
}
this.editGoodsDialogVisible = false
await this.getGoodsList()
this.$message.success(res.meta.msg)
})
},
editGoodsDialogClose(){
this.$refs.editGoodsFormRef.resetFields()
}
}
}
</script>
<style scoped>
.btn{
display: flex;
justify-content: left;
}
</style>
分类参数
- 级联选择器
<!-- 级联框选择-->
<el-row class="goods_opt">
<el-col class="goods_col">
<span>选择商品分类:</span>
<!-- 级联选择器-->
<el-cascader
expand-trigger="hover"
:options="paramsList"
:props="goodsParams"
v-model="selecteParamsKeys"
@change="handleChange">
</el-cascader>
</el-col>
</el-row>
-
options 可选项数据源,键名可通过 Props 属性配置
-
:props="goodsParams"配置选项
// 级联选择框的配置对象
goodsParams:{
value:‘cat_id’,
label:‘cat_name’,
children:‘children’
},- v-model=“selecteParamsKeys” 绑定选择的参数
- @change=“handleChange” 当选中节点变化时触发
// 级联选择框选中项变化会触发这个函数
handleChange(){
this.getParamsDataList()
},- 分类参数源码
<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="warning"
show-icon
:closable="false">
</el-alert>
<!-- 级联框选择-->
<el-row class="goods_opt">
<el-col class="goods_col">
<span>选择商品分类:</span>
<!-- 级联选择器-->
<el-cascader
expand-trigger="hover"
:options="paramsList"
:props="goodsParams"
v-model="selecteParamsKeys"
@change="handleChange">
</el-cascader>
</el-col>
</el-row>
<!-- 标签页区-->
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- 添加动态参数-->
<el-tab-pane label="动态参数" name="many">
<el-button class="btns" type="success" :disabled="isBtnDisabled" @click="addDialogVisible = true">添加参数</el-button>
<!-- 动态参数表格-->
<el-table :data="manyTableData" border stripe>
<!-- 展开列-->
<el-table-column type="expand" >
<template slot-scope="scope">
<el-tag v-for="(item,index) in scope.row.attr_vals"
:key="index"
closable
@close="handleChanged(index,scope.row)">
{{item}}
</el-tag>
<!-- 循环渲染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" label="#"></el-table-column>
<el-table-column label="参数名称" prop="attr_name"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="min" @click="showEditDialog(scope.row.attr_id)">修改</el-button>
<el-button type="danger" icon="el-icon-delete" size="min" @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 class="btns" type="success" :disabled="isBtnDisabled" @click="addDialogVisible = true">添加属性</el-button>
<!-- 静态属性表格-->
<el-table :data="onlyTableData" border stripe>
<el-table-column type="expand" >
<template slot-scope="scope">
<el-tag v-for="(item,index) in scope.row.attr_vals"
:key="index"
closable
@close="handleChanged(index,scope.row)">
{{item}}
</el-tag>
<!-- 循环渲染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" label="#"></el-table-column>
<el-table-column label="属性名称" prop="attr_name"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="min" @click="showEditDialog(scope.row.attr_id)">修改</el-button>
<el-button type="danger" icon="el-icon-delete" size="min" @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" class="demo-ruleForm">
<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" class="demo-ruleForm">
<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>
import goodsType from "@/views/goodsManagement/goodsType";
export default {
name: "Params",
data(){
return{
//商品分类列表
paramsList:[],
// 级联选择框的配置对象
goodsParams:{
value:'cat_id',
label:'cat_name',
children:'children'
},
// 级联选择框双向绑定到的数组
selecteParamsKeys:[],
// activeName被激活的页签的名称
activeName:'many',
// 动态参数数据
manyTableData:[],
// 静态属性的数据
onlyTableData:[],
addDialogVisible: false,
//动态参数的数据对象
addForm: {},
// 动态参数的验证规则
addFormRules:{
attr_name:[
{ required: true, message: '请输入参数名称', trigger: 'blur' }
]
},
// 修改按钮
editDialogVisible: false,
// 修改的对象
editForm:{},
// 修改的表单验证规则对象
editFormRules:{
attr_name:[
{ required: true, message: '请输入参数名称', trigger: 'blur' }
]
},
// 展开列的操作
//控制文本框与按钮的切换
inputVisible:false ,
// 文本框中输入的内容
inputValue:''
}
},
created() {
this.getGoodsParamsList()
},
methods:{
// 获取所有商品分类数据列表
async getGoodsParamsList(){
const {data:res} = await this.$http.get('categories')
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.paramsList = res.data
},
// 级联选择框选中项变化会触发这个函数
handleChange(){
this.getParamsDataList()
},
//tab页签点击事件的处理函数
handleTabClick(){
this.getParamsDataList()
console.log(this.activeName)
},
// 获取参数的列表数据
async getParamsDataList(){
//只允许选中第三级分类
if(this.selecteParamsKeys.length !== 3 ){
//选中的不是三级分类
this.selecteParamsKeys = []
this.manyTableData = []
this.onlyTableData = []
return
}
//证明是选中的是三级分类
// console.log(this.selecteParamsKeys)
// 根据所选分类的Id 和桑倩所处的面板,获取对应的参数
const {data:res} = await this.$http.get(`categories/${this.goodsId}/attributes`,{params:{sel:this.activeName}})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
// console.log(res.data)
//attr_vals将这个字符串分割为数组形式
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
}
// 获取到静态属性
this.onlyTableData = res.data
},
//监听对话框的关闭事件
addDialogClosed(){
this.$refs.addFormRef.resetFields()
},
// 对话框确定按钮添加动态参宿和静态属性处理事件
addParams(){
// 表单预验证
this.$refs.addFormRef.validate( async valid => {
if(!valid){
return
}
// 发起请求
const {data:res} = await this.$http.post(`categories/${this.goodsId}/attributes`,{attr_name: this.addForm.attr_name,attr_sel:this.activeName})
if(res.meta.status !== 201){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
this.addDialogVisible = false
await this.getParamsDataList()
})
},
//修改按钮点击显示对话框事件
async showEditDialog(id){
const {data:res} = await this.$http.get(`categories/${this.goodsId}/attributes/${id}`,{params:{attr_sel:this.activeName}})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.editForm = res.data
this.editDialogVisible = true
},
//重置修改表单
editDialogClosed(){
this.$refs.editFormRef.resetFields()
},
// 点击修改对话框确定按钮的事件
editParams(){
//表单预验证
this.$refs.editFormRef.validate( async valid=>{
if(!valid){
return
}
// 发起请求
const {data:res} = await this.$http.put(`categories/${this.goodsId}/attributes/${this.editForm.attr_id}`,{attr_name: this.editForm.attr_name,attr_sel:this.activeName})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
await this.getParamsDataList()
this.editDialogVisible = false
})
},
//删除操作
async removeParams(attr_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(`categories/${this.goodsId}/attributes/${attr_id}`)
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
await this.getParamsDataList()
this.$message.success(res.meta.msg)
},
// 展开列的操作
//文本框失去焦点,或者按回车键的事件
handleInputConfirm(row){
// console.log('ok')
//文本框失去焦点后,应该显示为按钮状态
if(row.inputValue.trim().length === 0){
row.inputValue = ''
row.inputVisible = false
return
}
// 如过没有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.goodsId}/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(res.meta.msg)
}
this.$message.success(res.meta.msg)
},
//点击按钮,显示按钮
showInput(row){
row.inputVisible =true
// 让文本框自动获取焦点
// $nextTick方法的作用,就是当页面上元素被重新渲染之后,才会指定回调函数中的代码
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
})
},
//删除对应的参数可选项
handleChanged(index,row){
row.attr_vals.splice(index,1)
//进行删除操作后,把这次操作保存到数据库
this.saveAttrVals(row)
}
},
// 计算属性
computed : {
// 如果按钮需要被禁用,则返回true,否则返回false
isBtnDisabled() {
if (this.selecteParamsKeys.length !== 3) {
return true
}
return false
},
//当前选中的三级分类的Id
//分类Id
goodsId() {
if (this.selecteParamsKeys.length === 3) {
return this.selecteParamsKeys[this.selecteParamsKeys.length - 1]
}
return null
},
// 动态添加对话框名称
titleText() {
if (this.activeName === 'many') {
return '动态参数'
}
return '静态属性'
},
}
}
</script>
<style scoped>
.el-alert{
line-height: 100%;
}
.goods_opt{
margin: 15px 0;
}
.goods_col{
display: flex;
justify-content: left;
}
.el-tabs{
line-height: 100%;
}
.btns{
display: flex;
justify-content: left;
}
.el-tag{
margin: 15px;
}
.input-new-tag{
width: 150px;
}
</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="success" @click="showGoodsDialogVisible">添加分类</el-button>
</el-col>
</el-row>
<!-- 带有树形网格的VUE表格区-->
<tree-table :data="goodsList"
:columns="columns"
:selection-type="false"
:expand-type="false"
border
stripe
show-index
index-text="#"
: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="sort" slot-scope="scope">
<el-tag v-if="scope.row.cat_level === 0">标签一</el-tag>
<el-tag v-else-if="scope.row.cat_level === 1" type="success">标签一</el-tag>
<el-tag v-else type="danger">标签一</el-tag>
</template>
<template slot="operation" slot-scope="scope">
<el-button size="min" type="success" icon="el-icon-edit" @click="showEditGoods(scope.row.cat_id)">编辑</el-button>
<el-button size="min" type="danger" icon="el-icon-error" @click="removeGoods(scope.row.cat_id)">删除</el-button>
</template>
</tree-table>
<!-- 分页区-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="querInfo.pagenum"
:page-sizes="[1,2,3,4,5,10]"
:page-size="querInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!-- 添加分类对话框-->
<el-dialog
title="添加分类"
:visible.sync="addGoodsDialogVisible"
width="50%"
@close="addDialogClose">
<el-form :model="goodsFrom" :rules="goodsFromRules" ref="goodsFromRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="分类名称" prop="cat_name">
<el-input v-model="goodsFrom.cat_name"></el-input>
</el-form-item>
<el-form-item label="父级分类" >
<!-- options指定数据源-->
<!-- props用来指定配置对象的-->
<el-cascader
:options="parenGoodsList"
expand-trigger="hover"
:props="caseGoodsProps"
v-model="selectKeys"
@change="parentGoodsChange"
clearable
change-on-select>
</el-cascader>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addGoodsDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addGoods">确 定</el-button>
</span>
</el-dialog>
<!-- 编辑对话框-->
<el-dialog
title="编辑"
:visible.sync="editDialogVisible"
width="50%"
@close="editDialogClose">
<el-form :model="editFrom" :rules="editFromRules"
ref="editFromRef" label-width="100px"
class="demo-ruleForm">
<el-form-item label="分类名称" prop="cat_name">
<el-input v-model="editFrom.cat_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="editGoods">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "goodsType",
data(){
return{
//查询条件
querInfo:{
type : 3,
pagenum:1,
pagesize:5
},
// 商品分类数据列表goodsList
goodsList:[],
// 总数据条数
total: 0,
//为tree-table指定列名的定义
columns:[
//第一列
{
label:'分类名称',
prop:'cat_name'
},
// 第二列 是否有效
{
label: '是否有效',
// 表示:当前列定义为模板列,也就是要是用到定义域插槽
type:'template',
template: 'isOk'
},
// 第三列 排序 用到定义域插槽
{
label: '排序',
type:'template',
template: 'sort'
},
// 第四列 操作 作用域插槽
{
label: '操作',
type:'template',
template: 'operation'
}
],
// 添加分类
addGoodsDialogVisible: false,
//添加分类的数据对象
goodsFrom:{
cat_name:'',
// 父级分类Id
cat_pid:0,
// 当前分类等级
cat_level: 0
},
goodsFromRules:{
cat_name:[
{ required: true, message: '请输入分类名称', trigger: 'blur' }
]
},
//父级分类的列表
parenGoodsList:[],
// 指定级联选择器的配置对象
caseGoodsProps:{
// :props="caseGoodsProps" props 的参数
// value指定选项的值为选项对象的某个属性值
// label指定选项标签为选项对象的某个属性值
// children指定选项的子选项为选项对象的某个属性值
value:'cat_id',
label:'cat_name',
children : 'children'
},
// 选中的父级分类Id数组
selectKeys:[],
// 编辑操作
editDialogVisible: false,
// 编辑数据列表
editFrom: {},
editFromRules:{
cat_name:[
{ required: true, message: '请输入分类名称', trigger: 'blur' }
]
}
}
},
created() {
this.getGoodsList()
},
methods:{
//请求获取商品分类数据列表
async getGoodsList(){
const {data:res} = await this.$http.get('categories',{params:this.querInfo})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
// 获取商品分类数据列表成功
this.goodsList = res.data.result
this.total = res.data.total
},
// 分页
handleSizeChange(newSize){
this.querInfo.pagesize = newSize
this.getGoodsList()
},
handleCurrentChange(newNum){
this.querInfo.pagenum = newNum
this.getGoodsList()
},
//添加分类操作
showGoodsDialogVisible(){
// 先获取父级分类的数据列表,
this.getParentGoodsList()
this.addGoodsDialogVisible = true
},
//获取父级分类的数据列表
async getParentGoodsList(){
const {data:res} =await this.$http.get('categories',{params:{type:2}})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
// console.log(res.data)data
this.parenGoodsList = res.data
},
//选择项发生变化触发这个函数
parentGoodsChange(){
// console.log(this.selectKeys)
// 如果selectKeys数组中的length大于0 证明选中了父级分类
// 如果selectKeys数组中的length等于0 证明没有选中父级分类
if(this.selectKeys.length > 0){
this.goodsFrom.cat_pid = this.selectKeys[this.selectKeys.length-1]
//位当前分类的等级赋值
this.goodsFrom.cat_level = this.selectKeys.length
return
}
this.goodsFrom.cat_pid = 0
this.goodsFrom.cat_level = 0
},
//添加分类点击按钮
addGoods(){
// 表单预验证
this.$refs.goodsFromRef.validate( async valid =>{
if(!valid){
return
}
// 请求数据
const {data :res} = await this.$http.post('categories',{cat_pid:this.goodsFrom.cat_pid,cat_name:this.goodsFrom.cat_name,cat_level:this.goodsFrom.cat_level})
if(res.meta.status !== 201){
return this.$message.error(res.meta.msg)
}
await this.getGoodsList()
this.addGoodsDialogVisible = false
})
},
//关闭对话框后,重置清空对话框
addDialogClose(){
this.$refs.goodsFromRef.resetFields()
this.selectKeys = []
this.goodsFrom.cat_pid = 0
this.goodsFrom.cat_level = 0
},
// 编辑
async showEditGoods(id){
const {data:res} = await this.$http.get('categories/'+id)
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
// console.log( res.data)
this.editFrom = res.data
this.editDialogVisible = true
},
editDialogClose(){
this.$refs.editFromRef.resetFields()
},
// 确定按钮
editGoods(){
// 表单预验证
this.$refs.editFromRef.validate( async valid =>{
if(!valid){
return
}
// 发起请求
const {data:res} = await this.$http.put('categories/'+this.editFrom.cat_id,{cat_name:this.editFrom.cat_name})
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
await this.getGoodsList()
this.editDialogVisible = false
this.$message.success(res.meta.msg)
})
},
// 删除操作
async removeGoods(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('categories/'+id)
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
await this.getGoodsList()
}
}
}
</script>
<style scoped>
.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>
<el-col :span="8">
<el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="searchOrders">
<el-button slot="append" icon="el-icon-search" @click="searchOrders"></el-button>
<!-- 点击后触发clear清空事件-->
</el-input>
</el-col>
</el-row>
<!-- 表格区-->
<el-table
:data="orderList"
stripe
border
style="width: 100%">
<el-table-column
type="index"
label="#">
</el-table-column>
<el-table-column
prop="order_number"
label="订单编号">
</el-table-column>
<el-table-column
prop="order_price"
label="订单价格"
width="180">
</el-table-column>
<el-table-column
prop="pay_status"
label="是否付款"
width="180">
<template slot-scope="scope">
<el-tag type="danger" v-if="scope.row.pay_status==='0'">未付款</el-tag>
<el-tag type="success" v-else>已付款</el-tag>
</template>
</el-table-column>
<el-table-column
prop="is_send"
label="是否发货"
width="180">
</el-table-column>
<el-table-column
prop="create_time"
label="下单时间"
width="200">
<template slot-scope="scope">
<!-- 时间格式 通过时间过滤器来装换-->
{{scope.row.update_time | dataFormat}}
</template>
</el-table-column>
<el-table-column
label="操作"
width="180">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" circle @click="showAddress(scope.row.order_id)"></el-button>
<el-button type="success" icon="el-icon-location" circle @click="showDetailsBox"></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">
</el-pagination>
</el-card>
<!-- 编辑对话框-->
<el-dialog
title="修改地址"
:visible.sync="editDialogVisible"
width="30%"
@close="addressHandleClosed">
<el-form :model="addressForm" :rules="addressFormRules" ref="addressFormRef" label-width="100px" class="demo-ruleForm">
<el-form-item label="省市区/县" prop="address1">
<el-cascader
v-model="addressForm.address"
:options="cityData"
:props="{ expandTrigger: 'hover' } ">
</el-cascader>
</el-form-item>
<el-form-item label="详细地址" prop="consignee_addr">
<el-input v-model="addressForm.address2"></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="editAddress">确 定</el-button>
</span>
</el-dialog>
<!-- 查看订单详情 -->
<el-dialog
title="查看订单详情"
:visible.sync="orderDetailsDialogVisible"
width="50%">
<el-timeline>
<el-timeline-item
v-for="(activity, index) in getOrderDetails"
:key="index"
:timestamp="activity.time">
{{activity.context}}
</el-timeline-item>
</el-timeline>
</el-dialog>
</div>
</template>
<script>
// <!--导入城市信息-->
import cityData from './citydata'
export default {
name: "orderList",
data(){
return{
// 获取到的订单列表的数据
orderList:[],
// 获取订单列表的参数
queryInfo:{
query:'',
pagenum:1,
pagesize:10
},
total:0,
// 修改地址
editDialogVisible:false,
addressForm:{
address1:[],
address2:'',
is_send:'',
order_pay:'',
order_price:0,
order_number:'',
pay_status:''
},
//城市信息
cityData,
addressFormRules:{
address1:[
{ required: true, message: '请选择省市区县', trigger: 'blur' }
],
address2: [
{ required: true, message: '请输入详细地址', trigger: 'blur' }
]
},
// 订单的id
getOrderId:0,
orderDetailsDialogVisible:false,
//物流信息
getOrderDetails:[]
}
},
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(res.meta.msg)
}
// console.log(res)
this.orderList = res.data.goods
// console.log(this.orderList)
this.total = res.data.total
},
// 分页
//当页面的条数改变时会触发这个事件
handleSizeChange(newSize){
this.queryInfo.pagesize = newSize
this.getOrderList()
},
//当页面的当前页面发生改变时,会触发这个事件,即选择下一页
handleCurrentChange(newNum){
this.queryInfo.pagenum = newNum
this.getOrderList()
},
//查询输入框
searchOrders(){
this.getOrderList()
},
//编辑对话框
addressHandleClosed(){
//关闭编辑对话框后,清空对话框内容
this.$refs.addressFormRef.resetFields()
},
showAddress(id){
this.getOrderId = id
this.editDialogVisible=true
},
editAddress(){
//表单预验证
this.$refs.addressFormRef.validate(async valid=>{
if(!valid){
return
}
//发起请求
const {data:res} = await this.$http.put('orders/'+this.getOrderId,{params:this.addressForm})
if (res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
this.$message.success(res.meta.msg)
await this.getOrderList()
this.editDialogVisible = false
})
},
async showDetailsBox(){
// const {data:res} = await this.$http.get('/kuaidi/1106975712662')
// if (res.meta.status !== 200){
// return this.$message.error('获取物流信息失败!')
// }
// this.getOrderDetails = res.data
this.orderDetailsDialogVisible =true
},
}
}
</script>
<style scoped>
.el-cascader{
width: 100%;
}
</style>
数据报表
需要用到echarts
<!-- 卡片区-->
<el-card>
<!-- 报表视图-->
<!-- 2.为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="width: 100%;height:400px;"></div>
</el-card>
- 从后台获取数据
- mounted钩子函数,表示当页面上的数据全部被渲染完毕之后会被调用
//mounted钩子函数,表示当页面上的数据全部被渲染完毕之后会被调用
async mounted() {
// 3. 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
//发起get请求,获取数据
const {data:res} = await this.$http.get('reports/type/1')
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
//4. 指定图表的配置项和数据
const result = _.merge(res.data,this.options)
//5. 使用刚指定的配置项和数据显示图表。
myChart.setOption(result);
},
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'
}
]
}
}
}
- 数据报表源码
<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: 100%;height:400px;"></div>
</el-card>
</div>
</template>
<script>
// 1.导入echarts
import * as echarts from 'echarts';
//导入这个组件用来合并,options和res.data,来确保图表中存在鼠标跟随等事件的发生
import _ from 'lodash'
export default {
name: "dataReport",
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() {
},
//mounted钩子函数,表示当页面上的数据全部被渲染完毕之后会被调用
async mounted() {
// 3. 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
//发起get请求,获取数据
const {data:res} = await this.$http.get('reports/type/1')
if(res.meta.status !== 200){
return this.$message.error(res.meta.msg)
}
//4. 指定图表的配置项和数据
const result = _.merge(res.data,this.options)
// var option = {
// title: {
// text: 'ECharts 入门示例'
// },
// tooltip: {},
// legend: {
// data:['销量']
// },
// xAxis: {
// data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
// },
// yAxis: {},
// series: [{
// name: '销量',
// type: 'bar',
// data: [5, 20, 36, 10, 10, 20]
// }]
// };
//5. 使用刚指定的配置项和数据显示图表。
myChart.setOption(result);
},
methods:{
}
}
</script>
<style scoped>
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './assets/CSS/global.css'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
import axios from "axios"
import 'default-passive-events'
//为项目添加进度条
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
//警告提示框
import { MessageBox } from 'element-ui'
import TreeTable from 'vue-table-with-tree-grid'
// 带有树形网格的VUE表
//导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
//导入富文本编辑器的样式
// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
//配置请求的根路径,也就是API接口文档中的接口基准地址
axios.defaults.baseURL = "http://127.0.0.1:8888/api/private/v1/"
//请求拦截器
//通过axios请求拦截器添加token,保证拥有获取数据的权限 显示进度条NProgress.start()
axios.interceptors.request.use(config=>{
NProgress.start()
// console.log(config)
config.headers.Authorization = window.sessionStorage.getItem('token')
return config
})
// response 拦截器中(这个拦截器表示获取数据成功), 隐藏进度条NProgress.done()
axios.interceptors.response.use(config => {
NProgress.done()
return config
})
Vue.config.productionTip = false
Vue.use(Element);
Vue.prototype.$http=axios;
//MessageBox.confirm弹框的引用
Vue.prototype.$confirm = MessageBox.confirm
// 带有树形网格的VUE表
Vue.component('tree-table', TreeTable)
//格式化时间,时间过滤器
Vue.filter('dataFormat', function (originVal) {
const dt = new Date(originVal)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
const d = (dt.getDate() + '').padStart(2, '0')
const hh = (dt.getHours() + '').padStart(2, '0')
const mm = (dt.getMinutes() + '').padStart(2, '0')
const ss = (dt.getSeconds() + '').padStart(2, '0')
// yyyy-mm-dd hh:mm:ss
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})
//添加富文本编辑器为全局变量。
Vue.use(VueQuillEditor)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
路由index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from "../views/Login";
import Home from "@/views/Home";
import User from "../views/user/User";
import Welcome from "../views/Welcome/Welcome";
import Rights from "../views/power/Rights"
import Roles from "../views/power/Roles"
import goodsType from "@/views/goodsManagement/goodsType";
import Params from "@/views/goodsManagement/Params";
import List from "@/views/goodsManagement/List";
import Add from "@/views/goodsManagement/Add";
import orderList from "@/views/order/orderList";
import dataReport from "@/views/dataReport/dataReport";
Vue.use(VueRouter)
const routes = [
{ //路由重定向
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path:'/home',
props:true, //获取name
name:'Home',
//路由重定向
redirect:'/welcome',
component: Home,
children:[
{
path:'/welcome',
name:'Welcome',
component:Welcome
},
{
path:'/users',
name:'User',
component: User
},
{
path: '/rights',
name: 'Rights',
component: Rights
},
{
path: '/roles',
name: 'Roles',
component: Roles
},
{
path: '/categories',
name: 'goodsType',
component: goodsType
},
{
path: '/params',
name:'Params',
component: Params
},
{
path: '/goods',
name:'List',
component: List
},
{
path: '/goods/add',
name:'Add',
component: Add
},
{
path: '/orders',
name:'orders',
component: orderList
},
{
path:'/reports',
name:'dataReport',
component: dataReport
}
]
}
]
const router = new VueRouter({
routes
})
//路由导航守卫控制访问权限
//如果用户没有登录,但是直接通过URL访问特定页面,需要重新导航到登录页面。
//为路由对象,添加beforEach 导航守卫
// router.beforeEach((to,from,next)=>{});
//开始路由守卫挂载
router.beforeEach((to,from,next)=>{
// to表示将要访问的路径
// from表示原路径
// next表示是一个函数,表示放行
// next()放行,,next(‘/login')强制跳转
if(to.path ==='/login')
return next();
// 获取token
const tokenStr= window.sessionStorage.getItem('token')
// 没有token,强制跳转到登录页面
if(!tokenStr)
return next('/login');
//有token直接放行
next();
})
export default router
更多推荐
所有评论(0)