第二阶段(day22)Vue增删改查

1.菜单服务

1.1连接登录服务接口

①.导入sql文件

1.选中数据库test,右键选择运行SQL文件

2.选中路径导入

第二阶段(day22)Vue增删改查_第1张图片

3.刷新页面

pn_admin_menu 菜单表

第二阶段(day22)Vue增删改查_第2张图片

有一级菜单和二级菜单,主菜单和副菜单的属性很相似,故在一个表。

pn_admin_user 用户表

第二阶段(day22)Vue增删改查_第3张图片

看一下sql语句:

登录成功之后会先找用户的权限字段:

第二阶段(day22)Vue增删改查_第4张图片

找到该用户对应的权限菜单:

第二阶段(day22)Vue增删改查_第5张图片

权限字段拿到之后,会通过它查一级和二级菜单(一般不会超过二级),最后返回给页面的是嵌套的数据。但这样的数据,数据库给不了。

现在先查一级菜单:

第二阶段(day22)Vue增删改查_第6张图片

查一级菜单,也就是pid=0 对应的sql语句:select * from pn_admin_menu pam WHERE pam.pid

二级菜单:select * from pn_admin_menu pam WHERE pam.pid != 0

现在是查用户有的菜单:

如下是匹配的二级菜单:

第二阶段(day22)Vue增删改查_第7张图片

拿到登录成功的用户的一级菜单就可以遍历,找对应的二级菜单,然后塞进一级菜单。

②.导入后台服务接口

第二阶段(day22)Vue增删改查_第8张图片

导入的项目会重新创建一个项目,项目名这里用smStep2

第二阶段(day22)Vue增删改查_第9张图片

设置环境:

第二阶段(day22)Vue增删改查_第10张图片

引入项目的依赖:

第二阶段(day22)Vue增删改查_第11张图片

选择Library,选择Tomcat运行环境:

第二阶段(day22)Vue增删改查_第12张图片

其他服务接口继承BaseServlet。

这里的过滤器主要允许跨域请求和登录效验。

先把服务跑起来:

第二阶段(day22)Vue增删改查_第13张图片

点击+号后,选择Tomcat Serve里的Local,添加Tomcat实例。

创建Artifacts:

第二阶段(day22)Vue增删改查_第14张图片

 第二阶段(day22)Vue增删改查_第15张图片

选择完之后,点击ok

加载当前的项目:

第二阶段(day22)Vue增删改查_第16张图片

运行服务器。

试试这几个服务接口有没有问题:

先访问登录服务:http://localhost:8080/login?username=abc&pwd=abc

页面显示 {"returnCode":20001,"returnMsg":"用户名或密码错误"}

访问:http://localhost:8080/login?username=%E6%B5%8B%E8%AF%951&pwd=abc123

页面显示 {"returnCode":10000,"returnMsg":"操作成功"}

登录成功后,登录接口会将当前用户登录信息和用户权限信息放到session。

//登录访问控制
session.setAttribute("loginUser", loginUser);
//权限访问控制  展示菜单
session.setAttribute("userRoles", userRole);

然后需要取用户session信息时,取出来拼成json格式给界面返回。(GetMenuServlet接口)

访问:http://localhost:8080/getmenu

页面显示:

