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

1.菜单服务

1.1连接登录服务接口

①.导入sql文件

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

2.选中路径导入

3.刷新页面

pn_admin_menu 菜单表

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

pn_admin_user 用户表

看一下sql语句:

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

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

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

现在先查一级菜单:

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

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

现在是查用户有的菜单:

如下是匹配的二级菜单:

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

②.导入后台服务接口

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

设置环境:

引入项目的依赖:

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

其他服务接口继承BaseServlet。

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

先把服务跑起来:

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

创建Artifacts:

 

选择完之后,点击ok

加载当前的项目:

运行服务器。

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

先访问登录服务: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<Menu> lm1 = md.getMenuByLevel(0,userRole);
        //查二级菜单
        List<Menu> 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工具。

新建集合:

起名newtest。

新建项目,new后选择Request:

点击确定。

点击send。

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

新建queryUserMenu项目:

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

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

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

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

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

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

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

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

④.前端部分

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

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

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

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

回到登录组件loginPage.vue:

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

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

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

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

<template>
    <div>
      <el-card class="box-card mycard">
        <div slot="header" class="clearfix">
          <span>登录</span>
        </div>
        
        <div class="text item">
          <el-form ref="myform" :rules="rules" :model="form" label-width="80px" hide-required-asterisk>
            
            <el-form-item label="用户名" prop="username">
              <el-input v-model="form.username" suffix-icon="el-icon-user"></el-input>
            </el-form-item>
          
          <el-form-item label="密码" prop="pwd">
            <el-input v-model="form.pwd" show-password suffix-icon="el-icon-lock"></el-input>
          </el-form-item>
          
            <el-form-item>
              <el-button type="primary" @click="mySubmit()">登录</el-button>
              <el-button @click="resetForm()">重置</el-button>
            </el-form-item>
          
          </el-form>
        </div>
      </el-card>
    </div>
</template>
​
<script> 
    
    export default {
                methods: {
                mySubmit(){
                    //$refs 注册给vue的组件
                    console.log(this);
                    this.$refs['myform'].validate((valid) => {
                              if (valid) {
                                console.log("提交数据");
                                console.log(this.$qs.stringify(this.form));
                                
                                //跨域访问用绝对路径,参数要和后端匹配。同时数据是从界面拿过来的,且拿过来之后用qs转一下再发送
                                console.log(this.$axios.get("http://localhost:8080/login?"+this.$qs.stringify(this.form))
                                .then((ret)=>{
                                    console.log(ret);
                                })
                                .catch((err)=>{
                                    console.log(err);   //若有错误,打印出来
                                }));
                                
                              } else {
                                console.log('数据有误 不能提交');
                                return false;
                              }
                    }); 
                   },
                    resetForm(formName) {
                            this.$refs['myform'].resetFields();    //通过this.$refs['myform']找到组件,调用resetFields方法清空数据
                          }
                   
                }, 
                data () {
                  return { 
                    form: {
                              username: '',
                              pwd:''
                            },
                    rules: {
                              pwd: [
                                { required: true, message: '请输入密码', trigger: 'blur' },
                                { min: 6, max: 8, message: '长度在 6 到 8 个字符', trigger: 'blur' }
                              ]
                            }  
                  };
            }   
        }
</script>
​
<style>
    .text {
      font-size: 14px;
    }
            
    .item {
      margin-bottom: 18px;
    }
            
    .clearfix:before,
    .clearfix:after {
      display: table;
      content: "";
    }
    .clearfix:after {
      clear: both
    }
            
    .box-card {
      width: 480px;
    }
    
    .mycard{
                  margin:240px auto;
    }
</style>

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

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

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

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

测试:

登录界面先来一个错的:

用户名:abcabc

密码:123456

(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);   
});
​
}

后台返回的数据:

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

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

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

<!--给不同用户展示不同的菜单,主要功能点还是在后台,后台根据用户的权限给前台准备好查询菜单的服务接口-->          
<el-submenu v-for="menu in menuList" :index="menu.menuid.toString()">
    <template slot="title">
<i :class="menu.glyphicon"></i>
<span>{{menu.menuname}}</span>
    </template>
    <el-menu-item v-for="subm in menu.submenu" :index="subm.menuurl">{{subm.menuname}}</el-menu-item>
