1 创建项目
1.1 Vue 2.0
cmd d:\html\test 创建一个文件夹放vue项目
vue init webpack test 创建项目
cd test 进入刚刚创建的项目
npm run dev 启动项目
1.2 Vue 3.0
cmd d:\html\test 创建一个文件夹放vue项目
vue create test 创建项目
根据下面图片选择配置:空格是选择,回车是确认!!
cd test 进入刚刚创建的项目
npm run serve 启动项目
2 导入插件
2.1 Element-UI 插件
npm install --save element-ui
在main.js 里面引用element-ui 组件
//引用element-ui 以及样式import ElementUI from'element-ui';import'element-ui/lib/theme-chalk/index.css';// 安装ElementUI 配置全局
Vue.use(ElementUI,{size:'small'});
2.2 Axios
npm install --save axios
2.2.1 简单
创建utils文件在api里面封装自己需要 get post put delete请求
import axios from'axios'let base ='';exportconstpostRequest=(url, params)=>{returnaxios({method:'post',url:`${base}${url}`,data: params
})};//传递json的put请求exportconstputRequest=(url, params)=>{returnaxios({method:'put',url:`${base}${url}`,data: params
})}//传递json的get请求exportconstgetRequest=(url, params)=>{returnaxios({method:'get',url:`${base}${url}`,data: params
})}//传递json的delete请求exportconstdeleteRequest=(url, params)=>{returnaxios({method:'delete',url:`${base}${url}`,data: params
})}
import{postRequest}from"./utils/api";import{putRequest}from"./utils/api";import{getRequest}from"./utils/api";import{deleteRequest}from"./utils/api";// 全局方法挂载Vue.prototype.postRequest = postRequest;Vue.prototype.putRequest = putRequest;Vue.prototype.getRequest = getRequest;Vue.prototype.deleteRequest = deleteRequest;
2.2.2 封装
创建utils文件夹 request.js
importaxios from 'axios'//请求超时时间
axios.defaults.timeout =10000;//设置请求头以json格式发送到后端
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
export default axios
创建api文件夹 login.js
data为post请求的携带信息
params是get请求携带的参数
importrequest from '@/utils/request'
//登录
export function login(data){returnrequest({
url: '/auth/login',
method:'post',
data: data
})}//注册
export function register(data){returnrequest({
url: '/auth/register',
method:'post',
data: data
})}
export function fetchList(query){returnrequest({
url: '/admin/user/page',
method:'get',
params: query
})}
export function addObj(obj){returnrequest({
url: '/admin/user',
method:'post',
data: obj
})}
2.3 Vuex 状态管理模式
npm install vuex --save
创建store文件
import Vue from'vue'import Vuex from'vuex'
Vue.use(Vuex);exportdefaultnewVuex.Store({state:{},//同步执行操作mutations:{},//异步执行操作actions:{},modules:{}})
import store from'./store'newVue({
router,
store,//引用render:h=>h(App)}).$mount('#app');
2.4 Scss
npm install node-sass --save
npm install sass-loader --save
npm install scss --save
npm install scss-loader --save
引入过程中会提示有高危漏洞使用命令:
npm audit fix
npm install
错误解决:
检查代码中并无写错的地方
其实涉及到这个问题,就是版本原因了,我安装的 scss-loader 版本太高,卸载安装低版本即可
卸载:npm uninstall 名字比如:sass
npm uninstall --save sass-loader // 卸载
npm i -D [email protected] // 安装
npm uninstall --save node-sass // 卸载
npm i [email protected]// 安装
2.5 图标
我们使用了 Font Awesome 的图标做为菜单图标,使用前先安装 Font Awesome
npm install font-awesome
导入 Font Awesome (main.js)
import'font-awesome/css/font-awesome.min.css'
3 配置全局响应拦截器(业务逻辑错误)
import{Message}from'element-ui'import router from'../router'//响应拦截器
axios.interceptors.response.use(success=>{//业务逻辑错误if(success.status && success.status ==200){//500 业务逻辑错误,401 未登录,403 权限错误if(success.data.code==500||success.data.code==401||success.data.code==403){
Message.error({message:success.data.msg});return;}if(success.data.message){
Message.success({message:success.data.msg});}}return success.data;},error=>{//504 服务器有问题,404 页面找不到if(error.response.code==504||error.response.code==404){
Message.error({message:'服务器没有了'});}elseif(error.response.code==403){
Message.error({message:'权限不足,请联系管理员!'})}elseif(error.response.code==401){
Message.error({message:'尚未登录,请登录'})
router.replace('/');}else{if(error.response.data.message){
Message.error({message:error.response.data.msg});}else{
Message.error({message:'未知错误!'});}}return;});
4 登录页面
4.1 配置跨越
创建vue.config.js文件
let proxyObj ={}//代理对象
proxyObj['/']={//代理路径//websocketws:true,//目标地址target:'http://localhost:8082',// target: 'http://47.115.143.129:8082',//发送请求头中host会设置成targetchangeOrigin:true,// 开启跨域//不重写请求地址pathReWrite:{'^/':'/'}};
module.exports ={assetsDir:'static',// 静态资源保存路径outputDir:'medicine-ui',// 打包后生成的文件夹lintOnSave:false,productionSourceMap:false,// 取消错误日志runtimeCompiler:true,// 实时编译devServer:{open:true,host:'localhost',port:80,proxy: proxyObj //代理}};
4.2 创建Login.vue
<template>
<div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">后台管理系统</h3>
<el-form-item prop="userName">
<el-input v-model="loginForm.userName"
type="text"
auto-complete="false"
placeholder="账号">
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password"
type="password"
auto-complete="false"
placeholder="密码">
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaOnOff">
<el-input v-model="loginForm.code"
auto-complete="false"
placeholder="验证码"
style="width: 63%">
</el-input>
<div class="login-code">
<img :src="codeUrl" @click="updateCaptcha" class="login-code-img"/>
</div>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
<el-form-item style="width:100%;">
<el-button :loading="loading" size="medium" type="primary" style="width:100%;">
<span v-if="!loading">登 录</span>
<span v-else>登 录 中...</span>
</el-button>
<div style="float: right;" v-if="register">
<router-link class="link-type" :to="'/register'">立即注册</router-link>
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2022 admin All Rights Reserved.</span>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
codeUrl: '/captcha?time=' + new Date(),
loginForm: {
userName: "admin",
password: "123456",
rememberMe: false,
code: ""
},
loginRules: {//错误提示信息
userName: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
},
loading: false,
// 验证码开关
captchaOnOff: true,
// 注册开关
register: false
};
},
methods: {
//获取验证码
updateCaptcha() {
this.codeUrl = '/captcha?time=' + new Date();
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
display: flex;
position: fixed;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
background-image: url("../assets/images/login-background.jpg");
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 38px;
}
</style>
import Vue from'vue'import VueRouter from'vue-router'import Login from'../views/Login.vue'import Home from'../views/Home.vue'
Vue.use(VueRouter)const routes =[{path:'/',name:'Login',component: Login,hidden:true//隐藏路由},{path:'/register',name:'注册',component: Register,hidden:true//隐藏路由},{path:'/home',//路径name:'控制台',//名字redirect:'console',//重定向路由component: Home,//文件地址hidden:true,//隐藏路由children:[//子级菜单{path:'/console',name:'控制台',component: Console
}]}]const router =newVueRouter({// mode: 'history', // 去掉url中的#
routes
})exportdefault router
4.3 验证码
4.3.1 前端代码
<el-form-item prop="code" v-if="captchaOnOff">
<el-input v-model="loginForm.code"
auto-complete="false"
placeholder="验证码"
style="width: 63%">
</el-input>
<div class="login-code">
<img :src="codeUrl" @click="updateCaptcha" class="login-code-img"/>
</div>
</el-form-item>
//获取验证码codeUrl:'/captcha?time='+newDate(),
methods:{//获取验证码updateCaptcha(){this.codeUrl ='/captcha?time='+newDate();}}
4.3.2 后端代码
<!--google kaptcha依赖--><dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>0.0.9</version></dependency><!--产生随机数--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency>
创建RandomUtils类
package com.example.admin.utils;import java.awt.*;import java.util.Random;publicclassRandomUtilsextendsorg.apache.commons.lang3.RandomUtils{privatestatic final char[]CODE_SEQ={'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z','2','3','4','5','6','7','8','9'};privatestatic final char[]NUMBER_ARRAY={'0','1','2','3','4','5','6','7','8','9'};privatestatic Random random =newRandom();publicstatic String randomString(int length){
StringBuilder sb =newStringBuilder();for(int i =0; i < length; i++){
sb.append(String.valueOf(CODE_SEQ[random.nextInt(CODE_SEQ.length)]));}return sb.toString();}publicstatic String randomNumberString(int length){
StringBuilder sb =newStringBuilder();for(int i =0; i < length; i++){
sb.append(String.valueOf(NUMBER_ARRAY[random.nextInt(NUMBER_ARRAY.length)]));}return sb.toString();}publicstatic Color randomColor(int fc, int bc){
int f = fc;
int b = bc;
Random random =newRandom();if(f >255){
f =255;}if(b >255){
b =255;}returnnewColor(f + random.nextInt(b - f), f + random.nextInt(b - f), f + random.nextInt(b - f));}publicstatic int nextInt(int bound){return random.nextInt(bound);}}
创建Model文件夹 VerifyCode
package com.example.admin.model;import lombok.Data;
@Data
publicclassVerifyCode{private String code;private byte[] imgBytes;private long expireTime;}
创建CaptchaUtils类
package com.example.admin.utils;import com.example.admin.model.VerifyCode;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;import java.util.Random;publicclassCaptchaUtils{privatestatic final Logger logger = LoggerFactory.getLogger(CaptchaUtils.class);privatestatic final String[]FONT_TYPES={"\u5b8b\u4f53","\u65b0\u5b8b\u4f53","\u9ed1\u4f53","\u6977\u4f53","\u96b6\u4e66"};privatestatic final int VALICATE_CODE_LENGTH=4;/**
* 设置背景颜色及大小,干扰线
*
* @param graphics
* @param width
* @param height
*/privatestaticvoidfillBackground(Graphics graphics, int width, int height){// 填充背景
graphics.setColor(Color.WHITE);//设置矩形坐标x y 为0
graphics.fillRect(0,0, width, height);// 加入干扰线条for(int i =0; i <8; i++){//设置随机颜色算法参数
graphics.setColor(RandomUtils.randomColor(40,150));
Random random =newRandom();
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
graphics.drawLine(x, y, x1, y1);}}/**
* 生成随机字符
*
* @param width
* @param height
* @param os
* @return
* @throws IOException
*/public String generate(int width, int height, OutputStream os) throws IOException {
BufferedImage image =newBufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();fillBackground(graphics, width, height);
String randomStr = RandomUtils.randomString(VALICATE_CODE_LENGTH);createCharacter(graphics, randomStr);
graphics.dispose();//设置JPEG格式
ImageIO.write(image,"JPEG", os);return randomStr;}/**
* 验证码生成
*
* @param width
* @param height
* @return
*/public VerifyCode generate(int width, int height){
VerifyCode verifyCode =null;try(//将流的初始化放到这里就不需要手动关闭流
ByteArrayOutputStream baos =newByteArrayOutputStream();){
String code =generate(width, height, baos);
verifyCode =newVerifyCode();
verifyCode.setCode(code);
verifyCode.setImgBytes(baos.toByteArray());}catch(IOException e){
logger.error(e.getMessage(), e);
verifyCode =null;}return verifyCode;}/**
* 设置字符颜色大小
*
* @param g
* @param randomStr
*/privatevoidcreateCharacter(Graphics g, String randomStr){
char[] charArray = randomStr.toCharArray();for(int i =0; i < charArray.length; i++){//设置RGB颜色算法参数
g.setColor(newColor(50+ RandomUtils.nextInt(100),50+ RandomUtils.nextInt(100),50+ RandomUtils.nextInt(100)));//设置字体大小,类型
g.setFont(newFont(FONT_TYPES[RandomUtils.nextInt(FONT_TYPES.length)], Font.BOLD,26));//设置x y 坐标
g.drawString(String.valueOf(charArray[i]),15* i +5,19+ RandomUtils.nextInt(8));}}}
创建Controller文件夹 CaptchaController
package com.example.admin.controller;import com.example.admin.model.VerifyCode;import com.example.admin.utils.CaptchaUtils;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/**
* 验证码
*/
@Controller
@Api(tags ="图片验证码")publicclassCaptchaController{privatestatic final Logger logger = LoggerFactory.getLogger(CaptchaController.class);
@ApiOperation(value ="验证码")
@GetMapping("captcha")publicvoidverifyCode(HttpServletRequest request, HttpServletResponse response){
CaptchaUtils iVerifyCodeGen =newCaptchaUtils();try{//设置长宽
VerifyCode verifyCode = iVerifyCodeGen.generate(80,28);
String code = verifyCode.getCode();//将VerifyCode绑定session
request.getSession().setAttribute("code", code);
logger.info("验证码:"+ code);//设置响应头
response.setHeader("Pragma","no-cache");//设置响应头
response.setHeader("Cache-Control","no-cache");//在代理服务器端防止缓冲
response.setDateHeader("Expires",0);//设置响应内容类型
response.setContentType("image/jpeg");
response.getOutputStream().write(verifyCode.getImgBytes());
response.getOutputStream().flush();}catch(IOException e){
logger.info("", e);
e.getStackTrace();}}}
4.4 登录
4.4.1 前端代码
<el-button
:loading="loading"
size="medium"
style="width:100%;"
type="primary"
@click="submitLogin"><span v-if="!loading">登 录</span><span v-else>登 录 中...</span></el-button>
methods:{//登录事件submitLogin(){this.$refs.loginForm.validate(valid=>{if(valid){
console.log(this.loginForm);this.loading =true;this.postRequest("/login",this.loginForm).then(resp=>{if(resp){this.loading =false;
console.log(resp);//页面跳转let path =this.$route.query.redirect;this.$router.replace(
path =="/"|| path ==undefined?"/home": path
);// this.$router.replace('/home');}else{this.loading =false;}});}else{this.$message.error("请输入所有字段");returnfalse;}});}}
4.4.2 后端代码
package com.example.admin.utils;import java.util.HashMap;/**
* 操作消息提醒
*
* @author ruoyi
*/publicclassAjaxResultextendsHashMap<String, Object>{privatestatic final long serialVersionUID = 1L;/**
* 状态码
*/publicstatic final String CODE_TAG="code";/**
* 返回内容
*/publicstatic final String MSG_TAG="msg";/**
* 数据对象
*/publicstatic final String DATA_TAG="data";/**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/publicAjaxResult(){}/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/publicAjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/publicAjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if(StringUtils.isNotNull(data)){super.put(DATA_TAG, data);}}/**
* 方便链式调用
*
* @param key
* @param value
* @return
*/
@Override
public AjaxResult put(String key, Object value){super.put(key, value);returnthis;}/**
* 返回成功消息
*
* @return 成功消息
*/publicstatic AjaxResult success(){return AjaxResult.success("操作成功");}/**
* 返回成功数据
*
* @return 成功消息
*/publicstatic AjaxResult success(Object data){return AjaxResult.success("操作成功", data);}/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/publicstatic AjaxResult success(String msg){return AjaxResult.success(msg,null);}/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/publicstatic AjaxResult success(String msg, Object data){returnnewAjaxResult(HttpStatus.SUCCESS, msg, data);}/**
* 返回错误消息
*
* @return
*/publicstatic AjaxResult error(){return AjaxResult.error("操作失败");}/**
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
*/publicstatic AjaxResult error(String msg){return AjaxResult.error(msg,null);}/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/publicstatic AjaxResult error(String msg, Object data){returnnewAjaxResult(HttpStatus.ERROR, msg, data);}/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
*/publicstatic AjaxResult error(int code, String msg){returnnewAjaxResult(code, msg,null);}}
package com.example.admin.vo;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.experimental.Accessors;import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper =false)
@Accessors(chain =true)
@ApiModel(value ="AdminLogin对象", description ="")publicclassAdminLoginimplementsSerializable{privatestatic final Long serialVersionUID =-80646425239914972L;
@ApiModelProperty(value ="账号")private String userName;
@ApiModelProperty(value ="密码")private String password;
@ApiModelProperty(value ="验证码")private String code;}
package com.example.admin.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.admin.model.SysUser;import com.example.admin.service.SysUserService;import com.example.admin.utils.AjaxResult;import com.example.admin.vo.AdminLogin;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;
@RestController
@Api(tags ="用户")publicclassLoginController{privatestatic final Logger logger = LoggerFactory.getLogger(CaptchaController.class);
@Resource
private SysUserService sysUserService;
@PostMapping("/login")
@ApiOperation("登录接口")public AjaxResult getLogin(@RequestBody AdminLogin adminLogin, HttpServletRequest request){//获取session里面的验证码
String captcha =(String) request.getSession().getAttribute("code");if(adminLogin !=null&& adminLogin.getCode().equals(captcha)){
SysUser sysUser = sysUserService.getOne(newQueryWrapper<SysUser>().eq("user_name", adminLogin.getUserName()).eq("password", adminLogin.getPassword()));if(sysUser !=null){return AjaxResult.success("登录成功!",null);}return AjaxResult.error("用户名不存在!");}else{return AjaxResult.error("验证码错误!");}}}
5 首页
<template>
<div class="box">
<el-container>
<el-aside class="nav-wrap" :width="isCollapse?'66px':'200px'">
<div style="height: 100%">
<el-menu
router
unique-opened
text-color="#fff"
:collapse="isCollapse"
:collapse-transition="false"
active-text-color="#409EFF"
style="height: 100%"
:default-active="activePath"
background-color="#344a5f">
<div class="nav-head">
<img :src="require('@/assets/images/tp6.jpg')"/>
<!--v-show 显示:true/隐藏:false-->
<div class="title" v-show="isCollapse?false:true">Admin权限管理系统</div>
</div>
<el-submenu :index="index+''" v-for="(item,index) in routes" v-if="!item.hidden" :key="index">
<template slot="title">
<i style="color: #ffffff;margin-right: 5px" :class="item.iconCls"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item :index="children.path"
v-for="(children,indexj) in item.children"
:key="indexj"
@click="saveNavState(children.path)">
<i style="color: #ffffff;margin-right: 5px" :class="children.iconCls"></i>
{{children.name}}
</el-menu-item>
</el-submenu>
</el-menu>
</div>
</el-aside>
<el-container>
<el-header class="homeHeader">
<el-row style="width: 250px">
<el-col :span="4">
<div style="font-size: 25px;margin-left: -8px">
<i style="cursor: pointer;"
:class="isCollapse?'el-icon-s-unfold':'el-icon-s-fold'"
@click="icons">
</i>
</div>
</el-col>
<el-col :span="20">
<div style="margin-top: 9px">
<el-breadcrumb separator-class="el-icon-arrow-right"
v-if="this.$router.currentRoute.path!='/home'">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
</el-breadcrumb>
</div>
</el-col>
</el-row>
<el-row style="width: 250px">
<el-col :span="12">
<div style="line-height: 56px">
<span style="font-size: 25px;">
<el-tooltip class="item" effect="dark" content="刷新" placement="bottom">
<i style="cursor: pointer;margin-right: 8px;" class="el-icon-refresh"
@click="ref"></i>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="全屏" placement="bottom">
<i style="cursor: pointer;margin-right: 8px" class="el-icon-full-screen"></i>
</el-tooltip>
<span class="lock-wrap" @click="lockChange">
<el-tooltip class="item" effect="dark" :content="lockFlag?`点击锁定`:`点击解锁`"
placement="bottom">
<i style="cursor: pointer;margin-right: 8px" class="el-icon-unlock"></i>
</el-tooltip>
</span>
</span>
</div>
</el-col>
<el-col :span="12">
<el-dropdown class="userInfo" @command="commandHandler">
<span class="nav-head">
<i><img :src="require('@/assets/images/tp6.jpg')"></i>
<span style="margin-left: 2px">
{{user.name}}<i class="el-icon-arrow-down"></i>
</span>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userInfo">个人中心</el-dropdown-item>
<el-dropdown-item command="setting">设置</el-dropdown-item>
<el-dropdown-item command="logout">注销登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
</el-header>
<el-main class="main-wrap">
<router-view class="homeRouterView"/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
//菜单展开/收起
isCollapse: false,
breadList: [], // 路由集合
user: {
name: "张三",
userFace: "",
},
// 被激活的链接地址
activePath: "",
lockFlag: true,
};
},
created() {
console.log(this.$router.options.routes);
//初始化菜单激活状态
this.activePath = window.sessionStorage.getItem("activePath");
},
computed: {
//数据初始化
routes() {
return this.$router.options.routes;
},
},
methods: {
//页面锁定事件
lockChange() {
if (this.lockFlag) {
localStorage.setItem("lockFlag", 0);
this.lockFlag = false;
this.$message({
type: "success",
message: "页面锁定成功!",
});
} else {
// const uInfo = JSON.parse(localStorage.getItem("userInfo")); // 用户
this.$prompt("", "请输入密码", {
inputType: "password",
confirmButtonText: "确定",
cancelButtonText: "取消",
inputValidator: (value) => {
// let hash = md5(value)//密码
// return hash.toUpperCase()===uInfo.passwd
},
inputErrorMessage: "密码输入不正确",
})
.then(({ value }) => {
this.$message({
type: "success",
message: "解锁成功",
});
// Message.error({message:'尚未登录,请登录'});
localStorage.setItem("lockFlag", 1);
this.lockFlag = true;
})
.catch(() => {});
}
},
//刷新页面
ref() {
this.$router.go(0);
},
//菜单展开/收起
icons() {
this.isCollapse = !this.isCollapse;
},
// 保存链接的激活状态
saveNavState(activePath) {
window.sessionStorage.setItem("activePath", activePath);
this.activePath = activePath;
},
//回调事件
commandHandler(cmd) {
if (cmd == "logout") {
this.$confirm("此操作将注销登录,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
//注销
this.postRequest("/logout");
//清除用户信息
window.sessionStorage.removeItem("user");
window.sessionStorage.removeItem("tokenStr");
//清空菜单
this.$store.commit("initRoutes", []);
//跳转登录页
this.$router.replace("/");
}).catch(() => {
this.$message({
type: "info",
message: "已取消操作",
});
});
}
if (cmd == "userInfo") {
this.$router.push("/userInfo");
}
},
},
};
</script>
<style lang="scss">
.box {
margin: -8px;
padding: 0;
height: 100%;
}
/*---- 侧边栏 start ----*/
.nav-wrap {
top: 0;
left: 0;
height: 100vh;
}
//头部背景
.nav-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
box-sizing: border-box;
height: 60px;
}
//头部字体大小
.nav-head .title {
font-size: 14px;
color: #fff;
}
//log
.nav-head img {
width: 48px;
height: 48px;
border-radius: 24px;
margin-left: -8px;
}
//二级菜单背景色
.el-menu-item {
/*background-color: rgb(31,45,61) !important;*/
}
//鼠标悬浮背景色
.el-menu-item:hover {
outline: 0 !important;
color: #409EFF !important;
}
//点击选择背景色
.el-menu-item.is-active {
color: #409EFF !important;
background: rgb(31, 45, 61) !important;
}
/*---- 侧边栏 end ----*/
/*---- 头部 start ----*/
.homeHeader {
-webkit-box-shadow: 0 3px 16px 0 rgba(0, 0, 0, .1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
box-sizing: border-box;
}
.homeHeader .userInfo {
cursor: pointer;
}
/*---- 头部 end ----*/
/*---- 内容 start ----*/
.main-wrap {
border: 20px solid #f7f7f7;
border-bottom: none;
-webkit-box-sizing: border-box
}
.homeRouterView {
/*margin-top: 10px;*/
}
/*---- 内容 end ----*/
</style>
6 侧边栏
6.1 静态菜单栏
6.1.1 页面
创建4个Test.vue页面方便测试
<template><div>test1</div></template><script>
export default{
name:"test1"}</script><style scoped></style>
6.1.2 路由
importVue from 'vue'importRouter from 'vue-router'
importLogin from '@/views/Login'
importHome from '@/views/Home'
importIndex from '@/views/Index'
importTest1 from '@/views/Test1'
importTest2 from '@/views/Test2'
importTest3 from '@/views/Test3'
importTest4 from '@/views/Test4'
Vue.use(Router)
export defaultnewRouter({
routes:[{
path:'/',
name:'Login',
component:Login,
hidden:true},{
path:'/home',//路径
name:'控制台',//名字
redirect: 'console',//重定向路由
component:Home,//文件地址
hidden:true,//隐藏路由
children:[//子级菜单{
path: '/console',
name:'控制台',
component:Index,}]},{
path:'/home',
name:'商品管理',
component:Home,
children:[{
path:'/test1',
name:'商品列表',
component:Test1,},{
path:'/test2',
name:'订单列表',
component:Test2}]},{
path:'/home',
name:'系统管理',
component:Home,
children:[{
path:'/test3',
name:'用户管理',
component:Test3,},{
path:'/test4',
name:'角色管理',
component:Test4}]}]})
6.1.3 主页
我们需要频繁添加菜单选项的时候会发现操作的步骤比较重复,因此我们可以将菜单和路由数据统一起来,将路由数据动态渲染到菜单上。
<template><div class="box"><el-container><el-aside class="nav-wrap"><div style="height: 100%"><el-menu
router
unique-opened
text-color="#fff":collapse-transition="false"
active-text-color="#409EFF"
style="height: 100%"
background-color="#344a5f"><div class="nav-head"><div class="title">Admin权限管理系统</div></div><el-submenu :index="index+''" v-for="(item,index) in routes" v-if="!item.hidden":key="index"><template slot="title"><span>{{item.name}}</span></template><el-menu-item :index="children.path" v-for="(children,indexj) in item.children":key="indexj">{{children.name}}</el-menu-item></el-submenu></el-menu></div></el-aside><el-container><el-header class="homeHeader"><h2>管理系统</h2></el-header><el-main class="main-wrap"><router-view/></el-main></el-container></el-container></div></template><script>
export default{
name:"Home",created(){
console.log(this.$router.options.routes)},
computed:{//数据初始化routes(){returnthis.$router.options.routes;}},
methods:{},};</script><style lang="scss">.box {
margin:-8px;
padding:0;
height:100%;}/*---- 侧边栏 start ----*/.nav-wrap {
top:0;
left:0;
height:100vh;}//头部背景.nav-head {
display: flex;
align-items: center;
justify-content: space-between;
padding:015px;
box-sizing: border-box;
height:60px;}//头部字体大小.nav-head .title {
font-size:14px;
color: #fff;}//log.nav-head img {
width:48px;
height:48px;
border-radius:24px;
margin-left:-8px;}//鼠标悬浮背景色.el-menu-item:hover {
outline:0!important;
color: #409EFF!important;}//点击选择背景色.el-menu-item.is-active {
color: #409EFF!important;
background:rgb(31,45,61)!important;}/*---- 侧边栏 end ----*//*---- 头部 start ----*/.homeHeader {-webkit-box-shadow:03px 16px 0rgba(0,0,0,.1);
display: flex;
align-items: center;
justify-content: space-between;
padding:015px;
box-sizing: border-box;}.homeHeader .userInfo {
cursor: pointer;}/*---- 头部 end ----*//*---- 内容 start ----*/.main-wrap {
border:20px solid #f7f7f7;
border-bottom: none;-webkit-box-sizing: border-box
}/*---- 内容 end ----*/</style>
router :是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转。我们可以取消之前的 select 事件。
6.2 动态菜单栏
6.2.1 前端代码
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
表字段:
后端响应菜单栏的格式
[{"mid":1,"url":"/","path":"/home","component":"Home","name":"商品管理","iconcls":null,"keepalive":null,"requireauth":true,"parentid":0,"enabled":true,"children":[{"mid":9,"url":null,"path":"/product/product","component":"product/product/list","name":"商品列表","iconcls":null,"keepalive":null,"requireauth":true,"parentid":1,"enabled":true,"children":null},{"mid":10,"url":null,"path":"/product/order","component":"product/order/list","name":"订单列表","iconcls":null,"keepalive":null,"requireauth":true,"parentid":1,"enabled":true,"children":null}]},{"mid":2,"url":"/","path":"/home","component":"Home","name":"会员管理","iconcls":null,"keepalive":null,"requireauth":true,"parentid":0,"enabled":true,"children":[{"mid":11,"url":null,"path":"/member/grade","component":"menber/grade","name":"会员等级","iconcls":null,"keepalive":null,"requireauth":true,"parentid":2,"enabled":true,"children":null}]},{"mid":3,"url":"/","path":"/home","component":"Home","name":"系统管理","iconcls":"el-icon-setting","keepalive":null,"requireauth":true,"parentid":0,"enabled":true,"children":[{"mid":5,"url":null,"path":"/system/user","component":"system/user/index","name":"用户管理","iconcls":"el-icon-user","keepalive":null,"requireauth":true,"parentid":3,"enabled":true,"children":null},{"mid":6,"url":null,"path":"/system/role","component":"system/role/index","name":"角色管理","iconcls":null,"keepalive":null,"requireauth":true,"parentid":3,"enabled":true,"children":null},{"mid":7,"url":null,"path":"/system/menu","component":"system/menu/index","name":"菜单栏管理","iconcls":null,"keepalive":null,"requireauth":true,"parentid":3,"enabled":true,"children":null}]},{"mid":4,"url":"/","path":"/home","component":"Home","name":"系统工具","iconcls":"el-icon-s-tools","keepalive":null,"requireauth":true,"parentid":0,"enabled":true,"children":[{"mid":8,"url":null,"path":"/tools/logs","component":"tools/logs/index","name":"日志管理","iconcls":null,"keepalive":null,"requireauth":true,"parentid":4,"enabled":true,"children":null}]}]
在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex。
import Vue from'vue'import Vuex from'vuex'
Vue.use(Vuex);exportdefaultnewVuex.Store({state:{routes:[]},//同步执行操作mutations:{//初始化initRoutes(state, data){
state.routes = data;}},//异步执行操作actions:{},modules:{}})
state全局state对象,用于保存所有组件的公共数据getters监听state值的最新状态(计算属性)actions异步执行mutations方法mutations唯一可以改变state值的方法(同步执行)
修改 main.js 增加刚才配置的 store/index.js
import Vue from'vue'import App from'./App.vue'import router from'./router'import store from'./store'//引用element-ui 以及样式import ElementUI from'element-ui';import'element-ui/lib/theme-chalk/index.css';import{postRequest}from"./utils/api";import{putRequest}from"./utils/api";import{getRequest}from"./utils/api";import{deleteRequest}from"./utils/api";//插件形式使用请求Vue.prototype.postRequest = postRequest;Vue.prototype.putRequest = putRequest;Vue.prototype.getRequest = getRequest;Vue.prototype.deleteRequest = deleteRequest;// 安装ElementUI 配置全局
Vue.use(ElementUI,{size:'small'});
Vue.config.productionTip =false;newVue({
router,
store,//引用render:h=>h(App)}).$mount('#app');
封装菜单请求工具类
后端接口返回的数据中 component 的值为String,我们需要将其转换为前端所需的对象并且我们需要将数据放入到路由的配置里。所以我们需要封装菜单请求工具类实现我们的需求。
import{getRequest}from"./api";exportconstinitMenu=(router, store)=>{if(store.state.routes.length >0){return;}//查询菜单栏getRequest('/system/menu/list').then(data=>{if(data){//格式化Routerlet fmtRoutes =formatRoutes(data);//添加到router
router.addRoutes(fmtRoutes);//将数据存入vuex
store.commit('initRoutes', fmtRoutes);}})};exportconstformatRoutes=(routes)=>{let fmtRoutes =[];
routes.forEach(router=>{let{
path,
component,
name,
iconCls,
children,}= router;if(children && children instanceofArray){//递归
children =formatRoutes(children);}let fmRouter ={path: path,name: name,iconCls: iconCls,children: children,//这里注意了,数据库里的路径要对页面的路径,不能出错了component:()=>import(`../views/${component}.vue`)// component(resolve) {// if (component.startsWith("Home")) {// require(['../views/' + component + '.vue'], resolve);// } else if (component.startsWith('system')) {// require(['../views/' + component + '.vue'], resolve);// } else if (component.startsWith('tools')) {// require(['../views/' + component + '.vue'], resolve);// } else if (component.startsWith('product')) {// require(['../views/' + component + '.vue'], resolve);// }// }};
fmtRoutes.push(fmRouter);});return fmtRoutes;};
导航守卫
菜单数据在用户点击刷新按钮时可能出现丢失的情况,解决办法
- 每个页面添加初始化菜单的方法,这显然很麻烦 。
- 路由导航守卫 。
vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的,单个路由独享的, 或者组件级的。
记住参数或查询的改变并不会触发进入离开的导航守卫。我们可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。
import Vue from'vue'import VueRouter from'vue-router'import Login from'../views/Login.vue'import Home from'../views/Home'import Console from'../views/console/index'
Vue.use(VueRouter);const routes =[{path:'/',name:'Login',component: Login,hidden:true,//隐藏路由},{path:'/home',//路径name:'Home',//名字redirect:'console',//重定向路由component: Home,//文件地址hidden:true,//隐藏路由children:[//子级菜单{path:'/console',name:'控制台',component: Console,}]}]const router =newVueRouter({
routes
})exportdefault router
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
to: Route即将要进入的目标路由对象。from: Route当前导航正要离开的路由。next: Function一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。next()进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed (确认的)。next(false)中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。next(‘/’) 或者 next({ path: ‘/’ }) : 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace:true 、 name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或router.push 中的选项。next(error)(2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给router.onError() 注册过的回调。
确保要调用 next 方法,否则钩子就不会被 resolved。
router.beforeEach((to, from, next)=>{if(to.path=='/'){next()}else{initMenu(router,store);next();}});
<el-menu router unique-opened>
<el-submenu :index="index+''" v-for="(item,index) in routes" v-if="!item.hidden" :key="index">
<template slot="title">
<i :class="item.iconCls" style="color: #1accff;margin-right: 5px"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item :index="children.path" v-for="(children,indexj) in item.children" :key="indexj">
{{children.name}}
</el-menu-item>
</el-submenu>
</el-menu>
<script>
export default {
name: "Home",
data() {
return {
isCollapse: false
};
},
computed: {//数据初始化
routes() {
return this.$store.state.routes;
}
}
}
</script>
6.2.2 后端代码
packagecom.example.admin.controller;importcom.alibaba.fastjson.JSON;importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;importcom.baomidou.mybatisplus.extension.api.ApiController;importcom.baomidou.mybatisplus.extension.api.R;importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;importcom.example.admin.model.SysMenu;importcom.example.admin.service.SysMenuService;importcom.example.admin.utils.AjaxResult;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjava.io.Serializable;importjava.util.List;/**
* (SysMenu)表控制层
*
* @author makejava
* @since 2022-04-08 16:58:26
*/@RestController@RequestMapping("sysMenu")publicclassSysMenuControllerextendsApiController{/**
* 服务对象
*/@ResourceprivateSysMenuService sysMenuService;@GetMapping("/getMenu")publicAjaxResultgetMenu(){List<SysMenu> menu = sysMenuService.getMenu();// System.out.println(JSON.toJSON(menu));if(!menu.isEmpty()){returnAjaxResult.success(menu);}returnAjaxResult.error();}}
packagecom.example.admin.service;importcom.baomidou.mybatisplus.extension.service.IService;importcom.example.admin.model.SysMenu;importjava.util.List;/**
* (SysMenu)表服务接口
*
* @author makejava
* @since 2022-04-08 16:58:27
*/publicinterfaceSysMenuServiceextendsIService<SysMenu>{List<SysMenu>getMenu();}
packagecom.example.admin.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.example.admin.dao.SysMenuDao;importcom.example.admin.model.SysMenu;importcom.example.admin.service.SysMenuService;importorg.springframework.stereotype.Service;importcom.alibaba.fastjson.JSON;importjavax.annotation.Resource;importjava.util.List;importjava.util.Objects;importjava.util.stream.Collectors;/**
* (SysMenu)表服务实现类
*
* @author makejava
* @since 2022-04-08 16:58:27
*/@Service("sysMenuService")publicclassSysMenuServiceImplextendsServiceImpl<SysMenuDao,SysMenu>implementsSysMenuService{@ResourceprivateSysMenuDao sysMenuDao;@OverridepublicList<SysMenu>getMenu(){List<SysMenu> sysMenus = sysMenuDao.selectList(null);//获取父节点List<SysMenu> collect = sysMenus.stream().filter(m -> m.getParentid()==0).map((m)->{
m.setChildList(getChildrens(m, sysMenus));return m;}).collect(Collectors.toList());return collect;}/**
* 递归查询子节点
*
* @param root 根节点
* @param all 所有节点
* @return 根节点信息
*/privateList<SysMenu>getChildrens(SysMenu root,List<SysMenu> all){List<SysMenu> children = all.stream().filter(m ->{returnObjects.equals(m.getParentid(), root.getMid());}).map((m)->{
m.setChildList(getChildrens(m, all));return m;}).collect(Collectors.toList());return children;}}
7 数据表格
7.1 分页显示
<template>
<div class="userContainer">
<el-form
:model="userForm"
ref="userForm">
<h3 class="logonTitle">个人信息</h3>
<span style="margin-left: 160px" class="el-dropdown-links">
<el-upload
action="http://medicine-img.oss-cn-shanghai.aliyuncs.com"
:data="dataObj"
list-type="picture"
:multiple="false" :show-file-list="showFileList"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview">
<img title="点击修改头像" :src="valueUrl"/>
</el-upload>
<!-- <SingleUpload v-model="userForm.avatar"></SingleUpload>-->
</span>
<el-row>
<el-col :span="13">
<el-form-item style="width: 180px" label="账号" prop="name">
<el-input v-model="userForm.name" :disabled="true"></el-input>
</el-form-item>
</el-col>
<el-col :span="11">
<el-form-item label="真实姓名" prop="nickName">
<el-input v-model="userForm.nickName" :disabled="true"></el-input>
</el-form-item>
</el-col>
</el-row>
<!--<el-form-item label="密码" prop="password">-->
<!--<el-input type="password" v-model="userForm.password"></el-input>-->
<!--</el-form-item>-->
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="userForm.sex" style="margin-top: 8px;margin-left: 28px">
<el-radio v-model="userForm.sex" :label="1">男</el-radio>
<el-radio v-model="userForm.sex" :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="邮箱" prop="email" :rules="[
{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
]">
<el-input type="email" v-model="userForm.email"></el-input>
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="userForm.mobile"></el-input>
</el-form-item>
<el-form-item label="过敏史" prop="allergy">
<el-input type="textarea"
v-model="userForm.allergy"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(userForm)">修改</el-button>
<el-button type="success" @click="submitFormss">修改密码</el-button>
<el-button @click="resetForm()">重置</el-button>
</el-form-item>
</el-form>
<el-dialog
:title="title"
:visible.sync="dialogVisible"
:modal="model"
width="40%"
>
<div>
<el-form :model="ruleForm" :rules="rules" status-icon ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="原始密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" show-password autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="checkPass">
<el-input type="password" v-model="ruleForm.checkPass" show-password autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="pass2">
<el-input type="password" v-model="ruleForm.pass2" show-password autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm1('ruleForm')" >提交</el-button>
<el-button @click="resetForm1('ruleForm')">重置</el-button>
<el-button @click="resetForm12('ruleForm')">取消</el-button>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import {getUUID, policy} from "../../components/upload/policy";
export default {
name: "UserMessage",
props: {
value: String
},
data() {
var checkAge = (rule, value, callback) => {
// console.log(value.length);
if (!value) {
return callback(new Error('原始密码不能为空'));
}
if (value.length < 5) {
callback(new Error('密码必须大于6位数'));
} else {
callback();
}
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
}
callback();
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.checkPass) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
ruleForm: {
pass: '',
checkPass: '',
pass2: ''
},
rules: {
checkPass: [
{ validator: validatePass }
],
pass2: [
{ validator: validatePass2 }
],
pass: [
{ validator: checkAge }
]
},
token: this.getToken(),
userForm: {
id:'56',
name:'',//账号
nickName: '',//真实姓名
password: '',//密码
email: '',//邮箱
mobile: '',//电话
sex: 1,//性别
allergy: '',//过敏史
avatar:''
},
valueUrl:'',
title: '',//弹出层标题
dialogVisible: false, //是否弹出,弹出层
model: false, //取消弹出层遮掩层
dataObj: {
policy: '',
signature: '',
key: '',
ossaccessKeyId: '',
dir: '',
host: '',
// callback:'',
},
}
},
computed: {
imageUrl() {
return this.value;
},
imageName() {
if (this.value != null && this.value !== '') {
return this.value.substr(this.value.lastIndexOf("/") + 1);
} else {
return null;
}
},
fileList() {
return [{
name: this.imageName,
url: this.imageUrl
}]
},
showFileList: {
get: function () {
return this.value !== null && this.value !== ''&& this.value!==undefined;
},
set: function (newValue) {
}
}
},
mounted(){
this.userGr();
},
methods:{
emitInput(val) {
this.$emit('input', val)
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy().then(response => {
console.log(response)
_self.dataObj.policy = response.obj.policy;
_self.dataObj.signature = response.obj.signature;
_self.dataObj.ossaccessKeyId = response.obj.accessid;
_self.dataObj.key = response.obj.dir + '/'+getUUID()+'_${filename}';
_self.dataObj.dir = response.obj.dir;
_self.dataObj.host = response.obj.host;
resolve(true)
}).catch(err => {
reject(false)
})
})
},
handleUploadSuccess(res, file) {
console.log("上传成功...")
this.showFileList = true;
this.fileList.pop();
this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
this.emitInput(this.fileList[0].url);
},
submitForm1(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.postRequest('/passwrods?password='+this.ruleForm.pass+'&passwrod1='+this.ruleForm.pass2+'&id='+this.userForm.id).then(resp => {
if (resp.data.code==200){
window.sessionStorage.removeItem("token");
this.getToken();
}else {
this.$message({
type: 'error',
message: resp.data.message
});
}
});
} else {
console.log('error submit!!');
return false;
}
});
},
res(){
this.getToken();
},
resetForm1(formName) {
this.$refs[formName].resetFields();
},
resetForm12(formName) {
this.dialogVisible = false;
this.$refs[formName].resetFields();
},
submitForm(row){
var sReg = /[_a-zA-Z\d\-\.]+@[_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+$/;
if (sReg.test(row.email)) {
this.$confirm('此操作将修改个人信息, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.postRequest('/sysUser/upSysuser',row).then(resp => {
const tokenStr = resp.data.obj;
this.userForm=resp.data.obj;
window.sessionStorage.setItem('user', JSON.stringify(tokenStr));
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消修改'
});
});
}
return false;
},
resetForm(){
this.userForm.email='';
this.userForm.mobile='';
this.userForm.allergy='';
},
userGr(){
var loginInfo=JSON.parse(window.sessionStorage.getItem("user"));
var uRL = window.sessionStorage.getItem("avatar");
this.userForm=loginInfo;
this.valueUrl = uRL;
},
submitFormss(){
this.title = '修改密码';
this.dialogVisible = true;
}
,
}
}
</script>
<style>
.userContainer {
border-radius: 15px;
background-clip: padding-box;
margin: 40px auto;
width: 400px;
padding: 15px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.el-dropdown-links {
display: flex;
text-align: center;
line-height: 50px;
}
.el-dropdown-links img {
width: 100px;
height: 100px;
border-radius: 50px;
}
</style>
7.2 CURD
<template>
<div>
<div style="display: flex;justify-content: space-between">
<div>
<el-input
v-model="userName"
@keydown.enter.native="initUser"
clearable
@clear="initUser"
style="width: 300px;margin-right: 10px"
prefix-icon="el-icon-search"
placeholder="请输入姓名进行搜索..."/>
<el-button
type="primary"
icon="el-icon-search"
@click="initUser">搜索
</el-button>
<el-button
type="primary"
icon="el-icon-plus"
@click="addClick">添加
</el-button>
<el-button
@click="deleteClicks"
type="danger"
icon="el-icon-delete">批量删除
</el-button>
</div>
</div>
<div style="margin-top: 10px;width: 80%;">
<el-table
:data="tableData"
stripe
border
v-loading="loading"
element-loading-text="正在加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
style="width: 100%">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="uid"
label="Id"
fixed
align="left"
width="70">
</el-table-column>
<el-table-column
prop="uname"
label="姓名"
align="left"
width="120">
</el-table-column>
<el-table-column
prop="phone"
label="手机号码"
align="left"
width="180">
</el-table-column>
<el-table-column
prop="mailbox"
label="邮箱"
align="left"
width="180">
</el-table-column>
<el-table-column
prop="userDate"
label="注册时间"
align="left"
width="240">
</el-table-column>
<el-table-column
prop="userState"
label="状态"
align="left"
width="110">
<template slot-scope="scope">
<span v-if="scope.row.userState ==0">已禁用</span>
<span v-else-if="scope.row.userState ==1">已启用</span>
</template>
</el-table-column>
<el-table-column
label="操作"
fixed="right"
width="200">
<template slot-scope="scope">
<el-button @click="updateClick(scope.row)" style="padding: 3px" size="mini">编辑</el-button>
<el-button @click="deleteClick(scope.row)" style="padding: 3px" size="mini" type="danger">删除
</el-button>
</template>
</el-table-column>
</el-table>
<div style="display: flex;justify-content: flex-end">
<el-pagination
background
@current-change="currentChange"
@size-change="sizeChange"
:page-sizes="[2, 10, 20, 30]"
:page-size="2"
layout="sizes, prev, pager, next, jumper, ->, total"
:total="total">
</el-pagination>
</div>
</div>
<!--弹出框:添加--->
<div>
<el-dialog
:title="title"
:visible.sync="dialogVisible"
width="40%">
<div>
<el-form ref="userForm" :rules="rules" :model="userForm">
<el-row>
<el-col :span="12">
<el-form-item required label="姓名" prop="uname">
<el-input style="width: 200px" placeholder="请输入姓名"
v-model="userForm.uname"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="密码" prop="upassword">
<el-input style="width: 200px" placeholder="请输入密码"
v-model="userForm.upassword"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phone">
<el-input style="width: 200px" placeholder="请输入手机号码"
v-model="userForm.phone"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="邮箱" prop="mailbox">
<el-input style="width: 200px" placeholder="请输入邮箱"
v-model="userForm.mailbox"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisible = false">取 消</el-button>
<el-button size="small" type="primary" @click="doAdd">确 定</el-button>
</span>
</el-dialog>
</div>
<!--弹出框:编辑-->
<div>
<el-dialog
:title="title"
:visible.sync="dialogVisibles"
width="40%">
<div>
<el-form ref="userUpdateForm" :rules="rules" :model="userUpdateForm">
<el-row>
<el-col :span="12">
<el-form-item required label="姓名" prop="uname">
<el-input style="width: 200px" placeholder="请输入姓名"
v-model="userUpdateForm.uname"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="密码" prop="upassword">
<el-input style="width: 200px" placeholder="请输入密码"
v-model="userUpdateForm.upassword"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phone">
<el-input style="width: 200px" placeholder="请输入手机号码"
v-model="userUpdateForm.phone"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="邮箱" prop="mailbox">
<el-input style="width: 200px" placeholder="请输入邮箱"
v-model="userUpdateForm.mailbox"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogVisibles = false">取 消</el-button>
<el-button size="small" type="primary" @click="doUpdate">确 定</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
tableData: [{//表格数据
uid: '1',
uname: 'admin',
phone: '10086',
mailbox: '[email protected]',
userState: 1,
userDate: '2021-5-15',
}],
currentPage: 1,//第一页
size: 5,//显示数量
loading: false,//进度条
total: 0,//有几条数据
title: '',//弹出层标题
dialogVisible: false, //是否弹出,弹出层
dialogVisibles: false, //是否弹出,弹出层
userForm: {//添加数据
uname: '',
upassword: '',
phone: '',
mailbox: '',
},
userUpdateForm: {//修改数据
uid: '',
uname: '',
upassword: '',
phone: '',
mailbox: '',
},
rules: {//验证器
uname: [{required: true, message: '请输入姓名', trigger: 'blur'}],
upassword: [{required: true, message: '请输入密码', trigger: 'blur'}],
phone: [{required: true, message: '请输入电话号码', trigger: 'blur'}],
mailbox: [{required: true, message: '请输入邮箱地址', trigger: 'blur'}, {
type: 'email',
message: '邮箱地址格式不正确',
trigger: 'blur'
}],
},
userName: '',//搜索框
}
},
mounted() {
//刷新表格数据
this.initUser();
},
methods: {
//显示页数
sizeChange(size) {
this.size = size;
this.initUser();
},
//页数比如:第几页
currentChange(currentPage) {
this.currentPage = currentPage;
this.initUser();
},
//编辑
updateClick(data) {
// console.log(data);
this.title = "编辑信息";
this.dialogVisibles = true;
this.userUpdateForm = data;
},
//确定修改
doUpdate() {
// console.log(this.userForm)
this.$refs['userUpdateForm'].validate(valid => {
if (valid) {
this.putRequest('/user/put', this.userUpdateForm).then(resp => {
if (resp) {
this.dialogVisibles = false;
console.log(JSON.stringify(resp.data));
// this.userForm = {};
}
})
} else {
this.$message.error('请输入所有字段');
return false;
}
})
},
//删除
deleteClick(data) {
// console.log(row);
this.$confirm('此操作将永久删除 ' + data.uname + ', 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deleteRequest('/user/' + data.uid).then(resp => {
if (resp) {
console.log(JSON.stringify(resp.data));
//重载表格
this.initUser();
}
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
//添加
addClick() {
this.title = "添加信息";
this.dialogVisible = true;
this.userForm = {};
},
//批量删除
deleteClicks() {
},
//确定添加
doAdd() {
this.$refs['userForm'].validate(valid => {
if (valid) {
this.postRequest('/user/addUser/', this.userForm).then(resp => {
if (resp) {
this.dialogVisible = false;
this.initUser();
console.log(JSON.stringify(resp.data))
}
})
} else {
this.$message.error('请输入所有字段');
return false;
}
})
},
//表格数据
initUser() {
this.loading = true;
// console.log(this.currentPage + "--" + this.size + "--" + this.userName);
if (this.userName == '') {
this.getRequest('/user/page/?currentPage=' + this.currentPage + '&size=' + this.size).then(resp => {
this.loading = false;
if (resp) {
// console.log("数据:" + JSON.stringify(resp.data));
this.tableData = resp.data.data;//表格数据
this.total = resp.data.total;
}
})
} else {
this.getRequest('/user/pagelike/?currentPage=' + this.currentPage + '&size=' + this.size + '&name=' + this.userName).then(resp => {
this.loading = false;
if (resp) {
// console.log("数据2:" + JSON.stringify(resp.data));
this.tableData = resp.data.data;//表格数据
this.total = resp.data.total;
}
})
}
}
}
}
</script>
<style scoped>
</style>
版权归原作者 ☆夜幕星河℡ 所有, 如有侵权,请联系我们删除。