{"returnCode":10000,"returnData":[{"glyphicon":"el-icon-user","menuid":11,"menuname":"管理中心","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"glyphicon glyphicon-lock","menuid":11001,"menuname":"用户管理","menuurl":"/users","pid":11,"pname":"","submenu":[]},{"glyphicon":"glyphicon glyphicon-lock","menuid":11002,"menuname":"菜单管理","menuurl":"/menus","pid":11,"pname":"","submenu":[]}]},{"glyphicon":"el-icon-s-order","menuid":12,"menuname":"模版管理","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"","menuid":12001,"menuname":"模版配置","menuurl":"/template/cfglist","pid":12,"pname":"","submenu":[]},{"glyphicon":"","menuid":12002,"menuname":"模版列表","menuurl":"/template/templatelist","pid":12,"pname":"","submenu":[]}]},{"glyphicon":"el-icon-data-line","menuid":13,"menuname":"广告管理","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"","menuid":13001,"menuname":"首页置顶广告","menuurl":"/banner/toplist","pid":13,"pname":"","submenu":[]}]},{"glyphicon":"el-icon-timer","menuid":14,"menuname":"游戏管理","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"glyphicon glyphicon-eye-close","menuid":14001,"menuname":"游戏配置","menuurl":"/game/gamelist","pid":14,"pname":"","submenu":[]}]},{"glyphicon":"el-icon-ship","menuid":15,"menuname":"合作公司管理","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"glyphicon glyphicon-send","menuid":15001,"menuname":"合作公司管理","menuurl":"/partner/list","pid":15,"pname":"","submenu":[]}]},{"glyphicon":"el-icon-setting","menuid":16,"menuname":"渠道版本","menuurl":"#","pid":0,"pname":"","submenu":[{"glyphicon":"glyphicon glyphicon-screenshot","menuid":16001,"menuname":"渠道管理","menuurl":"/channel/list","pid":16,"pname":"","submenu":[]},{"glyphicon":"glyphicon glyphicon-bullhorn","menuid":16002,"menuname":"渠道分类管理","menuurl":"/channel/typelist","pid":16,"pname":"","submenu":[]}]}],"returnMsg":"操作成功"}

新建测试类MyTest:

按照①.的设想,写一下代码:

public class MyTest {
    public static void main(String[] args) {
        LoginDao ld = new LoginDaoImpl();
        //查询用户的权限信息 11001,11002,12001,12002,12003,13001,14001,14002,15001,16001,16002,19001,19002,11,12,13,14,15,16
​
        User user = new User();
        user.setUserId(9);
        String userRole = ld.getUserRole(user);
        MenuDao md = new MenuDaoImpl();
        //查一级菜单
        List lm1 = md.getMenuByLevel(0,userRole);
        //查二级菜单
        List lm2 = md.getMenuByLevel(1,userRole);
        System.out.println(lm1);
        System.out.println(lm2);
​
        System.out.println("------------------------------------");
        for (Menu menu1:lm1){     //遍历一级菜单
           for(Menu menu2:lm2){   //遍历二级菜单
               if(menu2.getPid().equals(menu1.getPid())){   //若二级菜单能和一级菜单匹配
                  menu1.getSubmenu().add(menu2);     //将当前二级菜单塞进去
               }
           }
        }
        System.out.println(lm1);
​
    }
}

控制台输出:

select mu.mid,mu.menuname,mu.pid,mu.url,mu.glyphicon from pn_admin_menu mu where mu.pid=0  and  mu.mid in (11001,11002,12001,12002,12003,13001,14001,14002,15001,16001,16002,19001,19002,11,12,13,14,15,16) 
select mu.mid,mu.menuname,mu.pid,mu.url,mu.glyphicon from pn_admin_menu mu where mu.pid!=0  and  mu.mid in (11001,11002,12001,12002,12003,13001,14001,14002,15001,16001,16002,19001,19002,11,12,13,14,15,16) 
[Menu{menuid=11, menuname='管理中心', menuurl='#', pid=0, pname='', glyphicon='el-icon-user', submenu=[]}, Menu{menuid=12, menuname='模版管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-s-order', submenu=[]}, Menu{menuid=13, menuname='广告管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-data-line', submenu=[]}, Menu{menuid=14, menuname='游戏管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-timer', submenu=[]}, Menu{menuid=15, menuname='合作公司管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-ship', submenu=[]}, Menu{menuid=16, menuname='渠道版本', menuurl='#', pid=0, pname='', glyphicon='el-icon-setting', submenu=[]}]
[Menu{menuid=11001, menuname='用户管理', menuurl='/users', pid=11, pname='', glyphicon='glyphicon glyphicon-lock', submenu=[]}, Menu{menuid=11002, menuname='菜单管理', menuurl='/menus', pid=11, pname='', glyphicon='glyphicon glyphicon-lock', submenu=[]}, Menu{menuid=12001, menuname='模版配置', menuurl='/template/cfglist', pid=12, pname='', glyphicon='', submenu=[]}, Menu{menuid=12002, menuname='模版列表', menuurl='/template/templatelist', pid=12, pname='', glyphicon='', submenu=[]}, Menu{menuid=13001, menuname='首页置顶广告', menuurl='/banner/toplist', pid=13, pname='', glyphicon='', submenu=[]}, Menu{menuid=14001, menuname='游戏配置', menuurl='/game/gamelist', pid=14, pname='', glyphicon='glyphicon glyphicon-eye-close', submenu=[]}, Menu{menuid=15001, menuname='合作公司管理', menuurl='/partner/list', pid=15, pname='', glyphicon='glyphicon glyphicon-send', submenu=[]}, Menu{menuid=16001, menuname='渠道管理', menuurl='/channel/list', pid=16, pname='', glyphicon='glyphicon glyphicon-screenshot', submenu=[]}, Menu{menuid=16002, menuname='渠道分类管理', menuurl='/channel/typelist', pid=16, pname='', glyphicon='glyphicon glyphicon-bullhorn', submenu=[]}, Menu{menuid=19001, menuname='标签列表', menuurl='/tag/taglist', pid=19, pname='', glyphicon='glyphicon glyphicon-tags', submenu=[]}]
------------------------------------
[Menu{menuid=11, menuname='管理中心', menuurl='#', pid=0, pname='', glyphicon='el-icon-user', submenu=[]}, Menu{menuid=12, menuname='模版管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-s-order', submenu=[]}, Menu{menuid=13, menuname='广告管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-data-line', submenu=[]}, Menu{menuid=14, menuname='游戏管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-timer', submenu=[]}, Menu{menuid=15, menuname='合作公司管理', menuurl='#', pid=0, pname='', glyphicon='el-icon-ship', submenu=[]}, Menu{menuid=16, menuname='渠道版本', menuurl='#', pid=0, pname='', glyphicon='el-icon-setting', submenu=[]}]
​

之前submenu是空的,现在就有了数据了。

最后只需做格式转换,然后把这样的菜单数据返回给前端,前端就可以遍历使用了。

③.使用postman

信息太多,用postman工具。

新建集合:

第二阶段(day22)Vue增删改查_第17张图片

起名newtest。

新建项目,new后选择Request:

第二阶段(day22)Vue增删改查_第18张图片

点击确定。

第二阶段(day22)Vue增删改查_第19张图片

点击send。

控制台输出:{"returnCode":10000,"returnMsg":"操作成功"}

新建queryUserMenu项目:

第二阶段(day22)Vue增删改查_第20张图片

submenu里放的子菜单数据。配合前端的vue,两层v-for循环就可将数据遍历出来。

第二个服务接口GetMenuServlet作用:取登录的时候查出来的菜单数据。菜单数据的组织是在登录时就已经放好,登录成功后直接从session里拿。

后端剩下的就是用户模块和菜单模块的增删改查。

新建queryMenu(菜单模块查询):

第二阶段(day22)Vue增删改查_第21张图片

可传page去翻页:http://localhost:8080/menu/query?page=2

再测试一下菜单的增加功能:

第二阶段(day22)Vue增删改查_第22张图片

对应的数据库里,菜单表多了一条数据。

后台服务接口已经开发结束,接下来是前端。

④.前端部分

打开视图界面工具可访问:http://localhost:8000/dashboard

打开HBUilder,将配置文件的路由改为打开页面先显示登录页面:

    {path:'/',redirect:"/login"} 

现在要使前端的登录和后台登录服务接口真正结合起来:

回到登录组件loginPage.vue:

如果验证成功,发Ajax请求去后台。找到后台登录服务接口地址(从postman复制,建的第一个项目就是登录),填入。

现在是跨域访问,需要绝对路径。

发送的参数要和后台搭配起来,后台用的是username和pwd。

同时数据是从界面拿过来的,且拿过来之后用qs转一下再发送

同时后端的登录服务接口也要允许它的跨域访问,修改一下:

跨域访问功能移到了过滤器,CheckLogin类:

resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8088");

再检查一下前端服务器地址:

第二阶段(day22)Vue增删改查_第23张图片

测试:

登录界面先来一个错的:

用户名:abcabc

密码:123456

第二阶段(day22)Vue增删改查_第24张图片

(Header请求头和响应头 Payload给后端传递的参数

Preview后端返回的参数 Response后台返回的响应)

返回的响应是:{"returnCode":20001,"returnMsg":"用户名或密码错误"}

再来个正确的:

用户名:测试1

密码:abc123

返回的响应:{"returnCode":10000,"returnMsg":"操作成功"}

至此跨域访问成功。

如果用户名或者密码错误,要有一个体现,让用户知道。

⑤.使用elementUI

组件里Notice部分的Message部分的不同状态。

this.$message.error('错了哦,这是一条错误消息');

在组件里添加:

.then((ret)=>{
console.log(ret);
if(ret.data.returnCode == 20001){
this.$message.error(returnMsg);
}
})

最后用户名密码正确则跳转到main组件。

.then((ret)=>{
console.log(ret);
if(ret.data.returnCode == 20001){
this.$message.error(ret.data.returnMsg);
}else if(ret.data.returnCode == 10000){
this.$router.push("/main");
}
​
})

使用脚手架之后,前端项目和后端项目完全隔离开了。

前端服务和后端服务之间没什么关联,就是两套应用程序通过http的异步请求做数据交互。

每个组件访问后端都要写完整的访问路径,很麻烦,axios提供了新的方法。(官网可看)

后台通过cookie保存浏览器和它的对应关系,故往后台发消息时,cookie也要允许去发送。

在main.js,加全局配置:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import qs from 'qs'
import axios from 'axios'
​
Vue.config.productionTip = false
​
/*为axios配置全局参数*/
//服务器路径
axios.defaults.baseURL="http://localhost:8080/"
//跨域带cookie
axios.defaults.withCredentials = true
​
Vue.prototype.$qs = qs;   
Vue.prototype.$axios = axios
​
new Vue({ 
  router,
  render: h => h(App)
}).$mount('#app')

然后发ajax时:

this.$axios.get("/login?"+this.$qs.stringify(this.form))

1.2动态菜单 配合后台接口做访问控制(GetMenuServlet接口)

①.登录成功后,发送axios请求,去请求GetMenuServlet接口。

mainpage组件:

请求不需要参数。

先打印出数据看看:

mounted(){
console.log("页面加载结束后执行");
​
this.$axios.get("/getmenu")
.then((ret)=>{
console.log(ret);    
})
.catch((err)=>{
console.log(err);   
});
​
}

后台返回的数据:

第二阶段(day22)Vue增删改查_第25张图片

假数据删除,留一个空集合:

data () {
return { 
menuList:[]
};
}

从poetman里复制一组该集合,改一下key:

          

    
    {{subm.menuname}}

                 
                  

接下来将取回的数据遍历到页面:

mounted(){
console.log("页面加载结束后执行");
this.$axios.get("/getmenu")
.then((ret)=>{
console.log(ret);
if(ret.data.returnCode==10000){
this.menuList = ret.data.returnData;
}
​
})
.catch((err)=>{
console.log(err);
});
​
}

效果如下:

第二阶段(day22)Vue增删改查_第26张图片

②.点击菜单后要换到对应的组件:

每个菜单有自己的url地址,后台存的访问地址实际是前端的组件地址。

第二阶段(day22)Vue增删改查_第27张图片

如上,用户管理对应/users组件,菜单管理对应/menus组件。

故前端组件需要把他俩加上。

为方便管理,在components目录下新建controlCenter(管理中心)目录。里面放两个模块,新建menus.vue和users.vue

点击对应菜单实际是往这两个模块跳。

先导入这两个组件,打开index.js:

import Users from '../components/controlCenter/menus.vue'
import Menus from '../components/controlCenter/menus.vue'

并配上对应的路由,和后台保存的地址搭配起来:

{path:'/users',component:Users},
{path:'/menus',component:Menus},

先在浏览器访问这两个组件,看有没有问题。

接下来就做点击跳到这两个组件:

在elementUI里的导航菜单有vue-router模式

加一个router属性。


          


{{subm.menuname}}

此时,点击导航条的管理中心部分的用户管理和菜单管理,会跳到相应的组件。

③.配子路由

跳到用户或者菜单管理,也应该是上左右布局。每个组件里相同的内容都要写一遍。对于这种问题,还是路由解决。路由可配子路由,即在当前组件里替换某些部分,而不是整个替换。

之前在App.vue里

是所有的东西都替换到了根组件App.vue里。

若想替换部分:

在mainPage.vue要替换的地方加router-view标签,将

Main

改为:


     

然后配置路由时,配成子路由的形式:

{path:'/main',component:Main,children:[
        {path:'/users',component:Users},
        {path:'/menus',component:Menus}
]},

子路由就是来配合替换部分页面的。

此时效果:

第二阶段(day22)Vue增删改查_第28张图片

④.加访问权限控制

后端有权限控制,前端却没有。

将后台服务器重启,登录信息没了。前端服务器访问菜单模块,后端返回响应,没有登录。

但前台可以随便敲,虽然后台数据不会给。

第二阶段(day22)Vue增删改查_第29张图片

没有登录,应该返回登录页面,这样和后台就一致了。

在mainPage组件里:

.then((ret)=>{
console.log(ret);
if(ret.data.returnCode==10000){
this.menuList = ret.data.returnData;
}else if(ret.data.returnCode==20000){
this.$router.push("/login");
}
​
})

虽然现在是两个服务器了,但前端服务常配合后台服务接口做一些功能。

但是不是所有的请求都要加else if这样一个处理?即其他组件发ajax请求时是否也要看下有没有登录,做对应的处理。故还是找个公共的方式,axios官网提供了。

第二阶段(day22)Vue增删改查_第30张图片

现在需要响应拦截器:

回到入口文件main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import qs from 'qs'
import axios from 'axios'
​
Vue.config.productionTip = false
​
/*为axios配置全局参数*/
//服务器路径
axios.defaults.baseURL="http://localhost:8080/"
//跨域带cookie
axios.defaults.withCredentials = true
​
//axios响应拦截器  配合后台登录访问控制
axios.interceptors.response.use(function (response) {
    //如果没有登录 returnCode==20000
    if(response.data.returnCode==20000){
        //跳转到登录组件
        router.push("/login");
    }
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });
​
Vue.prototype.$qs = qs;   
Vue.prototype.$axios = axios
​
new Vue({ 
  router,
  render: h => h(App)
}).$mount('#app')
​

此时可删除其他组件发送请求失败时的else if。

至此,将系统里的公共功能写完了,每套系统以上部分都差不多。

接下来就写具体模块的增删改查。

vue工具下如何处理增删改查。

1.3菜单管理模块的增删改查

第二阶段(day22)Vue增删改查_第31张图片

在menus组件加代码。

刚进该组件,应该先查现有的菜单信息。

1.3.1 查询功能

①.elementUI将表格拉过来:

菜单信息需要展示出来,故将table拉进来(之前讲过)。

(把代码复制一份)

②.绑定服务接口

要绑定的服务接口是MenuServlet(其配置地址是menu)

先发Ajax请求,并打印出返回的数据,看一下:

(参数可先不给,默认第一页,每页10条记录)

mounted(){
console.log("页面加载结束后执行");
this.$axios.get("/menu/query")
.then((ret)=>{
console.log(ret);
}).catch((err)=>{
console.log(err);
});
}

返回的数据如下:

第二阶段(day22)Vue增删改查_第32张图片

所有的后台数据都有响应码,前端处理时根据响应码做一下区分。

若响应码是10000,做数据解析。

mounted(){
console.log("页面加载结束后执行");
this.$axios.get("/menu/query")
.then((ret)=>{
/*
从返回的数据复制一份
data:
pageinfo: {page: 1, pagesize: 10, total: 29}
returnCode: 10000
returnData: Array(10)
0: {glyphicon: 'el-icon-user', menuid: 11, menuname: '管理中心', menuurl: '#', pid: 0, …}
1: {glyphicon: 'el-icon-s-order', menuid: 12, menuname: '模版管理', menuurl: '#', pid: 0, …}
2: {glyphicon: 'el-icon-data-line', menuid: 13, menuname: '广告管理', menuurl: '#', pid: 0, …}
3: {glyphicon: 'el-icon-timer', menuid: 14, menuname: '游戏管理', menuurl: '#', pid: 0, …}
4: {glyphicon: 'el-icon-ship', menuid: 15, menuname: '合作公司管理', menuurl: '#', pid: 0, …}
5: {glyphicon: 'el-icon-setting', menuid: 16, menuname: '渠道版本', menuurl: '#', pid: 0, …}
6: {glyphicon: 'el-icon-setting', menuid: 17, menuname: 'CP', menuurl: '#', pid: 0, …}
7: {glyphicon: 'el-icon-setting', menuid: 18, menuname: '图书管理', menuurl: '#', pid: 0, …}
8: {glyphicon: 'el-icon-setting', menuid: 19, menuname: '标签管理', menuurl: '#', pid: 0, …}
9: {glyphicon: 'el-icon-setting', menuid: 20, menuname: '分类管理', menuurl: '#', pid: 0, …}
*/
​
console.log(ret);
if(ret.data.returnCode==10000){
this.tableData = ret.data.returnData;
}
}).catch((err)=>{
console.log(err);
});
}

假数据前面已经测试过,页面正常显示,先删除假数据:

data() {
return {
tableData: []
};
}

③.根据后台返回的字段修改el-table的数据:

会发现返回的数据多了个父级菜单的名字pname字段。数据库里只有pid,但前端最终展示给用户看,用父级菜单更合适。上级菜单的名称查询如下:

对应的sql:

第二阶段(day22)Vue增删改查_第33张图片

把后台返回的字段在页面里修改一下:

效果如下:

第二阶段(day22)Vue增删改查_第34张图片

图标直接来个文本不好看。

上面不是自己加的v-for,他是自动遍历的,故table里想自己加数据,需要用到数据插头。

显示一个图标,再显示一个文本:

 
   
 

此时的代码:

④.分页功能

从elementUI的Data找分页组件,复制一份过来,复制到el-table下面:


将:current-page="currentPage4"先改成:current-page="1"

函数handleSizeChange页码改变和handleCurrentChange每页显示多少条改变的对应函数也拉进来。

methods: {
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
}
},