</el-submenu>
                 
                 <!--
                 "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": []
                 }  
                 ]
​
--> 

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

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);
});
​
}

效果如下:

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

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

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

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

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

<template>
    <div>
        用户模块
    </div>
</template>
​
<script>
</script>
​
<style>
</style>
<template>
    <div>
        菜单模块
    </div>
</template>
​
<script>
</script>
​
<style>
</style>
​

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

先导入这两个组件,打开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属性。

<el-col :span="24">
<el-menu
router
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b">
​
<!--给不同用户展示不同的菜单,主要功能点还是在后台,后台根据用户的权限给前台准备好查询菜单的服务接口-->          
<el-submenu v-for="menu in menuList" :index="menu.menuid.toString()">
<template slot="title">
<i :class="menu.glyphicon"></i>
<span>{{menu.menuname}}</span>
</template>
<el-menu-item v-for="subm in menu.submenu" :index="subm.menuurl">{{subm.menuname}}</el-menu-item>
</el-submenu>

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

③.配子路由

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

之前在App.vue里

<template>
  <router-view></router-view>
</template>
​
<script>
   
</script>
​
<style>
​
</style>

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

若想替换部分:

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

<el-main>Main</el-main>

改为:

<el-main>
   <router-view></router-view>  
</el-main>

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

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

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

此时效果:

④.加访问权限控制

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

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

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

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

在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官网提供了。

现在需要响应拦截器:

回到入口文件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菜单管理模块的增删改查

在menus组件加代码。

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

1.3.1 查询功能

①.elementUI将表格拉过来:

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

(把代码复制一份)

<template>
<el-table
      :data="tableData"
      style="width: 100%">
      <el-table-column
        prop="date"
        label="日期"
        width="180">
      </el-table-column>
      <el-table-column
        prop="name"
        label="姓名"
        width="180">
      </el-table-column>
      <el-table-column
        prop="address"
        label="地址">
      </el-table-column>
    </el-table>
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [{
            date: '2016-05-02',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄'
          }, {
            date: '2016-05-04',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1517 弄'
          }, {
            date: '2016-05-01',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1519 弄'
          }, {
            date: '2016-05-03',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1516 弄'
          }]
        }
      }
    }
</script>
​
<style>
</style>

②.绑定服务接口

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

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

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

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

返回的数据如下:

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

若响应码是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:

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

<template>
<el-table
      :data="tableData"
      style="width: 100%">
      
      <!--
      glyphicon: "el-icon-user"
      menuid: 11
      menuname: "管理中心"
      menuurl: "#"
      pid: 0
      pname: "无"
       
      -->
      <el-table-column
        prop="menuid"
        label="菜单编号">
      </el-table-column>
      <el-table-column
        prop="menuname"
        label="菜单名称">
      </el-table-column>
      <el-table-column
        prop="menuurl"
        label="访问地址">
      </el-table-column>
      <el-table-column
        prop="pid"
        label="上级菜单编号">
      </el-table-column>
      <el-table-column
        prop="pname"
        label="上级菜单名称">
      </el-table-column>
      <el-table-column
        prop="glyphicon"
        label="菜单图标">
      </el-table-column>
        
    </el-table>
</template>

效果如下:

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

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

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

 <el-table-column
   label="菜单图标">
   <template v-slot="mydata"> 
       <i :class="mydata.row.glyphicon"></i>
       {{mydata.row.glyphicon}}
 </template>
 </el-table-column>

此时的代码:

<template>
<el-table
      :data="tableData"
      style="width: 100%">
      
      <!--
      glyphicon: "el-icon-user"
      menuid: 11
      menuname: "管理中心"
      menuurl: "#"
      pid: 0
      pname: "无"
       
      -->
      <el-table-column
        prop="menuid"
        label="菜单编号">
      </el-table-column>
      <el-table-column
        prop="menuname"
        label="菜单名称">
      </el-table-column>
      <el-table-column
        prop="menuurl"
        label="访问地址">
      </el-table-column>
      <el-table-column
        prop="pid"
        label="上级菜单编号">
      </el-table-column>
      <el-table-column
        prop="pname"
        label="上级菜单名称">
      </el-table-column>
      <el-table-column
        label="菜单图标">
        <template v-slot="mydata"> 
            <i :class="mydata.row.glyphicon"></i>
            {{mydata.row.glyphicon}}
        </template>
      </el-table-column>
        
    </el-table>
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: []
        };
      },
      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);
          });
      }
    }
