Front and back end separation learning notes -- [route nesting, query form display]

You can refer to the API of learning ElementUI framework - > API for ElementUI framework

1. Route nesting

Study of the last note Administrator login permission assignment
Click the operation menu on the left to generate a specific management form;

You can imagine that the basic layout of the open is like this

Here, several directories are written in the view view as different management operation page components;

For example, the role management page component rolelist vue

<template>
	<div>
		Role management
	</div>
</template>

<script>
</script>

<style>
</style>

Administrator management page component adminlist vue

<template>
	<div>
		Administrator management
	</div>
</template>

<script>
</script>

<style>
</style>

Note that after the left column is opened, it should be displayed in the main page instead of page Jump;


Index. In router routing directory JS to import the component adminlist Vue and rolelost vue;
They can be nested under the sub route of the main page; Complete route nesting

Index. In router routing directory js

import Vue from 'vue';
import router from 'vue-router'; /* Import route */
import Login from '../view/Login.vue'; /* Import other pages */
import Main from '../view/Main.vue'; /* Import other pages */

//Import administrator management component and role management component;
import Admin from '../view/admin/AdminList.vue';
import Role from '../view/role/RoleList.vue';
Vue.use(router)
/* Define component routing */
var rout = new router({
	routes: [
		//Login page component;
		{
			path: '/login',
			name: 'index',
			component: Login
		},
		//Main page component;
		{
			path: '/main',
			component: Main,
			//Nested sub routing;
			children:[
				//Administrator management component;
				{
				  path:	'/admin',
				  component:Admin,
				},
				//Role management component;
				{
					path:	'/role',
					component:Role,
				}
			]
		},
		
		/* {
			path: '/demo',
			component: demo
		}, */
	]
});
//Export route;
export default rout;

//To - the address of the page to be accessed, from - which page to access, and next - release function
rout.beforeEach((to, from, next) => {
	//If the user accesses the login page of, it will be released directly;
	if (to.path == '/login') {
		return next();
	} else {
		//If there is no token, push to the login page;
		var token = window.sessionStorage.getItem("token");
		if (token == null) {
			return next("/login");
		} else {
			next();
		}
	}
})

Start the front and rear end services and try;
Click role management to route to / role path;

Click administrator management to route to the / admin path

Pay attention to the storage path in the data table; Just store the routing path

2. Query form display information

Here, we will analyze the situation step by step, and finally make a big summary;

2.1 display administrator's Avatar

Also remember that in the previous spring study, a small demo written with my friends involved the display of avatars;
At that time, the local folder storing the Avatar was hosted on the virtual path of the tomcat server;
This case has similar notes in the previous case – > Virtual path to deploy local folder to tomcat server

This time, if the front and back ends are separated for learning, a server is opened on the springboot side; http://127.0.0.1:5277/ The port is 5277;
The front-end page placed by HBuilderX opens the default server http://localhost:8080/ ; The port is 8080;

Then I will open a separate server for the administrator's Avatar storage;
http://localhost:5927/

In fact, I decompress and install a tomcat server locally;

Note: modify the port number of the server under the conf configuration directory of the installation package; I changed it to 5927 according to my own needs;
In addition, if the modified port conflicts with other paths, change it again;

Note: put the accessed image folder in the ROOT directory under webapps of tomcat installation package;

Similarly, for better results, the administrator has prepared the default avatar folder directory default;

The server for storing avatars can be opened manually;

Avatar display effect

So how is the front-end component written here

First, the column attribute prop of this table is newFileName, which is the file name newFileName of the new avatar in the administrator table queried through the request;
Then it uses the table of element UI to customize the template< template slot-scope="scope">;
The scope here Row represents the data of the current row; This scope row. Account is the administrator account name of the current line;
scope.row.newFileName is the name of the administrator's new avatar in the current line;
http appearing in the binding path src of the picture img is the picture address of the administrator avatar defined below;

Here, v-if and v-else instructions are used;
Judge the avatar to be displayed according to whether the administrator has a avatar;

Define the server address where the administrator's Avatar file is stored
http: "http://127.0.0.1:5927/studyspringbootvue/",

This is the one created before;

2.2 custom display format