template标签里只能有一个分标签,故template里面最外层套一个div。

现在需要将分页组件和表格里的数据建立关系:

每次查询,后端发过来的数据都会有pageinfo

第二阶段(day22)Vue增删改查_第35张图片

需要将pageinfo和分页组件的相关数据搭配起来:

先在data里建一个pageinfo对象,顺便做一组假数据:

data() {
  return {
     tableData: [],
/* pageinfo:
page: 1
pagesize: 10
total: 29 */
      pageinfo:{
       page: 3,
       pagesize: 10,
       total: 660
      }
   };
}

再将拉进来的分页插件做相应修改:


发Ajax请求时,页码相关数据传给分页插件:

(使用json对象的另一个好处,如果返回的键值对和设置的json对象的键值对完全一样,整个对象给他就行,不需要一个一个值去给)

if(ret.data.returnCode==10000){
//展示table数据
this.tableData = ret.data.returnData;
//把页码相关数据传给分页组件
//this.pageinfo = ret.data.pageinfo;
}

现在组件按真数据走,但翻页的功能还要改。翻页就是往后端传要看第几页,每页显示多少条。

handleCurrentChange(val) {         //页数发生改变触发该函数
console.log(`当前页: ${val}`);    //可知当前要翻的页数
}

只需再传查询的Ajax即可,上面最开始就是查询,多个查询可合并成一个。