</script>
​
<style>
</style>
​

④.分页功能

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

<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage4"
:page-sizes="[100, 200, 300, 400]"
:page-size="100"
layout="total, sizes, prev, pager, next, jumper"
:total="400">
</el-pagination>

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

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

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

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

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

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

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

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

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

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

<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageinfo.page"
:page-sizes="[10, 20, 30]"
:page-size="pageinfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="pageinfo.total">
</el-pagination>

发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);     //先看一下这个参数  
          
 }

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

参数没问题。

开始发参数:

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);
}

至此,翻页功能做完。

现在全部的代码:

<template>
    <div>
    <el-table
          :data="tableData"
          style="width: 100%">
          
          <!--
          glyphicon: "el-icon-user"
          menuid: 11
          menuname: "管理中心"
          menuurl: "#"
          pid: 0
          pname: "无"
           
          -->
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column
            label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>
                {{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          }
        };
      },
      methods: {
        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);
        },
        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就找外部的全局参数。
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              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);     
      }
    }
</script>
​
<style>
</style>
​

页面效果:

⑤.条件查询

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

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

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

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

<el-form :inline="true" :model="formInline" class="demo-form-inline">
  <el-form-item label="审批人">
    <el-input v-model="formInline.user" placeholder="审批人"></el-input>
  </el-form-item>
  <el-form-item label="活动区域">
    <el-select v-model="formInline.region" placeholder="活动区域">
      <el-option label="区域一" value="shanghai"></el-option>
      <el-option label="区域二" value="beijing"></el-option>
    </el-select>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="onSubmit">查询</el-button>
  </el-form-item>
</el-form>

修改内容:

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

    <el-form :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单">
        <el-input v-model="queryForm.qpid" placeholder="pid"></el-input> 
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
      </el-form-item>
    </el-form>  

将对象创建出来:

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

将查询函数写出来:

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

测试:

模糊查询:

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

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

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就找外部的全局参数。
}

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

精确查询:

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

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

⑥.查询改成下拉列表

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

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

    <el-form :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单"> 
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option label="区域二" value="beijing"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
      </el-form-item>
    </el-form>

效果如下:

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

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

对应的在MenuServlet里有getmenuselect方法。

在postman里测一下:

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

将拿到的数据传给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里写值(往后台做查询用)。

<el-form :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单"> 
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
      </el-form-item>
    </el-form>

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

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

⑦.重置查询表单

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

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

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

<el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单"> 
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
      </el-form-item>
    </el-form>

增加重置函数:

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

form-item上需要有prop属性:

<el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
      </el-form-item>
    </el-form>  

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

<template>
    <div>
    <el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
      </el-form-item>
    </el-form>  
        
    <el-table
          :data="tableData"
          style="width: 100%">
          
          <!--
          glyphicon: "el-icon-user"
          menuid: 11
          menuname: "管理中心"
          menuurl: "#"
          pid: 0
          pname: "无"
           
          -->
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column
            label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>
                {{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          selectOptions:[],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          },
          queryForm:{
              qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
              qpid:''
          }
          
          
        };
      },
      methods: {
        resetForm(formname){
            this.$refs[formname].resetFields();
        },  
        queryMenu(){
        let params = this.$qs.stringify(this.queryForm);    
        this.myQuery(params);
        },  
        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就找外部的全局参数。
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        },
        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);
            });
         }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = "";
          this.myQuery(params); 
          this.getMenuSelect();  
      }
    }
</script>
​
<style>
</style>
​

效果如下:

1.3.2添加功能

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

①.elementUI拉过弹出框

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

放到div里的最下层:

<el-button type="text" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>
        <el-dialog title="收货地址" :visible.sync="dialogFormVisible">
          <el-form :model="form">
            <el-form-item label="活动名称" :label-width="formLabelWidth">
              <el-input v-model="form.name" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="活动区域" :label-width="formLabelWidth">
              <el-select v-model="form.region" placeholder="请选择活动区域">
                <el-option label="区域一" value="shanghai"></el-option>
                <el-option label="区域二" value="beijing"></el-option>
              </el-select>
            </el-form-item>
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
          </div>
        </el-dialog>

按钮还是放上面:

换一下添加按钮的样式

<el-button type="primary" @click="queryMenu()">查询</el-button>
<el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
<el-button type="success" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>

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

dialogFormVisible : false,
formLabelWidth: '120px'

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

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

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

<el-dialog title="收货地址" :visible.sync="dialogFormVisible">
          <el-form :model="addForm">
            <el-form-item label="活动名称" :label-width="formLabelWidth">
              <el-input v-model="addForm.myval1" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="活动区域" :label-width="formLabelWidth">
              <el-select v-model="addForm.myval2" placeholder="请选择活动区域">
                <el-option label="区域一" value="shanghai"></el-option>
                <el-option label="区域二" value="beijing"></el-option>
              </el-select>
            </el-form-item>
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
          </div>
        </el-dialog>

dialogFormVisible太长,以上全部换成addFormVisible

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

<el-button type="success" @click="dialogFormVisible = true">添加</el-button>

设置弹出框的内容:

父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"); -->
        
        <el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormVisible = false">确 定</el-button>
          </div>
        </el-dialog>

将假数据改成真数据:

/* 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:'',
  }

效果如下:

修改父菜单样式:

<el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">

同时在样式标签里:

.el-select{
    width:100%;
}

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

<el-button type="primary" @click="addFormSubmit()">确 定</el-button>

对应函数:

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)=>{
​
})

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

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

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)=>{
                
    })

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

找到添加按钮:

<el-button type="success" @click="addFormOpen('addForm')">添加</el-button>

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

<el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form ref="addForm" :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>

在下面定义该函数:

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

④.功能正常,但还报错

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

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

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

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

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

解决办法:

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

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

<template>
    <div>
    <el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
        <el-button type="success" @click="addFormOpen('addForm')">添加</el-button>
      </el-form-item>
    </el-form>  
        
    <el-table
          :data="tableData"
          style="width: 100%">
          
          <!--
          glyphicon: "el-icon-user"
          menuid: 11
          menuname: "管理中心"
          menuurl: "#"
          pid: 0
          pname: "无"
           
          -->
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column
            label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>
                {{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
        
        <!-- 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"); -->
        
        <el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form ref="addForm" :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          selectOptions:[],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          },
          queryForm:{
              qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
              qpid:''
          },
          
          /* 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:'',
          },
          addFormVisible : false,
          formLabelWidth: '120px'     
        };
      },
      methods: {
        addFormOpen(formName){
            this.addFormVisible = true;
            this.$nextTick(function(){    //渲染时机后移,做完一次渲染后再执行括号里的代码
                this.$refs[formName].resetFields();
            })  
        },  
        addFormSubmit(){
            this.addFormVisible = false; //让弹出框关闭
            //this.$qs.stringify(this.addForm);   //取addForm数据,并格式化
            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)=>{
                
            })
        },  
        resetForm(formname){
            this.$refs[formname].resetFields();
        },  
        queryMenu(){
        let params = this.$qs.stringify(this.queryForm);    
        this.myQuery(params);
        },  
        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就找外部的全局参数。
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        },
        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);
            });
         }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = "";
          this.myQuery(params); 
          this.getMenuSelect();  
      }
    }
</script>
​
<style>
    .el-select{
        width:100%;
    }
</style>
​

1.3.3修改功能

①.定位当前行数据

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

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

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

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

在页面显示的table标签上:

<el-table
:data="tableData"
highlight-current-row
@current-change="handleCurrentChange()"
style="width: 100%">

定义该方法:

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

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

data里定义该对象:

currentRow: null

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

@current-change="handleCurrentChange"

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

<el-button type="warning" @click="editFormOpen('editForm')">修改</el-button>

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

editForm和添加很像。

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

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

<el-dialog title="修改" :visible.sync="editFormVisible">
          <el-form ref="editForm" :model="editForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="editForm.menuid" autocomplete="off" readonly></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="editForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="editForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="editForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="editForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="editFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="editFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>

下面对应的数据:

将data里addForm复制一份修改:

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

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

editFormOpen(){
  this.editFormVisible = true;
}

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

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

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

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

这是数据类型的问题:

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

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

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

<el-select v-model="editForm.pid" placeholder="pid">
    <el-option label="无" :value="0"></el-option>
    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
</el-select>

③.修改数据

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

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

里面有一个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);
​
}

此时,整体代码如下:

<template>
    <div>
    <el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
        <el-button type="success" @click="addFormOpen('addForm')">添加</el-button>
        <el-button type="warning" @click="editFormOpen('editForm')">修改</el-button>
      </el-form-item>
    </el-form>  
        
    <el-table
          :data="tableData"
          highlight-current-row
           @current-change="selectRowChange"
          style="width: 100%">
        
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>{{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
        
        <!-- 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"); -->
        
        <el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form ref="addForm" :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
        <el-dialog title="修改" :visible.sync="editFormVisible">
          <el-form ref="editForm" :model="editForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="editForm.menuid" autocomplete="off" readonly></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="editForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="editForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="editForm.pid" placeholder="pid">
                    <el-option label="无" :value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="editForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="editFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="editFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          selectOptions:[],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          },
          queryForm:{
              qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
              qpid:''
          },
          
          /* 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:'',
          },
          editForm:{
              menuid:'',
              menuname:'',
              menuurl:'',
              pid:'',
              glyphicon:'',
          },
          editFormVisible : false,
          addFormVisible : false,
          formLabelWidth: '120px',
          currentRow: null
        };
      },
      methods: {
        editFormSubmit(){
            this.editFormVisible = false; //让弹出框关闭
            let url = "/menu/edit";
            let params = this.$qs.stringify(this.editForm);
            this.myCUD(url,params);
            
        },  
        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));  //可写成一句话
        },  
        selectRowChange(val){         //参数是当前改变的这行数据
            this.currentRow = val;
        },  
        addFormOpen(formName){
            this.addFormVisible = true;
            this.$nextTick(function(){    //渲染时机后移,做完一次渲染后再执行括号里的代码
                this.$refs[formName].resetFields();
            })  
        },  
        addFormSubmit(){
            this.addFormVisible = false; //让弹出框关闭
            let url = "/menu/add";
            let params = this.$qs.stringify(this.addForm);
            this.myCUD(url,params);
            
        },  
        resetForm(formname){
            this.$refs[formname].resetFields();
        },  
        queryMenu(){
        let params = this.$qs.stringify(this.queryForm);    
        this.myQuery(params);
        },  
        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就找外部的全局参数。
        },
        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)=>{
                
            })
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        },
        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);
            });
         }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = "";
          this.myQuery(params); 
          this.getMenuSelect();  
      }
    }
</script>
​
<style>
    .el-select{
        width:100%;
    }
</style>
​

效果如下:

1.3.4删除功能

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

①.复制小弹出框的代码

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

添加进按钮:

<el-button type="danger" @click="removeMenu()">删除</el-button>

将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: '已取消删除'
                  });          
                });
        }

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

代码如下:

<template>
    <div>
    <el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
        <el-button type="success" @click="addFormOpen('addForm')">添加</el-button>
        <el-button type="warning" @click="editFormOpen('editForm')">修改</el-button>
        <el-button type="danger" @click="removeMenu()">删除</el-button>
      </el-form-item>
    </el-form>  
        
    <el-table
          :data="tableData"
          highlight-current-row
           @current-change="selectRowChange"
          style="width: 100%">
        
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>{{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
        
        <!-- 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"); -->
        
        <el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form ref="addForm" :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
        <el-dialog title="修改" :visible.sync="editFormVisible">
          <el-form ref="editForm" :model="editForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="editForm.menuid" autocomplete="off" readonly></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="editForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="editForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="editForm.pid" placeholder="pid">
                    <el-option label="无" :value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="editForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="editFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="editFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          selectOptions:[],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          },
          queryForm:{
              qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
              qpid:''
          },
          
          /* 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:'',
          },
          editForm:{
              menuid:'',
              menuname:'',
              menuurl:'',
              pid:'',
              glyphicon:'',
          },
          editFormVisible : false,
          addFormVisible : false,
          formLabelWidth: '120px',
          currentRow: null
        };
      },
      methods: {
        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: '已取消删除'
                  });          
                });
        }, 
        editFormSubmit(){
            this.editFormVisible = false; //让弹出框关闭
            let url = "/menu/edit";
            let params = this.$qs.stringify(this.editForm);
            this.myCUD(url,params);
            
        },  
        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));  //可写成一句话
        },  
        selectRowChange(val){         //参数是当前改变的这行数据
            this.currentRow = val;
        },  
        addFormOpen(formName){
            this.addFormVisible = true;
            this.$nextTick(function(){    //渲染时机后移,做完一次渲染后再执行括号里的代码
                this.$refs[formName].resetFields();
            })  
        },  
        addFormSubmit(){
            this.addFormVisible = false; //让弹出框关闭
            let url = "/menu/add";
            let params = this.$qs.stringify(this.addForm);
            this.myCUD(url,params);
            
        },  
        resetForm(formname){
            this.$refs[formname].resetFields();
        },  
        queryMenu(){
        let params = this.$qs.stringify(this.queryForm);    
        this.myQuery(params);
        },  
        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就找外部的全局参数。
        },
        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)=>{
                
            })
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        },
        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);
            });
         }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = "";
          this.myQuery(params); 
          this.getMenuSelect();  
      }
    }
</script>
​
<style>
    .el-select{
        width:100%;
    }
</style>
​

1.3.5最后一个功能

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

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

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

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

先加两个disabled的属性:

<el-button type="warning" @click="editFormOpen('editForm')" :disabled="btnstatus">修改</el-button>
<el-button type="danger" @click="removeMenu()" :disabled="btnstatus">删除</el-button>

在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(钩子函数)是同级的。

最终的代码:

<template>
    <div>
    <el-form ref="queryForm" :inline="true" :model="queryForm" class="demo-form-inline">
      <el-form-item label="菜单名称" prop="qmname">
        <el-input v-model="queryForm.qmname" placeholder="menuname"></el-input>
      </el-form-item>
      <el-form-item label="父菜单" prop="qpid">  
        <el-select v-model="queryForm.qpid" placeholder="pid">
              <el-option label="无" value="0"></el-option>
              <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
            </el-select>
          </el-form-item>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="queryMenu()">查询</el-button>
        <el-button type="info" plain @click="resetForm('queryForm')">重置</el-button>
        <el-button type="success" @click="addFormOpen('addForm')">添加</el-button>
        <el-button type="warning" @click="editFormOpen('editForm')" :disabled="btnstatus">修改</el-button>
        <el-button type="danger" @click="removeMenu()" :disabled="btnstatus">删除</el-button>
      </el-form-item>
    </el-form>  
        
    <el-table
          :data="tableData"
          highlight-current-row
           @current-change="selectRowChange"
          style="width: 100%">
        
          <el-table-column
            prop="menuid"
            label="菜单编号">
          </el-table-column>
          <el-table-column
            prop="menuname"
            label="菜单名称">
          </el-table-column>
          <el-table-column
            prop="menuurl"
            label="访问地址">
          </el-table-column>
          <el-table-column
            prop="pid"
            label="上级菜单编号">
          </el-table-column>
          <el-table-column
            prop="pname"
            label="上级菜单名称">
          </el-table-column>
          <el-table-column label="菜单图标">
            <template v-slot="mydata"> 
                <i :class="mydata.row.glyphicon"></i>{{mydata.row.glyphicon}}
            </template>
          </el-table-column>
        </el-table>
        
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageinfo.page"
          :page-sizes="[10, 20, 30]"
          :page-size="pageinfo.pagesize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageinfo.total">
        </el-pagination>
        
        
        <!-- 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"); -->
        
        <el-dialog title="添加" :visible.sync="addFormVisible">
          <el-form ref="addForm" :model="addForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="addForm.menuid" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="addForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="addForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="addForm.pid" placeholder="pid">
                    <el-option label="无" value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="addForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="addFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="addFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
        <el-dialog title="修改" :visible.sync="editFormVisible">
          <el-form ref="editForm" :model="editForm">
            <el-form-item label="菜单编号" :label-width="formLabelWidth" prop="menuid">
              <el-input v-model="editForm.menuid" autocomplete="off" readonly></el-input>
            </el-form-item>
            <el-form-item label="菜单名称" :label-width="formLabelWidth" prop="menuname">
              <el-input v-model="editForm.menuname" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="访问地址" :label-width="formLabelWidth" prop="menuurl">
              <el-input v-model="editForm.menuurl" autocomplete="off"></el-input>
            </el-form-item>     
            <el-form-item label="父菜单" prop="qpid" :label-width="formLabelWidth">
                <el-select v-model="editForm.pid" placeholder="pid">
                    <el-option label="无" :value="0"></el-option>
                    <el-option v-for="opt in selectOptions" :label="opt.menuname" :value="opt.menuid"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="图标" :label-width="formLabelWidth" prop="glyphicon">
              <el-input v-model="editForm.glyphicon" autocomplete="off"></el-input>
            </el-form-item> 
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="editFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="editFormSubmit()">确 定</el-button>
          </div>
        </el-dialog>
        
    </div>      
</template>
​
<script>
 export default {
      data() {
        return {
          tableData: [],
          selectOptions:[],
          /* pageinfo:
          page: 1
          pagesize: 10
          total: 29 */
          pageinfo:{
              page: 3,
              pagesize: 10,
              total: 660
          },
          queryForm:{
              qmname:'',    //做双向绑定时,就不给假数据了,默认空值   
              qpid:''
          },
          
          /* 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:'',
          },
          editForm:{
              menuid:'',
              menuname:'',
              menuurl:'',
              pid:'',
              glyphicon:'',
          },
          editFormVisible : false,
          addFormVisible : false,
          formLabelWidth: '120px',
          currentRow: null,
          btnstatus:true
        };
      },
      methods: {
        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: '已取消删除'
                  });          
                });
        }, 
        editFormSubmit(){
            this.editFormVisible = false; //让弹出框关闭
            let url = "/menu/edit";
            let params = this.$qs.stringify(this.editForm);
            this.myCUD(url,params);
            
        },  
        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));  //可写成一句话
        },  
        selectRowChange(val){         //参数是当前改变的这行数据
            this.currentRow = val;
            this.btnstatus = false;
        },  
        addFormOpen(formName){
            this.addFormVisible = true;
            this.$nextTick(function(){    //渲染时机后移,做完一次渲染后再执行括号里的代码
                this.$refs[formName].resetFields();
            })  
        },  
        addFormSubmit(){
            this.addFormVisible = false; //让弹出框关闭
            let url = "/menu/add";
            let params = this.$qs.stringify(this.addForm);
            this.myCUD(url,params);
            
        },  
        resetForm(formname){
            this.$refs[formname].resetFields();
        },  
        queryMenu(){
        let params = this.$qs.stringify(this.queryForm);    
        this.myQuery(params);
        },  
        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就找外部的全局参数。
        },
        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)=>{
                
            })
        },
        myQuery(params){
            this.$axios.get("/menu/query?"+params)   
            .then((ret)=>{
              console.log(ret);
              if(ret.data.returnCode==10000){
                  /* this.$nextTick(function(){
                      this.btnstatus = true;     //按钮禁用
                  }) */ 
                  //展示table数据
                  this.tableData = ret.data.returnData;
                  //把页码相关数据传给分页组件
                  this.pageinfo = ret.data.pageinfo;
              }
            }).catch((err)=>{
                          console.log(err);
            });
        },
        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);
            });
         }
      },
      mounted(){
          console.log("页面加载结束后执行");
          let params = "";
          this.myQuery(params); 
          this.getMenuSelect();  
      },
      watch:{   //监控指定的数据
          tableData:function(){   //刷新数据后自动触发
              console.log("tableData数据渲染完成了");
              this.$nextTick(function(){
                this.btnstatus = true;     //按钮禁用
              })
          }
      }
    }
</script>
​
<style>
    .el-select{
        width:100%;
    }
</style>
​

2.用户服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值