For example; Administrator type and date display format in the list;
01 (0: super management; 1: ordinary administrator) storage is used when the database storage administrator type is used;
You can't directly display numbers here; Then you need to customize the displayed template;

Desired display effect

You can refer to the API of learning ElementUI framework - > API for ElementUI framework

First, see the format processing of administrator type;
The formatter function is used here to bind the event formaterType

Bind the event formatType(row,column) in the following JS;
Judge the administrator type of the current line; Return data;

Let's look at the format processing of date types;
You can use this sortable to generate sorting buttons for time;
Similarly, the formatter function is used to bind the event formaterTime

Bind the event formatTime(row,column) in the following JS;
Format conversion for the administrator operation time of the current line; Return data;

Click ^ to sort and display in ascending time order, and Click v to sort and display in descending time order;

2.3 processing of double cycle data;

Note that the role of the current administrator needs to be displayed;
Then there is more than one role, that is, an administrator may correspond to multiple roles;
Here, you can directly use the administrator table to left associate the management role relationship table, and then left associate the role table to query the corresponding role of the administrator;

SELECT 
 a.`id`,
 a.`account`,
 a.`password`,
 a.`sex`,
 a.`phone`,
 a.`new_file_name`,
 a.`type`,
 a.`oper_time`,
 r.`name`
 FROM t_admin a 
      LEFT JOIN t_admin_role am ON  am.`admin_id`=a.`id`
      LEFT JOIN t_role r       ON  am.`role_id`  = r.`id`
 WHERE  a.`type` = 1  

Pay attention to the query effect. Of course, if you want to query, it is entirely possible;
However, the case I want to build this time needs to page the queried data;
Then, when the data is paged, it will be paged according to the query results;
If you page directly like this, the display effect is certainly not very good;

It can be assumed that; For example, an administrator has more than 50 roles; The 50 rows of data in this query are all the information of the administrator. The data is redundant. For the system, the quality effect is not very good;

Then you have to consider nested queries; Review previous knowledge of nested queries and lazy loading;
This was learned when mybatis was studying;
Here are some notes to sort out - > Mybatis framework learning notes (6) - [lazy load configuration applied to nested query]

In this case, first query the administrator table, and then query the associated role information of the corresponding Id according to the queried administrator Id;

In the administrator class, the

private List<Role> roleList;

In SQL processing, the resultmap result set mapping is used as a bridge;
Use the queried administrator Id as a condition to query all associated roles corresponding to this Id; Finally, the queried management list information is returned;

If the front-end component is displayed here, of course, the attribute prop of the table column is also the attribute roleList in the four administrator classes;
Then traverse the role list data of the row; Just display the name of the role

Display effect

2.4 query criteria setting at the top of the form column

Here, you need to bind data and transfer values;

Bound data field

Bound search event; Here, because the request query list needs to be sent when loading the page, it is queried according to the conditions,
In fact, you can use the same request; Then the back-end dynamic SQL binding is used;

Decide whether to add query criteria according to whether to pass in query criteria;

Query effect:

2.5 paging processing, using PageHelper component

You can find it in the paging component of ElementUI, which is the display effect of the front end;
Then the data interaction between the front and back ends is required;

  • The front end needs to display the total number of data,
  • Displays the current page number,
  • Several pieces of data can be displayed on the current page;
  • Display the total number of pages;
    In fact, you only need to let the back-end transfer a total of several pieces of data. The number of pages displayed here is left to the component packaging calculation;

Current page = "1" the default is the first page
Page sizes = "[2, 4, 6, 8]" generate an array of drop-down boxes (several pieces of data are displayed on each page)
Page size = "2" the default size per page
layout="total, sizes, prev, pager, next, jumper" layout
total="200" total number of data
@Size change = "handlesizechange" triggered when the drop-down box changes the page size
@Current change = "handlecurrentchange" triggered when the page number is changed

Note that there are several values that need to be bound; It needs to transfer data dynamically;
The front end sends the current page number of pageNum successively; Issue pageSize to the data volume of the current page;

Of course, the general method of querying the administrator is directly used here; Pass in the current page number data and the amount of data displayed on the current page number;

Select a page number, and an event will be triggered when the amount of data displayed on the current page is selected;

Let's look at the back-end processing. The first thing that comes to mind for paging queries is the limit restriction statement in SQL;
But the paging component here is also good;
PageHelper component