故再新建个查询函数,其他的调用它:

methods: {
        handleSizeChange(val) {
          console.log(`每页 ${val} 条`);
        },
        handleCurrentChange(val) {         //页数发生改变触发该函数
          console.log(`当前页: ${val}`);    //可知当前要翻的页数
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   //get方式可直接拼参数
            .then((ret)=>{
              /*
              从返回的数据复制一份
              data:
              pageinfo: {page: 1, pagesize: 10, total: 29}
              returnCode: 10000
              returnData: Array(10)
              0: {glyphicon: 'el-icon-user', menuid: 11, menuname: '管理中心', menuurl: '#', pid: 0, …}
              1: {glyphicon: 'el-icon-s-order', menuid: 12, menuname: '模版管理', menuurl: '#', pid: 0, …}
              2: {glyphicon: 'el-icon-data-line', menuid: 13, menuname: '广告管理', menuurl: '#', pid: 0, …}
              3: {glyphicon: 'el-icon-timer', menuid: 14, menuname: '游戏管理', menuurl: '#', pid: 0, …}
              4: {glyphicon: 'el-icon-ship', menuid: 15, menuname: '合作公司管理', menuurl: '#', pid: 0, …}
              5: {glyphicon: 'el-icon-setting', menuid: 16, menuname: '渠道版本', menuurl: '#', pid: 0, …}
              6: {glyphicon: 'el-icon-setting', menuid: 17, menuname: 'CP', menuurl: '#', pid: 0, …}
              7: {glyphicon: 'el-icon-setting', menuid: 18, menuname: '图书管理', menuurl: '#', pid: 0, …}
              8: {glyphicon: 'el-icon-setting', menuid: 19, menuname: '标签管理', menuurl: '#', pid: 0, …}
              9: {glyphicon: 'el-icon-setting', menuid: 20, menuname: '分类管理', menuurl: '#', pid: 0, …}
              */
             
              console.log(ret);
              if(ret.data.returnCode==10000){
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = ""
          this.myQuery(params);     //包含在vue对象里,直接调用即可。初次查询没有参数,给一个空值
      }

现在处理翻页:

handleCurrentChange(val) {         //页数发生改变触发该函数
  console.log(`当前页: ${val}`);    //可知当前要翻的页数
  this.pageinfo.page = val;         //将当前组件的page值设置成要翻页的页码    
  let params = this.$qs.stringify(this.pageinfo);   //传一个pageinfo对象给后台,后端拿需要的参数即可
  console.log(params);     //先看一下这个参数  
          
 }

点击第二页,触发该函数,控制台输出:

第二阶段(day22)Vue增删改查_第36张图片

参数没问题。

开始发参数:

handleCurrentChange(val) {         //页数发生改变触发该函数
console.log(`当前页: ${val}`);    //可知当前要翻的页数
this.pageinfo.page = val;         //将当前组件的page值设置成要翻页的页码    
let params = this.$qs.stringify(this.pageinfo);   //传一个pageinfo对象给后台,后端拿需要的参数即可
console.log(params);     //先看一下这个参数  
this.myQuery(params);    //要加this,不加this就找外部的全局参数。加this找vue里methods定义的函数。
}

现在翻页和跳页都正常了。

下拉列表里是页码数,点击20条/页,现在没反应,也需要传值查数据。

handleSizeChange(val) {
console.log(`每页 ${val} 条`);   
this.pageinfo.pagesize = val;   //选中每页需要展示的页码数
this.pageinfo.page = 1;   //切换pagesize之后,习惯将页数指定到第一页                  
let params = this.$qs.stringify(this.pageinfo);  
console.log(params);     
this.myQuery(params);
}

至此,翻页功能做完。

现在全部的代码:

页面效果:

第二阶段(day22)Vue增删改查_第37张图片

⑤.条件查询

后端MenuServlet条件查询需要接收的参数是qmname和qpid。故前端应该把这两个值传到后台。

name部分模糊查询,id部分精确查询。

到elementUI拉一个查询按钮和两个框。在Form表单部分。

复制的内容放到el-table上面:


  
    
  
  
    
      
      
    
  
  
    查询
  

修改内容:

绑定queryForm对象,queryForm传的两个值和后台搭配起来。

    
      
        
      
      
         
          
      
      
        查询
      
      

将对象创建出来:

queryForm:{
   qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
   qpid:''
}

将查询函数写出来:

queryMenu(){
let params = this.$qs.stringify(this.queryForm);    
this.myQuery(params);
}

测试:

模糊查询:

第二阶段(day22)Vue增删改查_第38张图片

再翻翻页(翻页也带条件)

翻页的时候,参数多添加一些:

handleSizeChange(val) {
  console.log(`每页 ${val} 条`);   
  this.pageinfo.pagesize = val;   //选中每页需要展示的页码数
  this.pageinfo.page = 1;   //切换pagesize之后,习惯将页数指定到第一页                    
  let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm);  
  console.log(params);     
  this.myQuery(params);
},
handleCurrentChange(val) {         //页数发生改变触发该函数
  console.log(`当前页: ${val}`);    //可知当前要翻的页数
  this.pageinfo.page = val;         //将当前组件的page值设置成要翻页的页码    
  let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm); ;   //传一个    pageinfo对象给后台,后端拿需要的参数即可
  console.log(params);     //先看一下这个参数  
  this.myQuery(params);    //要加this,不加this就找外部的全局参数。
}

再测,翻页时也带上了条件。

精确查询:

第二阶段(day22)Vue增删改查_第39张图片

再测个父菜单0的,并且翻翻页。

再测菜单名称:管理234,父菜单:0。找个没数据的。

⑥.查询改成下拉列表

两个输入框使用不方便。将数据库里父id有的数据放在父菜单做成下拉列表。

还是刚才Form表单的那个例子,将相关代码复制进来:

    
      
        
      
       
        
              
              
            
          
      
      
        查询
      
    

效果如下:

第二阶段(day22)Vue增删改查_第40张图片

无是特殊的一个。不需要从后台拿。

下拉列表的相关数据从后台获取:

第二阶段(day22)Vue增删改查_第41张图片

对应的在MenuServlet里有getmenuselect方法。

在postman里测一下:

第二阶段(day22)Vue增删改查_第42张图片

将菜单数据的方法定义下:

将拿到的数据传给selectOptions

getMenuSelect(){
  this.$axios.get("/menu/getmenuselect")
  .then((ret)=>{
  console.log(ret);
  if(ret.data.returnCode==10000){
//展示table数据
  this.selectOptions = ret.data.returnData;
​
  }
}).catch((err)=>{
  console.log(err);
  });
}
}

相应的data里增加:

selectOptions:[],

回到下拉列表,需要增加的选项用v-for从selectOptions里遍历拿数据。

label里写名称(给用户看),value里写值(往后台做查询用)。


      
        
      
       
        
              
              
            
          
      
      
        查询
      
    

页面加载结束后,拿下拉列表的数据:

 mounted(){
 console.log("页面加载结束后执行");
 let params = "";
 this.myQuery(params); 
 this.getMenuSelect();  
 }

⑦.重置查询表单

增加一个重置按钮,清空el-form表单数据。

首先给el-form加ref属性,将组件注册上去。

然后加一个点击事件,将注册后的名字以参数形式传入。


      
        
      
       
        
              
              
            
          
      
      
        查询
        重置
      
    

增加重置函数:

resetForm(formname){
this.$refs[formname].resetFields();
}

form-item上需要有prop属性:


      
        
      
        
        
              
              
            
          
      
      
        查询
        重置
      
      

查询功能结束,代码如下:

效果如下:

第二阶段(day22)Vue增删改查_第43张图片

1.3.2添加功能

增加添加按钮,添加信息时弹出弹出框。

①.elementUI拉过弹出框

在Others的Dialog,复制相关代码:

放到div里的最下层:

打开嵌套表单的 Dialog
        
          
            
              
            
            
              
                
                
              
            
          
          
        

按钮还是放上面:

换一下添加按钮的样式

查询
重置
打开嵌套表单的 Dialog

关键的值在data里定义一下:

dialogFormVisible : false,
formLabelWidth: '120px'

新拉进来的代码还绑定了form,故在data里,定义绑定的数据:(改成addForm)

addForm:{
myval1:'',
myval2:''
}

上面的数据将form也全部改成addForm,并修改相应参数:


          
            
              
            
            
              
                
                
              
            
          
          
        

dialogFormVisible太长,以上全部换成addFormVisible

将添加按钮的内容改成添加

添加

设置弹出框的内容:

父id应该做成下拉列表。


        
        
          
            
              
            
            
              
            
            
              
                 
            
                
                    
                    
                
            
            
              
             
          
          
        

将假数据改成真数据:

/* String menuid = req.getParameter("menuid");
String menuname = req.getParameter("menuname");
String menuurl = req.getParameter("menuurl");
String pid = req.getParameter("pid");
String glyphicon = req.getParameter("glyphicon"); */
  addForm:{
     menuid:'',
     menuname:'',
     menuurl:'',
     pid:'',
     glyphicon:'',
  }

效果如下:

第二阶段(day22)Vue增删改查_第44张图片

修改父菜单样式:

同时在样式标签里:

.el-select{
    width:100%;
}

点击确定后,应往后台发送,也做成一个函数:

确 定

对应函数:

addFormSubmit(){
   this.addFormVisible = false; //让弹出框关闭
//this.$qs.stringify(this.addForm);   //取addForm数据,并格式化
   this.$axios.post("/menu/add",this.$qs.stringify(this.addForm))
   .then((ret)=>{  //先和接口接通
​
    }).catch((err)=>{
​
    })
 }

随便写一组数据,点击确定,数据库添加成功。

②.做出弹出框后,添加数据结束,使页面相应改变

添加请求成功,但页面没反应。故干两个事:

1.数据刷新(实际是重新查询一遍) 2.打印出执行操作的结果(用提示框)。

接收到后台的响应代码后,做出不同的提示框(代码从elementUI取)。

this.$axios.post("/menu/add",this.$qs.stringify(this.addForm))
.then((ret)=>{  //先和接口接通
    //根据响应 提示操作的结果
  if(ret.data.returnCode==10000){
      this.$message(ret.data.returnMsg);
  }else{
      this.$message.error(ret.data.returnMsg);
  } 
  }).catch((err)=>{
​
  })

刷新数据:

this.$axios.post("/menu/add",this.$qs.stringify(this.addForm))
.then((ret)=>{  //先和接口接通
//根据响应 提示操作的结果
    if(ret.data.returnCode==10000){
       this.$message({
          message:ret.data.returnMsg,
          type: 'success'
     });
    }else{
       this.$message.error(ret.data.returnMsg);
    }   
    //刷新数据
   let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm);    
   this.myQuery(params);
}).catch((err)=>{
​
})

③.一个问题:添加完成以后,父菜单的下拉列表没有跟着动。

第二阶段(day22)Vue增删改查_第45张图片

因为该下拉列表是页面加载结束取的。添加完数据没有再取数据。故刷新数据也要刷新父菜单的下拉列表数据:

this.$axios.post("/menu/add",this.$qs.stringify(this.addForm))
    .then((ret)=>{  //先和接口接通
    //根据响应 提示操作的结果
        if(ret.data.returnCode==10000){
            this.$message({
              message:ret.data.returnMsg,
              type: 'success'
        });
        }else{
            this.$message.error(ret.data.returnMsg);
        }   
    //刷新数据
    let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm);   
    this.myQuery(params);
    //刷新下拉列表数据:
    this.getMenuSelect();
    }).catch((err)=>{
                
    })

添加结束后,再添加数据时,上一次的数据还在。应该再次点击添加按钮时,弹出框的数据清空。

找到添加按钮:

添加

找到表格,注册一下addForm,并给下面的表格项增加prop属性:


          
            
              
            
            
              
            
            
              
                 
            
                
                    
                    
                
            
            
              
             
          
          
        

在下面定义该函数:

addFormOpen(formName){
 this.addFormVisible = true;
 this.$refs[formName].resetFields();
}

④.功能正常,但还报错