In POM It can be used by importing from XML;

<!-- pagehelper rely on -->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.5</version>
</dependency>

The Sql mapping file doesn't even need to write a limit statement here. It will be spliced into the query automatically

For the case study here, in order to accept the data conveniently, you can directly save the data into the Admin administrator class;
For actual use, it is recommended to redefine a paging entity class;

At the invoked service layer method;
First, use the startPage of PageHelper class to transfer the paged page number data and the amount of data displayed on each page;

After the persistence layer finds out all the administrator information lists,
Then use PageInfo class for paging processing;
Then, the return value type of this query must also follow the PageInfo type;

Then, in the general return class written before, you also need to define an attribute to represent the total amount of data;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author by @CSDN Xiaozhi RE0
 * @date 2021-12-28 16:02
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult {
    private Integer code;
    private String msg;
    private Object data;
    //Paging use; Total data;
    private Long total;

    public CommonResult(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

At this time, you may have to ask, because the front end needs the total amount of data to prepare for paging;
Do you still need to query the total data of the administrator;

Not at all;
PageInfo can call this method getTotal() to get the total number of administrator information queried before when there are no restrictions;

Points needing attention

After springboot version 2.5, to integrate this paging component, you have to set the open loop dependency in the yml configuration file of springboot
Note the settings in main under spring

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/day20211126_manage_db?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    filters: stat
    initialSize: 5
    minIdle: 1
    maxActive: 20
  main:
    allow-circular-references: true

Of course, the front end needs to receive the total amount of data calculated by the back end; Bind to the corresponding data domain module variable total;

The whole query form is easy to build

Main of the front end Vue main page
The permission assignment case was written when it was built Separation of front and back end learning notes (3) -- [case of administrator permission allocation operation menu]

Administrator list component page
AdminList.vue

<template>
	<el-card class="box-card" style="background-color: #E9EEF3;">
		<div slot="header" class="clearfix">
			<span>managers</span>
			<el-button style="float: right; padding: 10px 10px" type="success"
			@click="openAddAdmin()">newly added</el-button>
		</div>
		<div class="text">
			<!-- Query criteria box-->
			<div class="clearfix" style="padding-bottom: 10px;">
				<!-- query criteria-->
				<el-row :gutter="20">
					<el-col :span="6">
						<el-input placeholder="account number" v-model="query.account"></el-input>
					</el-col>
					<el-col :span="6">
						<el-input placeholder="Gender" v-model="query.sex"></el-input>
					</el-col>
					<el-col :span="6">
						<el-button type="primary" icon="el-icon-search" @click="search()">search</el-button>
					</el-col>
				</el-row>
			</div>
			<!-- Data table location-->
			<el-table :data="tableData" style="width: 100%" border max-height="450">
				<el-table-column fixed type="index" label="number" width="60">
			 </el-table-column>
				<el-table-column prop="account" label="account number" width="100"></el-table-column>
				<el-table-column prop="newFileName" label="head portrait" width="60">
					<template slot-scope="scope">
						<!-- If there is no avatar,The default avatar is displayed -->
						<img v-bind:src="http+'default/admin.png'" width="30" height="30"
							v-if="scope.row.newFileName === null " />
						<!-- Otherwise, the administrator's Avatar is displayed -->
						<img v-bind:src="http+scope.row.account+'/'+scope.row.newFileName" width="30" height="30"
							v-else />
					</template>
				</el-table-column>
				<el-table-column prop="sex" label="Gender" width="50"></el-table-column>
				<el-table-column prop="phone" label="mobile phone" width="120"></el-table-column>
				<el-table-column prop="type" label="type" width="120" :formatter="formatType"></el-table-column>
				<el-table-column prop="roleList" label="role" width="250" align="center">
					<template slot-scope="scope">
						<span v-for="(role,index) in scope.row.roleList" :key="index">{{role.name}}-</span>
					</template>
				</el-table-column>
				<!-- Time plus sorting -->
				<el-table-column prop="operTime" label="time" width="200" :formatter="formatTime" sortable>
				</el-table-column>
				<el-table-column fixed="right" label="operation" width="200">
					<template slot-scope="scope">
						<el-button size="mini" @click="toUpdate(scope.row.id)">edit</el-button>
						<el-button size="mini" type="danger" @click="toDelAdmin(scope.row.id)">delete</el-button>
					</template>
				</el-table-column>
			</el-table>
			<!-- Paging component-->
			<!-- 
			 current-page="1" The default is the first page
			 page-sizes="[2, 4, 6, 8]" Generate drop-down box
			 page-size="2" Default per page size
			 layout="total, sizes, prev, pager, next, jumper" layout
			 total="400" Total number
			 @size-change="handleSizeChange" Triggered when the drop-down box changes the page size
			 @current-change="handleCurrentChange" Triggered when the page number is changed
			 -->
			<div class="block">
				<el-pagination 
				@size-change="handleSizeChange" 
				@current-change="handleCurrentChange"
				:page-sizes ="[2, 4, 6, 8,10,15]" 
				:page-size="2" 
				layout="total, sizes, prev, pager, next, jumper"
				:total= "total">
				</el-pagination>
			</div>
		</div>
	</el-card>
</template>

<script>
	export default {
		data: function() {
			return {
				tableData: [],
				//Defined image server location;
			    http: "http://127.0.0.1:5927/studyspringbootvue/",
				//Binding parameters when searching according to conditions;
				query: {
					account: '',
					sex: '',
					//Current page number;
					pageNum:1,
					//Current page data volume;
					pageSize:2
				},
				//Total number of data;
				total:0
			}
		},
		methods: {
			//User defined binding events of administrator type;
			//Row indicates the row of data Column name gets the data of the current column
			formatType(row, column) { 
				return (row.type == 1 ? "General administrator" : "Supertube"); //The returned data shall prevail in the final table
			},

			//Formatting of time;
			formatTime(row, column) {
				var date = new Date(row.operTime);
				return date.toLocaleString();
			},

			//Public search administrator list method;
			getAdminList() {
				//Send the administrator's data to the back end;
				var _this = this;
				this.$http.post("/admin/getAllAdmin", this.query).then(function(resp) {
					console.log(resp);
					_this.tableData = resp.data.data;
					_this.total = resp.data.total;
					console.log(resp.data.total)
				});
			},

			//Search administrators according to criteria;
			search() {
				this.query.pageNum=1;
				this.getAdminList();
			},

            //Modify the amount of data displayed on the current page;
			handleSizeChange(val) {
				console.log(`each page ${val} strip`);
				this.query.pageSize = val;
				this.getAdminList();
			},
			//Modify the displayed page number of the current page;
			handleCurrentChange(val) {
				console.log(`Current page: ${val}`);
				this.query.pageNum = val;
				this.getAdminList();
			},
			
			//Jump to open the new administrator window;
			openAddAdmin(){
			   
			},
			
			//Jump to open the page and modify the administrator information;
			toUpdate(id) {
				console.log(id)
			},

			//Delete administrator;
			toDelAdmin(id) {
				console.log(id)
			},
		},
		//Triggered when the page is loaded;
		created() {
			//Call the general query administrator method;
			this.getAdminList();
		}
	}
</script>

<style>

</style>

In the index of the route The route nesting when importing the administrator list component in JS is described above; I will not repeat it here;

Back end part

Administrator managed SQL mapping file adminmapper xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--Note that the corresponding space here is the persistence layer mapping interface-->
<mapper namespace="com.xiaozhi.backserver.startspringboot.dao.AdminDao">

    <!--Custom result set,Where associated roles are required-->
    <resultMap id="adminmap" type="admin">
        <id property="id" column="id"/>
        <result property="account" column="account"/>
        <result property="password" column="password"/>
        <result property="sex" column="sex"/>
        <result property="phone" column="phone"/>
        <result property="newFileName" column="new_file_name"/>
        <result property="operTime" column="oper_time"/>
        <result property="type" column="type"/>

        <collection property="roleList" javaType="list" ofType="role"
         select="getRoleBYAdminId" column="id">
            <result property="name" column="name"/>
            <result property="id"   column="role_id"/>
        </collection>
    </resultMap>

    <!--Query all administrators;-->
    <select id="getAllAdmin" parameterType="admin" resultMap="adminmap">
            SELECT
            `id`,
            `account`,
            `sex`,
            `phone`,
            `new_file_name`,
            `type`,
            `oper_time`
            FROM t_admin
            WHERE  `type` = 1
            <if test="account!=''">
                and `account` =#{account}
            </if>
            <if test="sex!=''">
                and `sex` =#{sex}
            </if>
    </select>

    <!--According to the administrator's id The corresponding role information is queried-->
    <select id="getRoleBYAdminId" resultType="role">
        select
         r.name
        from
        t_admin_role ar
        left join t_role r on ar.`role_id`=r.`id`
        where ar.`admin_id` =#{id}
    </select>
</mapper>

Administrator managed persistence layer processing
AdminDao

package com.xiaozhi.backserver.startspringboot.dao;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
 * @author by Xinji 1801 class Li Zhiqing (Xiaozhi RE0)
 * @date 2021-12-29 18:58
 */
@Repository
public interface AdminDao {

    //Get all administrators;
    List<Admin> getAllAdmin(Admin admin);
}

Administrator managed service layer AdminService

package com.xiaozhi.backserver.startspringboot.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xiaozhi.backserver.startspringboot.dao.AdminDao;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
 * @author by Xinji 1801 class Li Zhiqing (Xiaozhi RE0)
 * @date 2021-12-29 18:57
 */
@Transactional
@Service
public class AdminService {
    @Autowired
    AdminDao adminDao;

    /**
     *Query to get a list of all administrators
     */
    public PageInfo<Admin> getAllAdmin(Admin admin) {
        //Store the current number of page numbers and the data amount of the current page number in the paging component;
        PageHelper.startPage(admin.getPageNum(), admin.getPageSize());

        List<Admin> allAdmin = adminDao.getAllAdmin(admin);
        //Perform paging query on the queried list;
        PageInfo<Admin> info = new PageInfo<>(allAdmin);
        return info;
    }
}

Administrator managed control layer AdminController

package com.xiaozhi.backserver.startspringboot.controller;

import com.github.pagehelper.PageInfo;
import com.xiaozhi.backserver.startspringboot.model.Admin;
import com.xiaozhi.backserver.startspringboot.model.Role;
import com.xiaozhi.backserver.startspringboot.service.AdminService;
import com.xiaozhi.backserver.startspringboot.util.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;


/**
 * @author by Xinji 1801 class Li Zhiqing (Xiaozhi RE0)
 * @date 2021-12-29 18:54
 */
@RestController
@RequestMapping(value = "/api/admin")
public class AdminController {
    CommonResult commonResult;

    @Autowired
    AdminService adminService;

    //Query all administrators and view subordinate roles
    @PostMapping(path = "/getAllAdmin")
    public CommonResult getAllAdmin(@RequestBody Admin admin){
        try{
            //Call the method to query all administrators;
            PageInfo<Admin> allAdmin = adminService.getAllAdmin(admin);
                // getList() the data queried in the current page; getTotal() the total amount of data in the current data list;
                commonResult = new CommonResult(200,"Query of administrator list information succeeded",allAdmin.getList(),allAdmin.getTotal());
        }catch (Exception e){
            e.printStackTrace();
            commonResult = new CommonResult(500,"Server exception",null);
        }
        return commonResult;
    }
}

Common return class CommonResult

package com.xiaozhi.backserver.startspringboot.util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author by @CSDN Xiaozhi RE0
 * @date 2021-12-28 
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult {
    private Integer code;
    private String msg;
    private Object data;
    //Paging use; Total data;
    private Long total;

    public CommonResult(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

springboot configuration file application yml

server:
  port: 5277

#jdbc connection and data source configuration;
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/day20211126_manage_db?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    filters: stat
    initialSize: 5
    minIdle: 1
    maxActive: 20
  main:
    allow-circular-references: true


#Integrate mybatis;
mybatis:
  type-aliases-package: com.xiaozhi.backserver.startspringboot.model
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    map-underscore-to-camel-case: true
cache-enabled: true
# Log printing;
logging:
  level:
    com.xiaozhi.backserver.startspringboot.dao: trace
  file:
   name: E:\\logs\\log.log

Overall effect

Keywords: Spring Boot Vue.js elementUI

Added by forzatio on Mon, 03 Jan 2022 03:33:48 +0200