虽然可以正常运行,但这里报错

第二阶段(day22)Vue增删改查_第46张图片

意思是this.$refs[formName]是空的,再拿空对象调resetFields()。类似java里的空指针异常。

组件已经注册上去,怎么会空?

这是vue机制问题,它的渲染过程是自动的,交给它的代码的执行顺序不一定是我们编写的代码的顺序。

使用这个对象时,组件还没注册,故报错。

解决办法:

addFormOpen(formName){
   this.addFormVisible = true;
   this.$nextTick(function(){    //渲染时机后移,做完一次渲染后再执行括号里的代码
   this.$refs[formName].resetFields();
 }) 
}

增加功能结束,代码如下:

1.3.3修改功能

①.定位当前行数据

法一:每一行记录加一个修改按钮

法二:使table变成可选择的效果

这里用法二,elementUI的Table表格,复制相关代码(单选):

Table 组件提供了单选的支持,只需要配置highlight-current-row属性即可实现单选。之后由current-change事件来管理选中时触发的事件,它会传入currentRowoldCurrentRow。如果需要显示索引,可以增加一列el-table-column,设置type属性为index即可显示从 1 开始的索引号。

在页面显示的table标签上:

定义该方法:

该方法名被翻页占了,改成selectRowChange

selectRowChange(val){         //参数是当前改变的这行数据
this.currentRow = val;
}

data里定义该对象:

currentRow: null

按钮那块没传参,改成如下,就是用它的默认参数:

@current-change="handleCurrentChange"

现在行就选中了。再加一个按钮:

修改

②.将选中行数据填到修改框里

editForm和添加很像。

直接复制一份addForm,做修改即可。

菜单编号不让修改,只读。


          
            
              
            
            
              
            
            
              
                 
            
                
                    
                    
                
            
            
              
             
          
          
        

下面对应的数据:

将data里addForm复制一份修改:

editForm:{
   menuid:'',
   menuname:'',
   menuurl:'',
   pid:'',
   glyphicon:'',
},
editFormVisible : false,

点击修改按钮,先让它的弹出框出来:

editFormOpen(){
  this.editFormVisible = true;
}

接下来将选中的数据填到弹出框:

第二阶段(day22)Vue增删改查_第47张图片

即将选中的数据填到editForm字段里:

editFormOpen(){
this.editFormVisible = true;
this.editForm = this.currentRow;   //将选中行的数据对象填到editForm里
}

选中不同的行,修改框里的父菜单内容有点问题:

这是数据类型的问题:

第二阶段(day22)Vue增删改查_第48张图片

select里,0是自己拼的。其他的值是动态生成的。

使用value数据绑定时,后台给的格式和这个格式对应,现在使用menuid实际是数字类型。这个选项和它的类型没有匹配上。

这里加个冒号,变成数值绑定,里面写的js代码,0就是数字0.(第一天讲value时的问题)


    
    

③.修改数据

第二阶段(day22)Vue增删改查_第49张图片

修改数据,还没传到后台,页面数据就改变了。

这也是vue里的一个机制,vue会把这些对象监控起来

第二阶段(day22)Vue增删改查_第50张图片

里面有一个ob,即Observer。表示对象被监控了,并且每个对象会起一个对应的对象编号。这个对象是遍历数据时生成的。

editFormOpen(){
            this.editFormVisible = true;
            //将选中行的数据对象填到editForm里
            //this.editForm = this.currentRow;   //这里实际只传了对象的引用,故editForm里的数据修改,页面数据也会改。他们指向同一个引用,并且做了数据双向绑定
            //java里也有同样的问题,深克隆和浅克隆
            
            //console.log(JSON.stringify(this.currentRow));     //json格式字符串化,且是标准的json格式
            //console.log(JSON.parse(JSON.stringify(this.currentRow)));  //将对象转成字符串,再转回来,就新创建了一个对象,虽然数据还是一样的
            //this.editForm = this.currentRow;   //用新的对象赋值给editForm。这就是js里的深克隆
            
            this.editForm = JSON.parse(JSON.stringify(this.currentRow));  //可写成一句话
        }

接下来点击修改弹出框的确定按钮,将数据从editFrom里拿到,传给后台,后台保存起来。

editFormSubmit函数类似addFormSubmit:

editFormSubmit(){
   this.editFormVisible = false; //让弹出框关闭
   this.$axios.post("/menu/edit",this.$qs.stringify(this.editForm))
   .then((ret)=>{  
      if(ret.data.returnCode==10000){
        this.$message({
          message:ret.data.returnMsg,
          type: 'success'
      });
      }else{
        this.$message.error(ret.data.returnMsg);
      } 
      //刷新数据
let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm);   
this.myQuery(params);
//刷新下拉列表数据:
this.getMenuSelect();
}).catch((err)=>{
​
})
}

除了参数不同,都一致。

再加一个函数,写公共代码:

myCUD(url,params){
    this.$axios.post(url,params)
    .then((ret)=>{  //先和接口接通
    //根据响应 提示操作的结果
    if(ret.data.returnCode==10000){
        this.$message({
            message:ret.data.returnMsg,
            type: 'success'
        });
     }else{
        this.$message.error(ret.data.returnMsg);
        }   
    //刷新数据
    let params = this.$qs.stringify(this.pageinfo)+"&"+this.$qs.stringify(this.queryForm);   
    this.myQuery(params);
    //刷新下拉列表数据:
    this.getMenuSelect();
    }).catch((err)=>{
                
    })
}

修改:

editFormSubmit(){
this.editFormVisible = false; //让弹出框关闭
let url = "/menu/edit";
let params = this.$qs.stringify(this.editForm);
this.myCUD(url,params);
​
}

添加:

addFormSubmit(){
this.addFormVisible = false; //让弹出框关闭
let url = "/menu/add";
let params = this.$qs.stringify(this.addForm);
this.myCUD(url,params);
​
}

此时,整体代码如下:

效果如下:

第二阶段(day22)Vue增删改查_第51张图片

1.3.4删除功能

定位一条数据(已有),并做一个删除数据的展示(不用弹出框)。

①.复制小弹出框的代码

elementUI里的Notice里的MessageBox的确认信息.

添加进按钮:

删除

将removeMenu()方法添加:

removeMenu(){   
this.$confirm('此操作将删除编号为 '+this.currentRow.menuid+' 的记录, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning' 
    }).then(() => {      //表示成功,即点击了确定按钮
    this.$message({
        type: 'success',
        message: '删除成功!'
        });
    }).catch(() => {     //表示失败
        this.$message({
        type: 'info',
        message: '已取消删除'
    });          
});
}

点击确定按钮后的内容自己改下:

应该将要删除的id传到后台的删除接口

删除完还是刷新对应的数据。

removeMenu(){   
         this.$confirm('此操作将删除编号为 '+this.currentRow.menuid+' 的记录, 是否继续?', '提示', {
                  confirmButtonText: '确定',
                  cancelButtonText: '取消',
                  type: 'warning' 
                }).then(() => {      //表示成功,即点击了确定按钮
                  //把id发到后台删除
                  console.log(this.currentRow.menuid);
                  let url = "/menu/remove";
                  let params = "menuid="+this.currentRow.menuid;
                  this.myCUD(url,params);
                  
                }).catch(() => {     //表示失败
                  this.$message({
                    type: 'info',
                    message: '已取消删除'
                  });          
                });
        }

单个模块的增删改查就写完了。

代码如下:

1.3.5最后一个功能

修改和删除按钮,只有选中记录才会生效。不选中直接点会报错,这里优化一下。

没选中记录,这两个按钮禁用。

这两个按钮的disabled属性本来是true,点击一行之后变成false。

需要属性绑定的方式去写。

先加两个disabled的属性:

修改
删除

在data里增加:

btnstatus:true

点击一行能用,找selectRowChange函数,

selectRowChange(val){         //参数是当前改变的这行数据
this.currentRow = val;
this.btnstatus = false;
}

使用vue之后,如何考虑页面效果的切换。做页面绑定。

现在翻页之后,按钮还可以选择,应该变回true。选择每页的记录数后,也应该变为true。

也就是凡是页面重新查询后,按钮都该禁用。

      myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  this.btnstatus = true;     //按钮禁用
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        }

好像不太好用,有时候可行,有时候不行。

还是渲染时机问题。

this.$nextTick(function(){
this.btnstatus = true;     //按钮禁用
})

现在不好使了。

因为nextTick只是把渲染时机后移,具体移到哪次渲染之后没法控制。

现在使用vue的监控机制,可精确找到代码要执行的时机。

数据刷新时,会取消掉选中的行,也就是table重新生成的时候。

watch:{   //监控指定的数据
          tableData:function(){   //刷新数据后自动触发
              console.log("tableData数据渲染完成了");
              this.$nextTick(function(){
                this.btnstatus = true;     //按钮禁用
              })
          }
      }

和mounted(钩子函数)是同级的。

最终的代码:

2.用户服务

你可能感兴趣的:(第二阶段,java)