SSM框架實現後臺管理系統權限管理(用戶、菜單、角色)

後臺管理系統開發

功能模塊:用戶登錄、權限管理(用戶管理、菜單管理、角色管理)

技術應用:SSM框架+Mysql5.7+jsp+前端layui和EasyUI框架

項目工具:Maven+tomcat9.0+jdk1.8+GIT

開發工具:IDEA

測試環境:window7+搜狗瀏覽器10.0

 

一、數據庫表結構設計

基礎權限管理需要五張表:菜單、用戶、角色、角色權限、用戶角色

創建數據庫

CREATE DATABASE IF NOT EXISTS exam;
1、菜單表menu
描述 字段 類型 約束
菜單id id int 主鍵自增
菜單名 name varchar 不能爲空
鏈接頁面 url varchar
父菜單id parent_id int
菜單排序 sort int
icon varchar
perms varchar
菜單類型 type smallint

建表sql:

CREATE TABLE menu (
	id int PRIMARY KEY AUTO_INCREMENT,
	name varchar(50) NOT NULL,
    parent_id int,
	url varchar(500),
	icon varchar(30),
	perms varchar(100),
	type smallint(6),
    sort int
);

插入數據:

insert into menu(name,icon,type,sort) value("權限管理","fa fa-bug",0,1);
insert into menu(name,parent_id,url,icon,perms,type,sort) value("菜單管理",2,"/sys/menu.html","fa fa-th-list","svs:menu:list",1,1000);
insert into menu(name,parent_id,url,icon,perms,type,sort) value("角色管理",2,"/sys/role.html","fa fa-key","svs:role:list",1,1000);
insert into menu(name,parent_id,url,icon,perms,type,sort) value("用戶管理",2,"/sys/user.html","fa fa-user","svs:user:list",1,1000);

insert into menu(name,parent_id,perms,type,sort) value("添加",5,"sys:user:add",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("修改",5,"sys:user:update",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("刪除",5,"sys:user:delete",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("授權",5,"sys:user:assign",2,1000);

insert into menu(name,parent_id,perms,type,sort) value("添加",3,"sys:menu:add",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("修改",3,"sys:menu:update",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("刪除",3,"sys:menu:delete",2,1000);

insert into menu(name,parent_id,perms,type,sort) value("添加",4,"sys:role:add",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("修改",4,"sys:role:update",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("刪除",4,"sys:role:delete",2,1000);
insert into menu(name,parent_id,perms,type,sort) value("授權",4,"sys:role:assign",2,1000);

2、用戶表user
描述 字段 類型 約束
用戶id id int 主鍵自增
用戶名 account varchar 唯一、不能爲空
用戶密碼 password varchar
暱稱 nickname varchar
動態鹽(用戶名md5) salt varchar
用戶狀態 status int 爲1正常,爲0表示停用

建表sql:

CREATE TABLE user (
	id int PRIMARY KEY AUTO_INCREMENT,
	account varchar(50) NOT NULL UNIQUE,
	password varchar(50),
	nickname varchar(50),
	salt varchar(50),
    STATUS int DEFAULT '1'
);

插入數據:

insert into user(account, password, nickname)
VALUES ("a000001", "123456", "瞪誰誰懷孕"),
       ("a000002", "123456", "騎豬上高速"),
       ("a000003", "123456", "朕要去幼兒園深造了"),
       ("a000004", "123456", "帥到拖網速"),
       ("a000005", "123456", "遇蛇撐傘裝許仙"),
       ("a000006", "123456", "談情不如逗狗"),
       ("a000007", "123456", "地球是哥捏圓的"),
       ("a000008", "123456", "不賤不散"),
       ("a000009", "123456", "跳進海里躲雨"),
       ("a000010", "123456", "此用戶下落不明")
       ("a000011", "123456", "軟妹轟炸機"),
       ("a000012", "123456", "怡紅院掌櫃"),
       ("a000013", "123456", "被丟棄的小盆友"),
       ("a000014", "123456", "近豬者喫"),
       ("a000015", "123456", "跪是種美德"),
       ("a000016", "123456", "老衲逛青樓"),
       ("a000017", "123456", "武功再高也怕菜刀"),
       ("a000018", "123456", "鏡子你又胖了"),
       ("a000019", "123456", "騎豬總裁"),
       ("a000020", "123456", "搶我辣條還想跑"),
       ("a000021", "123456", "騙子被騙子騙了"),
       ("a000022", "123456", "農夫三拳"),
       ("a000023", "123456", "夜以深,適宜私奔"),
       ("a000024", "123456", "賣女孩的小火柴"),
       ("a000025", "123456", "唐伯虎點蚊香"),
       ("a000026", "123456", "賤男春"),
       ("a000027", "123456", "老鼠上了貓"),
       ("a000028", "123456", "窮人的孩子早出家"),
       ("a000029", "123456", "車到山前是死路"),
       ("a000030", "123456", "我在馬路邊丟了一分錢"),
       ("a000031", "123456", "賣身葬樓主"),
       ("a000032", "123456", "人賤人愛"),
       ("a000033", "123456", "看野花一朵朵");
3、角色表role
描述 字段 類型 約束
角色id id int 主鍵自增
角色名 name varchar
角色備註 remark varchar
角色狀態 status int

建表sql:

CREATE TABLE role (
	id int PRIMARY KEY AUTO_INCREMENT,
	name varchar(50) NOT NULL,
	remark varchar(50) NOT NULL,
	status int NOT NULL
);

插入數據:

INSERT INTO role (NAME, REMARK, STATUS) VALUES ('admin', '超級管理員', 1),
('test1', 'remark', 1),
('aa3', '3333', 0),
('asd', 'sadsa', 0),
('sa', 'asd', 0);
4、角色權限表role_menu
描述 字段 類型 約束
角色id role_id int 不能爲空
菜單名 menu_id int 不能爲空

建表sql:

CREATE TABLE role_menu (
	role_id int NOT NULL,
	menu_id int NOT NULL,
	CONSTRAINT `PRIMARY` PRIMARY KEY (role_id, menu_id)
);

插入數據:

INSERT INTO role_menu (role_id, menu_id) 
VALUES(1, 2),(1, 3),(1, 4),(1, 5),(1, 6),(1, 7),(1, 8),(1, 9),(1, 10),(1, 11),(1, 12),(1, 13),(1, 14),(1, 15);
INSERT INTO role_menu (role_id, menu_id) 
VALUES(2, 4),(2, 12),(2, 13),(2, 14),(2, 15);
5、用戶角色表user_role
描述 字段 類型 約束
用戶id user_id int 不能爲空
角色id role_id int 不能爲空

建表sql:

CREATE TABLE user_role (
	user_id int NOT NULL,
	role_id int NOT NULL,
	CONSTRAINT `PRIMARY` PRIMARY KEY (user_id, role_id)
);

插入數據:

INSERT INTO user_role (USER_ID, ROLE_ID) VALUES (1, 1),(2, 2);

二、項目準備

1、創建maven項目

建包

主包:com.booy.ssm.exam

子包:實體類包pojo、dao包、service包、controller包、utils包

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h63d5ung-1592834137816)(\img\1.jpg)]

2、創建實體類

User類

package com.booy.ssm.exam.pojo;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String account;
    private String password;
    private String nickname;
    private Integer status;
}

Menu類:

package com.booy.ssm.exam.pojo;

import lombok.Data;

@Data
public class Menu {
    private Integer id;
    private String name;//菜單名
    private String url;
    private Integer parentId;//父id
    private Integer sort;//排序
}
3、添加依賴

在pom.xml添加項目所需要用到的依賴包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ssmdemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <spring.version>5.1.3.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- mybatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- mysql驅動包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>
        <!--日誌包-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--spring數據庫-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring核心包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring-mybatis整合包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        <!--spring相關包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--實體類註解-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
        <!--分頁插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <!--配置資源文件掃描,否則Mapper-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

三、登錄功能簡單實現

1、dao層

創建UserDAO接口:

package com.booy.ssm.exam.dao;

import com.booy.ssm.exam.pojo.User;

public interface UserDAO {
    User getUserByAccount(String account);
}

創建UserMapper.xml映射sql查詢:

<?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">
<mapper namespace="com.booy.ssm.exam.dao.UserDAO">
    <!--<![CDATA[你的註釋]]>-->
    <select id="getUserByAccount" resultType="User" parameterType="String">
        select id, account, password,status
        from user
        where account=#{account}
    </select>
</mapper>
2、service層

創建UserService接口:

package com.booy.ssm.exam.service;

import com.booy.ssm.exam.pojo.User;

public interface UserService {
    //用戶登錄
    User dologin(String account,String password);
}

UserService接口實現類:

package com.booy.ssm.exam.service.impl;

import com.booy.ssm.exam.dao.UserDAO;
import com.booy.ssm.exam.pojo.User;
import com.booy.ssm.exam.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;

    @Override
    public User dologin(String account, String password) {
        User user = userDAO.getUserByAccount(account);
        if(user==null || !user.getPassword().equals(password)|| user.getStatus().equals(ExamConstants.USER_STATUS_DELETE)){
            return null;//賬號或密碼錯誤
        }
        return user;
    }
}
3、controller層

創建SystemController

package com.booy.ssm.exam.controller;

import com.booy.ssm.exam.pojo.User;
import com.booy.ssm.exam.service.UserService;
import com.booy.ssm.exam.utils.ExamConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

@Controller
public class SystemController {
    @Autowired
    UserService userService;

    @RequestMapping("login.html")
    public String login(){
        return "login";
    }
    
    @RequestMapping("dologin.html")
    public String dologin(String account, String password, Model model, HttpSession session){
        User user = userService.dologin(account, password);
        if(user==null){
            model.addAttribute("message","用戶名或密碼錯誤!");
            return "login";
        }
        model.addAttribute("message","登錄成功!");
        session.setAttribute(ExamConstants.SESSION_USER,user.getAccount());
        return "redirect:index.html";
    }
}
4、utils工具類
package com.booy.ssm.exam.utils;

public interface ExamConstants {
    String SESSION_USER="SESSION_USER";
    int USER_STATUS_DELETE=0;
}
5、引入配置文件

1)、jdbc配置文件jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/exam?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

2)、日誌文件log4j.properties

log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
#begin
#for normal test, delete when online
log4j.logger.com.ibatis=DEBUG
1og4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
1og4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
1og4j.logger.java.sql.Connection=DEBUG
1og4j.logger.java.sql.Statement=DEBUG
1og4j.logger.java.sql.PreparedStatement=DEBUG
1og4j.1ogger.java.sq1.ResultSet=DEBUG

3)、spring配置文件spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--數據源管理-->
    <context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--管理session工廠-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定數據源-->
        <property name="dataSource" ref="dataSource"/>
        <!--掃描pojo包,給包下所有pojo對象起別名-->
        <property name="typeAliasesPackage" value="com.booy.ssm.exam.pojo"/>
        <property name="mapperLocations" value="classpath:com/booy/ssm/exam/dao/mapper/*.xml"/>
        <!--配置PageHelper分頁插件-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageHelper">
                    <property name="properties">
                        <value>
                            dialect=mysql
                            reasonable=true
                        </value>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!--掃描接口包路徑,生成包下所有接口的代理對象,並且放入spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.booy.ssm.exam.dao" />
    </bean>
    <!--掃描指定包-->
    <context:component-scan base-package="com.booy.ssm.exam.service" >
        <!--指定掃描的是Service註解-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

    <!-- 事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--定義事務規則-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--對方法的增強-->
        <tx:attributes>
            <!--有異常時回滾事務-->
            <tx:method name="add*" rollback-for="Exception"/>
            <tx:method name="update*" rollback-for="Exception"/>
            <tx:method name="delete*" rollback-for="Exception"/>
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    <!--配置aop切入點-->
    <aop:config>
        <aop:pointcut id="point" expression="execution(* com.booy.ssm.exam.service..*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
    </aop:config>
</beans>

4)、springMvc配置文件springMvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
    <!-- 啓動註解,註冊服務-->
    <mvc:annotation-driven/>
    <!--視圖解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 啓動自動掃描-->
    <!-- 制定掃包規則 ,掃描controller包下面的類-->
    <context:component-scan base-package="com.booy.ssm.exam.controller">
        <!-- 掃描使用@Controller註解的JAVA 類 -->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

5)、web配置文件web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <!--將歡迎頁設置成  index.html-->
    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
    <!--指定listener讀取文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-config.xml</param-value>
    </context-param>
    <!--默認讀取WEB-INF下的applicationContext.xml-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--配置  DispatcherServlet -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <!--解決編碼亂碼-->
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>
</web-app>
6、layui創建登錄頁與後臺主頁

1)、在webapp下創建文件夾media引入layui文件和jquery文件

2)、創建jsp頁面引入layui表單組件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>後臺登錄頁面</title>
    <link rel="stylesheet" href="<%=request.getContextPath()%>/media/layui/css/layui.css">
</head>
<body>
<div class="layui-container">
    <div class="layui-row">
        <div class="layui-col-md9">
            <form class="layui-form" action="<%=request.getContextPath()%>/dologin.html" method="post">
                <div class="layui-form-item">
                    <label class="layui-form-label">用戶名:</label>
                    <div class="layui-input-inline">
                        <input type="text" name="account" required lay-verify="required" placeholder="請輸入用戶名"
                               autocomplete="off"
                               class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">&nbsp;&nbsp;&nbsp;&nbsp;碼:</label>
                    <div class="layui-input-inline">
                        <input type="password" name="password" required lay-verify="required" placeholder="請輸入密碼"
                               autocomplete="off" class="layui-input">
                    </div>
                    <div class="layui-form-mid layui-word-aux">${message}</div>
                </div>
                <div class="layui-form-item">
                    <div class="layui-input-block">
                        <button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
                        <button type="reset" class="layui-btn layui-btn-primary">重置</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>
</body>
</html>

3)、創建jsp後臺主頁面引入layui後臺組件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>考試管理系統</title>
    <link rel="stylesheet" href="<%=request.getContextPath()%>/media/layui/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
    <div class="layui-header">
        <div class="layui-logo">考試管理系統</div>
        <!-- 頭部區域(可配合layui已有的水平導航) -->
        <ul class="layui-nav layui-layout-left">
            <li class="layui-nav-item"><a href="">控制檯</a></li>
            <li class="layui-nav-item"><a href="">商品管理</a></li>
            <li class="layui-nav-item"><a href="">用戶</a></li>
            <li class="layui-nav-item">
                <a href="javascript:;">其它系統</a>
                <dl class="layui-nav-child">
                    <dd><a href="">郵件管理</a></dd>
                    <dd><a href="">消息管理</a></dd>
                    <dd><a href="">授權管理</a></dd>
                </dl>
            </li>
        </ul>
        <ul class="layui-nav layui-layout-right">
            <li class="layui-nav-item">
                <a href="javascript:;">
                    <img src="http://t.cn/RCzsdCq" class="layui-nav-img">
                    賢心
                </a>
                <dl class="layui-nav-child">
                    <dd><a href="">基本資料</a></dd>
                    <dd><a href="">安全設置</a></dd>
                </dl>
            </li>
            <li class="layui-nav-item"><a href="">退了</a></li>
        </ul>
    </div>

    <div class="layui-side layui-bg-black">
        <div class="layui-side-scroll">
            <!-- 左側導航區域(可配合layui已有的垂直導航) -->
            <ul class="layui-nav layui-nav-tree"  lay-filter="test">
                <li class="layui-nav-item layui-nav-itemed">
                    <a class="" href="javascript:;">所有商品</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="javascript:;">列表三</a></dd>
                        <dd><a href="">超鏈接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item">
                    <a href="javascript:;">解決方案</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="">超鏈接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item"><a href="">雲市場</a></li>
                <li class="layui-nav-item"><a href="">發佈商品</a></li>
            </ul>
        </div>
    </div>

    <div class="layui-body">
        <!-- 內容主體區域 -->
        <div style="padding: 15px;">內容主體區域</div>
    </div>

    <div class="layui-footer">
        <!-- 底部固定區域 -->
        © layui.com - 底部固定區域
    </div>
</div>
<script src="<%=request.getContextPath()%>/media/layui/layui.js"></script>
<script>
    //JavaScript代碼區域
    layui.use('element', function(){
        var element = layui.element;

    });
</script>
</body>
</html>

登錄頁面效果
在這裏插入圖片描述

7、Tomcat配置與運行

配置tomcat並運行代碼,在瀏覽器打開http://localhost:8080/login.html能打開並且可登錄,表示初運行成功。

四、用戶登錄完善

1、配置攔截器

1)、創建interceptor包,創建攔截器類實現HandlerInterceptorAdapter接口的preHandle方法

package com.booy.ssm.exam.interceptor;

import com.booy.ssm.exam.utils.ExamConstants;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(request.getSession().getAttribute(ExamConstants.SESSION_USER)==null){
            response.sendRedirect("/login.html");
            return false;
        }
        return true;
    }
}

2)、springMVC-servlet.xml中配置攔截器規則

<!--攔截器配置-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <mvc:exclude-mapping path="/login.html"/>
        <mvc:exclude-mapping path="/dologin.html"/>
        <bean class="com.booy.ssm.exam.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
2、登錄退出實現

SystemController添加登錄退出方法

//用戶註銷
@RequestMapping("logout.html")
public String logout(HttpSession session){
    session.invalidate();
    return "login";
}
3、展示登錄暱稱

在index.jsp中找到暱稱位置修改爲:

${sessionScope.SESSION_USER.nickname}
4、動態鹽加密處理

創建MD5Utils工具類

package com.booy.ssm.exam.utils;

import org.springframework.util.DigestUtils;

public class MD5Utils {
    //添加時生成密碼
    public static String getDigestMD5(String account, String password){
        String salt = DigestUtils.md5DigestAsHex(account.getBytes());
        return DigestUtils.md5DigestAsHex((salt + password).getBytes());
    }
    //登錄時的密碼
    public static String getLoginMD5(String salt, String password){
        return DigestUtils.md5DigestAsHex((salt + password).getBytes());
    }
}

UserServiceImpl實現類

@Override
public User dologin(String account, String password) {
    User user = userDAO.getUserByAccount(account);
    String loginMD5 = MD5Utils.getLoginMD5(user.getSalt(), password);
    System.out.println("加密後密碼爲"+loginMD5);
    System.out.println("加密後密碼爲"+user.getPassword());
    if(user==null || !user.getPassword().equals(loginMD5) || user.getStatus().equals(ExamConstants.USER_STATUS_DELETE)){
        return null;//賬號或密碼錯誤
    }
    return user;
}

五、導航展示

1、dao層

1)、創建MenuDAO接口

package com.booy.ssm.exam.dao;

import com.booy.ssm.exam.pojo.Menu;

import java.util.List;

public interface MenuDAO {
    List<Menu> getAllMenu();
}

2)、創建MenuMapper.xml映射sql查詢:

<?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">
<mapper namespace="com.booy.ssm.exam.dao.MenuDAO">
    <select id="getAllMenu" resultType="Menu">
        select id,name,parent_id parentId,url,sort from menu
    </select>
</mapper>

新增Menu實體類屬性

private List<Menu> children=new ArrayList<>();
2、service層

創建MenuService接口:

package com.booy.ssm.exam.service;

import com.booy.ssm.exam.pojo.Menu;

import java.util.List;

public interface MenuService {
    List<Menu> getMenuTree();
}

創建MenuServiceInpl實現類:

package com.booy.ssm.exam.service.impl;

import com.booy.ssm.exam.dao.MenuDAO;
import com.booy.ssm.exam.pojo.Menu;
import com.booy.ssm.exam.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class MenuServiceImpl implements MenuService {
    @Autowired
    MenuDAO menuDAO;

    @Override
    public List<Menu> getMenuTree() {
        //所有菜單對象
        List<Menu> menus = menuDAO.getAllMenu();
        //一級菜單
        List<Menu> fistMenus=new ArrayList<>();
        //所有菜單鍵值對存儲
        HashMap<Integer, Menu> menuMap = new HashMap<>();
        //遍歷所有菜單對象,沒有父節點存儲到一級菜單列表,並且將所有菜單對象存儲到map中
        for (Menu menu : menus) {
            if (menu.getParentId() == null) {
                fistMenus.add(menu);
            }
            menuMap.put(menu.getId(), menu);
        }
        //填充拼接菜單樹,遍歷所有菜單對象,有父節點存儲到屬性子節點列表
        for (Menu menu : menus){
            //不是一級菜單,並且map中有父節點,把當前菜單對象設置給父節點列表中
            if(menu.getParentId()!=null && menuMap.containsKey(menu.getParentId())){
                menuMap.get(menu.getParentId()).getChildren().add(menu);
            }
        }
        return fistMenus;
    }
}
3、controller層

新增SystemController方法

    @Autowired
    MenuService menuService;
//主頁
@RequestMapping("index.html")
public String index(Model model){
    List<Menu> menuList = menuService.getMenuTree();
    model.addAttribute("menuList",menuList);
    return "index";
}

菜單樹顯示效果

在這裏插入圖片描述

4、jsp頁面配置

pom.xml中添加jstl依賴

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

index.jsp頁面

<div class="layui-side layui-bg-black">
    <div class="layui-side-scroll">
        <!-- 左側導航區域(可配合layui已有的垂直導航) -->
        <ul class="layui-nav layui-nav-tree" lay-filter="test">
            <c:forEach items="${requestScope.menuList}" var="parent">
                <li class="layui-nav-item layui-nav-itemed">
                    <a class="" href="javascript:;">${parent.name}</a>
                    <c:if test="${not empty parent.children}">
                        <dl class="layui-nav-child">
                            <c:forEach items="${parent.children}" var="c">
                                <dd><a href="javascript:;" >${c.name}</a></dd>
                            </c:forEach>
                        </dl>
                    </c:if>
                </li>
            </c:forEach>
        </ul>
    </div>
</div>
5、頁面局部加載

1)、創建UserController

package com.booy.ssm.exam.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/sys/user.html")
public class UserController {
    @RequestMapping
    public String user(){
        return "User";
    }
}

2)、創建user.jsp頁面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
我是用戶管理頁面

3)、新增index.jsp頁面加載局部js

<dd><a href="javascript:;" οnclick="$('#main-content').load('${c.url}')">${c.name}</a></dd>

六、實現用戶管理功能

用戶管理功能:新增用戶、修改用戶,刪除用戶,條件查詢用戶

1、dao層

新增UserDAO接口刪除用戶、添加用戶、修改用戶和查詢用戶列表方法

    void addUser(User user);
    void updateUser(User user);
    List<User> getUserList(User user);
    void deleteUser(int id);

新增UserMapper.xml映射sql查詢:

    <insert id="addUser" parameterType="User" >
        insert into user(account, password,nickname) values(#{account},#{password},#{nickname})
    </insert>
    <update id="updateUser" parameterType="User">
        update user <set>
        <if test="password!=null and password!=''">password=#{password},</if>
        <if test="nickname!=null and nickname!=''">nickname=#{nickname},</if>
        <if test="status!=null">status=#{status}</if>
    </set>
    where id=#{id}
    </update>
    <update id="deleteUser" parameterType="int">
        update user set status=0 where id=#{id}
    </update>
    <select id="getUserList" parameterType="User" resultType="User">
        select id, account, password,nickname,status
        from user
        <where>
            <if test="account!=null and account!=''">and account like concat('%',#{account},'%')</if>
            <if test="nickname!=null and nickname!=''">
                <bind name="temp" value="'%'+nickname+'%'"/>
                and nickname like #{temp}
            </if>
        </where>
    </select>
2、返回狀態工具類

AjaxResult,新增、修改和刪除時給前端的返回狀態值

package com.booy.ssm.exam.utils;

import lombok.Data;

@Data
public class AjaxResult {
    private boolean status;//是否成功
    private String message;//提示信息
    private Object result;//成功信息

    public AjaxResult() {
    }

    public AjaxResult(boolean status, String message, Object result) {
        this.status = status;
        this.message = message;
        this.result = result;
    }
}
3、service層

新增UserService接口方法:

    AjaxResult addUser(User user);
    AjaxResult updateUser(User user);
    PageInfo<User> getUserList(User user,int pageNum,int pageSize);
    AjaxResult deleteUser(int[] ids);

新增UserServiceImpl實現類方法:

    @Override
    public AjaxResult addUser(User user) {
        AjaxResult result = new AjaxResult();
        if(userDAO.getUserByAccount(user.getAccount())==null ){
            userDAO.addUser(user);
            result.setStatus(true);
            return result;
        }
        result.setStatus(false);
        result.setMessage("用戶名已存在!");
        return result;
    }

    @Override
    public AjaxResult updateUser(User user) {
        AjaxResult result = new AjaxResult();
        try {
            System.out.println("user="+user);
            userDAO.updateUser(user);
            result.setStatus(true);
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage("更新失敗!");
        }
        return result;
    }

    @Override
    public PageInfo<User> getUserList(User user, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum,pageSize);
        PageInfo<User> users = new PageInfo<>(userDAO.getUserList(user));
        return users;
    }

    @Override
    public AjaxResult deleteUser(int[] ids) {
        AjaxResult result = new AjaxResult();
        for(int id:ids){
            userDAO.deleteUser(id);
        }
        result.setStatus(true);
        return result;
    }
4、controller層

新增UserController數據操作功能

@RequestMapping(params = "act=edit")
@ResponseBody
public AjaxResult edit(User user){
    //添加
    if(user.getId()==null){
        AjaxResult result = userService.addUser(user);
        return result;
    }else{//修改
        AjaxResult result = userService.updateUser(user);
        return result;
    }
}
@RequestMapping(params = "act=delete")
@ResponseBody
public AjaxResult deleteUser(int[] ids){
    try {
        AjaxResult result = userService.deleteUser(ids);
        result.setStatus(true);
        return result;
    } catch (Exception e) {
        e.printStackTrace();
        AjaxResult result = new AjaxResult();
        result.setStatus(false);
        result.setMessage("刪除失敗!");
        return result;
    }
}
5、動態表格數據渲染

1)、新增user.jsp頁面動態表格

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<table id="userTable" lay-filter="test"></table>

<script>
    layui.use('table', function(){
       //初始化table對象,當前頁面所有table都歸lay管理,以id區分
        var table = layui.table;

        table.render({
            elem: '#userTable'//渲染的table
            ,height: 668
            ,url: '/sys/user.html?act=table' //數據接口
            ,page: true //開啓分頁
            ,limit:15
            ,limits:[15,30,45,60,75,90]
            ,cols: [[ //表頭
                {field: 'id', title: 'ID', width:80,  fixed: 'left'}
                ,{field: 'account', title: '用戶名'}
                ,{field: 'nickname', title: '暱稱'}
            ]]
        });

    });
</script>

2)、創建封裝返回數據的工具類TableData

import lombok.Data;

import java.util.List;

//數據封裝
@Data
public class TableData<T> {
    private int code=0;
    private String msg="";
    private long count;
    private List<T> data;

    public TableData() {
    }

    public TableData(long count, List<T> data) {
        this.count = count;
        this.data = data;
    }
}

3)、新增返回的數據接口

import com.booy.ssm.exam.pojo.User;
import com.booy.ssm.exam.service.UserService;
import com.booy.ssm.exam.utils.TableData;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/sys/user.html")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping
    public String user(){
        return "user";
    }

    @RequestMapping(params = "act=table")
    @ResponseBody
    public TableData table(User user,int page,int limit){
        PageInfo<User> pageInfo = userService.getUserList(user, page, limit);
        TableData tableData = new TableData(pageInfo.getTotal(), pageInfo.getList());
        return tableData;
    }
}

4)、在pom中添加json依賴

注意json版本和spring版本的匹配

<!--json依賴-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>

5)、在springMVC中配置json數據轉換

<!--配置返回值轉換器-->
<bean id="contentNegotiationManagerFactoryBean"
      class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <!--是否支持後綴匹配-->
    <property name="favorPathExtension" value="false"/>
    <!--是否支持參數匹配-->
    <property name="favorParameter" value="false"/>
    <!--是否 accept-header 匹配-->
    <property name="ignoreAcceptHeader" value="false"/>
    <property name="mediaTypes">
        <map>
            <!--表示.json 結尾的請求返回 json-->
            <entry key="json" value="application/json"/>
        </map>
    </property>
</bean>

啓用json

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManagerFactoryBean"/>

用戶數據顯示效果:
在這裏插入圖片描述

6、條件檢索功能實現

新增user.jsp中搜索框,表格重載渲染

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--頂部搜索--%>
<div class="layui-form-item">
    <form class="layui-form">
        <div class="layui-inline">
            <label class="layui-form-label">用戶名:</label>
            <div class="layui-input-inline" style="width: 200px;">
                <input type="text" name="account" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-inline">
            <label class="layui-form-label">暱稱:</label>
            <div class="layui-input-inline" style="width: 200px;">
                <input type="text" name="nickname" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-inline">
            <button class="layui-btn" lay-submit  lay-filter="searchForm">查詢</button>
        </div>
    </form>

</div>
<%--動態表格--%>
<table id="userTable" lay-filter="test"></table>

<script>
    layui.use(['table','form'], function(){
        //初始化table對象,當前頁面所有table都歸lay管理,以id區分
        var table = layui.table;
        var form = layui.form;

        table.render({
            id:'userTable'//渲染後的table
            ,elem: '#userTable'//渲染的table
            ,height: 668
            ,url: '/sys/user.html?act=table' //數據接口
            ,page: true //開啓分頁
            ,limit:15
            ,limits:[15,30,45,60,75,90]
            ,cols: [[ //表頭
                {field: 'id', title: 'ID', width:80,  fixed: 'left'}
                ,{field: 'account', title: '用戶名'}
                ,{field: 'nickname', title: '暱稱'}
            ]]
        });
        form.on('submit(searchForm)', function(data){
            console.log(data.field);//當前容器的全部表單字段,名值對形式:{name: value}
            table.reload('userTable', {
                where: data.field //設定異步數據接口的額外參數
                //,height: 300
            });
            return false;
        });
    });
</script>

條件搜索效果:
在這裏插入圖片描述

7、頁面頭部工具欄實現

新增User.jsp代碼

1)、添加複選框

table.render裏的表頭第一行添加如下代碼:

{type: 'checkbox', fixed: 'left'}

2)、添加頭部工具欄

<script type="text/html" id="headtool">
    <div class="layui-btn-container">
       <button class="layui-btn layui-btn-sm " lay-event="add"><i class="layui-icon">&#xe654;</i>添加</button>
        <button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="update"><i class="layui-icon">&#xe642;</i>編輯</button>
        <button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete"><i class="layui-icon">&#xe640;</i>刪除</button>
    </div>
</script>

在table.render中調用以上代碼

//JS 調用:
table.render({
elem: '#demo'
,toolbar: '#headtool'
//,…… //其他參數
});

複選框及工具欄顯示效果
在這裏插入圖片描述

3)、彈出層代碼塊

<%--添加功能彈出層--%>
<script type="text/html" id="editFormlayer">
    <form class="layui-form" style="width:80%;padding-top: 20px" id="editForm" lay-filter="editForm">
        <input type="hidden" name="id">
        <div class="layui-form-item">
            <label class="layui-form-label">用戶名:</label>
            <div class="layui-input-block">
                <input type="text" name="account" required lay-verify="required" placeholder="請輸入用戶名" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">密碼:</label>
            <div class="layui-input-inline">
                <input type="password" name="password" required lay-verify="required" placeholder="請輸入密碼"
                       autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">暱稱:</label>
            <div class="layui-input-block">
                <input type="text" name="nickname" required lay-verify="required" placeholder="請輸入暱稱" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">狀態</label>
            <div class="layui-input-block">
                <input type="radio" name="status" value="1" title="有效">
                <input type="radio" name="status" value="0" title="無效">
            </div>
        </div>
    </form>
</script>

4)、頭部工具條js

頭部工具條具體實現的js,主要爲事件監聽、打開彈出層及json傳輸數據

<%--工具條--%>
<script>
    layui.use(['table', 'form'], function () {
        //初始化table對象,當前頁面所有table都歸lay管理,以id區分
        var table = layui.table;
        var form = layui.form;

        table.render({
            id: 'userTable'//設置渲染後的table
            , elem: '#userTable'//根據id取首次渲染的table
            , toolbar: '#headtool'//頭部工具條
            , height: 600
            , url: '/sys/user.html?act=table' //數據接口
            , page: true //開啓分頁
            , cols: [[ //表頭
                {type: 'checkbox', fixed: 'left'}
                , {field: 'id', title: 'ID', width: 80}
                , {field: 'account', title: '用戶名'}
                , {field: 'nickname', title: '暱稱'}
                , {field: 'status', title: '狀態' ,templet: function(d){
                        return d.status==1?'<span class="layui-badge layui-bg-green">正常</span>':'<span class="layui-badge layui-bg-gray">停用</span>'
                    }}
                ,{fixed: 'right', width:150, align:'center', toolbar: '#rowtool'}
            ]]
        });
        //添加搜索渲染
        form.on('submit(searchForm)', function (data) {
            console.log(data.field);//當前容器的全部表單字段,名值對形式:{name: value}
            table.reload('userTable', {//渲染後的tableid
                where: data.field //設定異步數據接口的額外參數
                //,height: 300
            });
            return false;
        });
        //監聽頭部工具條事件
        table.on('toolbar(userTable)', function (obj) {
            var checkStatus = table.checkStatus(obj.config.id);
            var data = checkStatus.data;//獲取選中行數據
            switch (obj.event) {
                case 'add':
                    if (data.length>0) {
                        return;
                    }
                    openidielayer(data);//null
                    break;
                case 'delete':
                    if (data.length<1) {
                        return;
                    }
                    layer.confirm('你確定要刪除嗎?', {
                        btn: ['確定','取消'] //按鈕
                    }, function(index){
                        var ids = new Array();
                        for (var i = 0; i < data.length; i++) {
                            ids.push(data[i].id)
                        }
                        $.ajax({
                            url: "/sys/user.html?act=delete",
                            method: "post",
                            data: "ids="+ids,//jquery獲取表單內容
                            success: function (res) {
                                if (res.status) {
                                    table.reload('userTable', {});//刪除數據後刷新table
                                } else {
                                    layer.msg(res.message);
                                }
                            }
                        })
                        layer.close(index)
                    })

                    break;
                case 'update':
                    if (data.length != 1) {
                        layer.msg('請選中一行數據');
                        return;
                    }
                    openidielayer(data[0]);
                    break;
            }
            ;
        });
        //打開彈出層,添加編輯共用
        function openidielayer(data) {//打開後往表單裏添加數據
            layer.open({
                type: 1
                , area: '500px'
                , content: $("#editFormlayer").html()
                , btn: ['確認', '取消']
                , yes: function (index, layero) {
                    $.ajax({
                        url: "/sys/user.html?act=edit",
                        method: "post",
                        data: $("#editForm").serialize(),//jquery獲取表單內容
                        success: function (res) {
                            if (res.status) {
                                table.reload('userTable', {});//添加數據後刷新table
                                layer.close(index);
                            } else {
                                layer.msg(res.message);
                            }
                        }
                    })
                }
                , btn2: function (index, layero) {
                    layer.close(index)
                }, success: function (layero, index) {
                    console.log(data);
                    form.render();//重新渲染table
                    form.val("editForm",data);//獲取array的第0個元素渲染表單
                    form.val("editForm",{status:data.status+""});//數字轉字符串才能被渲染
                }
            });
        }
    });
</script>
8、頁面數據行工具條實現

新增User.jsp代碼

1)、頁面展示

<%--行工具條--%>
<script type="text/html" id="rowtool">
    <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">編輯</a>
    <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">刪除</a>
</script>

2)、新增js片段

//監聽行工具條
table.on('tool(userTable)', function(obj) { //注:tool 是工具條事件名,test 是 table 原始容器的屬性 lay-filter="對應的值"
    var data = obj.data; //獲得當前行數據
    var layEvent = obj.event; //獲得 lay-event 對應的值
    switch (layEvent) {
        case 'del':
            layer.confirm('你確定要刪除嗎?', {
                btn: ['確定','取消'] //按鈕
            }, function(index){
                $.ajax({
                    url: "/sys/user.html?act=delete",
                    method: "post",
                    data: "ids="+data.id,//獲取當前行的id
                    success: function (res) {
                        if (res.status) {
                            table.reload('userTable', {});//刪除數據後刷新table
                        } else {
                            layer.msg(res.message);
                        }
                    }
                })
                layer.close(index)
            })

            break;
        case 'edit':
            openidielayer(data);
            break;
    }
})
9、頁面效果

頁面全局效果

在這裏插入圖片描述

添加功能

複選框沒有選中的情況才能打開

在這裏插入圖片描述

頭部修改功能

只能選中一行且必須選中一行才能修改,多選或不選給提示,右側則直接彈出層

在這裏插入圖片描述

刪除功能

頭部工具條多條批量刪除,右側工具條刪除當前條

在這裏插入圖片描述

七、實現菜單管理功能

1、菜單樹顯示

1)、MenuController向頁面返回菜單樹

@RequestMapping(params = "act=tree")
@ResponseBody
public List<Menu> MenuTree(Boolean needButton){
    return menuService.getMenuTree(true);
}

此處更改了MenuService接口和實現類的菜單樹方法重載,當方法爲true需要按鈕菜單,index頁面不需要按鈕菜單,而菜單管理頁面需要

List<Menu> getMenuTree(Boolean needButton);

2)、引入EasyUI的js和css文件

<script src="media/jquery.easyui.min.js"/>
<link rel="stylesheet" href="media/easyui.css">

3)、頁面標籤:

<button class="layui-btn layui-btn-sm" id="addMenu"><i class="layui-icon">&#xe654;</i>添加</button>
<button class="layui-btn layui-btn-sm layui-btn-danger" id="delMenu"><i class="layui-icon">&#xe640;</i>刪除</button>
<ul id="menu-tree" class="easyui-tree">
</ul>

4)、菜單樹顯示js

<script>
    layui.use(['form'], function () {
        $('#menu-tree').tree({
            url: "sys/menu.html?act=tree&needButton=true",//數據接口
            checkbox: true,
            formatter: function (node) {
                return node.name;//獲取節點的內容
            }
        })
      })

顯示效果:
在這裏插入圖片描述

2、dao層

新增MenuDAO接口增刪改方法

void addMenu(Menu menu);
void updateMenu(Menu menu);
void deleteMenu(Integer id);

新增MenuMapper.xml增刪改映射sql:

<insert id="addMenu" parameterType="Menu">
    insert into menu(name, parent_id, url, type) VALUES(#{name},#{parentId},#{url},#{type})
</insert>
<update id="updateMenu" parameterType="Menu">
    update menu set
        <if test="name!=null and name!=''">name=#{name},</if>
        <if test="parentId!=null and parentId!=''">parent_id=#{parentId},</if>
        <if test="url!=null and url!=''">url=#{url},</if>
        <if test="type!=null">type=#{type}</if>
         where id=#{id}
</update>
<delete id="deleteMenu" parameterType="int">
    delete from menu where id=#{id}
</delete>
3、service層

新增MenuService接口增刪改方法

void addMenu(Menu menu);
void updateMenu(Menu menu);
void deleteMenu(int[] ids);

新增MenuServiceImpl實現類增刪改方法

@Override
public void addMenu(Menu menu) {
    menuDAO.addMenu(menu);
}

@Override
public void updateMenu(Menu menu) {
    menuDAO.updateMenu(menu);
}

@Override
public void deleteMenu(int[] ids) {
    for(int id:ids){
        menuDAO.deleteMenu(id);
    }
}
4、controller層

新增MenuController增刪改方法

@RequestMapping(params = "act=edit")
@ResponseBody
public AjaxResult edit(Menu menu){
    AjaxResult result = new AjaxResult();
    //添加功能
    try {
        if(menu.getId()==null){
            menuService.addMenu(menu);
        }else{
            menuService.updateMenu(menu);
        }
        result.setStatus(true);
    } catch (Exception e) {
        e.printStackTrace();
        result.setStatus(false);
        result.setMessage("編輯失敗!");
    }
    return result;
}
@RequestMapping(params = "act=delete")
@ResponseBody
public AjaxResult deleteMenu(int[] ids){
    AjaxResult result = new AjaxResult();
    try {
        menuService.deleteMenu(ids);
        result.setStatus(true);
    } catch (Exception e) {
        e.printStackTrace();
        result.setStatus(false);
        result.setMessage("刪除失敗!");
    }
    return result;
}

5、頁面處理

1)、添加功能彈出層代碼塊

<%--添加功能彈出層--%>
<script type="text/html" id="editFormlayer">
    <form class="layui-form" style="width:80%;padding-top: 20px" id="editForm" lay-filter="editForm">
        <input type="hidden" name="id">
        <div class="layui-form-item">
            <label class="layui-form-label">菜單名</label>
            <div class="layui-input-block">
                <input type="text" name="name" required lay-verify="required" placeholder="請輸入菜單名" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">父節點</label>
            <div class="layui-input-block">
                <input type="text" name="parentId" id="parentId">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">url</label>
            <div class="layui-input-block">
                <input type="text" name="url" placeholder="請輸入url" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">狀態</label>
            <div class="layui-input-block">
                <input type="radio" name="type" value="0" checked title="目錄">
                <input type="radio" name="type" value="1" title="鏈接">
                <input type="radio" name="type" value="2" title="按鈕">
            </div>
        </div>
    </form>
</script>

2)、菜單管理模塊js

<script>
    layui.use(['form'], function () {
        var form = layui.form;//初始化form
        $('#menu-tree').tree({
            url: "sys/menu.html?act=tree&needButton=true",//數據接口
            checkbox: true,
            formatter: function (node) {
                return node.name;//獲取節點的內容
            }, onClick: function (node) {
                openidielayer(node)
            }
        })
        $('#addMenu').click(function () {
            openidielayer(null)
        })
        $('#delMenu').click(function () {
            layer.confirm('你確定要刪除嗎?', {
                btn: ['確定','取消'] //按鈕
            }, function(index){
                var nodes = $('#menu-tree').tree('getChecked');
                var ids=new Array();
                for (var i = 0; i <nodes.length ; i++) {
                    ids.push(nodes[i].id);
                }
                $.ajax({
                    url: "/sys/menu.html?act=delete",
                    method: "post",
                    data: "ids="+ids,
                    success: function (res) {
                        if (res.status) {
                            $('#menu-tree').tree('reload');
                        } else {
                            layer.msg(res.message);
                        }
                    }
                })
                layer.close(index)
            })
        })

        //打開彈出層,添加編輯共用
        function openidielayer(data) {//打開後往表單裏添加數據
            layer.open({
                type: 1
                , zIndex: 10000
                , area: '500px'
                , content: $("#editFormlayer").html()
                , btn: ['確認', '取消']
                , yes: function (index, layero) {
                    $.ajax({
                        url: "/sys/menu.html?act=edit",
                        method: "post",
                        data: $("#editForm").serialize(),//jquery獲取表單內容
                        success: function (res) {
                            if (res.status) {
                                $('#menu-tree').tree('reload');
                                layer.close(index);
                            } else {
                                layer.msg(res.message);
                            }
                        }
                    })
                }
                , btn2: function (index, layero) {
                    layer.close(index)
                }, success: function (layero, index) {
                    console.log(data);
                    form.render();//重新渲染table
                    if (data != null) {
                        form.val("editForm", data);//獲取array的元素渲染表單
                        form.val("editForm", {type: data.type + '',});//數字轉字符串才能被渲染
                    }
                    //表單初始化完成後初始化input
                    $('#parentId').combotree({
                        url: 'sys/menu.html?act=tree&needButton=false',
                        required: true
                    });
                }
            });
        }
    })
</script>
5、頁面效果

添加菜單

在這裏插入圖片描述

刪除菜單

在這裏插入圖片描述

編輯菜單

在這裏插入圖片描述

八、實現角色管理功能

1、dao層

創建Role實體類

package com.booy.ssm.exam.pojo;

import lombok.Data;

@Data
public class Role {
    private Integer id;
    private String name;
    private String remark;
    private Integer status;
}

創建RoleDAO接口

package com.booy.ssm.exam.dao;

import com.booy.ssm.exam.pojo.Role;

import java.util.List;

public interface RoleDAO {
    List<Role>  getRoleList();
    List<Role> getRoleListByIF(Role role);
    void addRole(Role role);
    void updateRole(Role role);
    void deleteRole(Integer id);
}

創建RoleMapper映射sql

<?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">
<mapper namespace="com.booy.ssm.exam.dao.RoleDAO">
    <select id="getRoleList" resultType="Role">
        select id, name, remark, status
        from role
    </select>
    <select id="getRoleListByIF" parameterType="Role" resultType="Role">
        select id,name,remark,status from role
        <where>
            <if test="name!=null and name!=''">and name like concat('%',#{name},'%')</if>
            <if test="remark!=null and remark!=''">and remark like concat('%',#{remark},'%')</if>
        </where>
    </select>
    <insert id="addRole" parameterType="Role">
        insert into role (name, remark, status)
        VALUES (#{name}, #{remark}, #{status})
    </insert>
    <update id="updateRole" parameterType="Role">
        update role set
        <if test="name!=null and name!=''">name=#{name},</if>
        <if test="remark!=null and remark!=''">remark=#{remark},</if>
        <if test="status!=null">status=#{status}</if>
        where id=#{id}
    </update>
    <delete id="deleteRole" parameterType="int">
        delete from role where id=#{id}
    </delete>
</mapper>
2、service層

創建RoleService接口

package com.booy.ssm.exam.service;

import com.booy.ssm.exam.pojo.Role;
import com.github.pagehelper.PageInfo;

import java.util.List;

public interface RoleService {
    //角色列表
    PageInfo<Role> getRoleList(Role role, int pageNum,int pageSize);
    //獲取所有角色,角色樹用
    List<Role> getRoleList();
    //增刪改
    void addRole(Role role);
    void updateRole(Role role);
    void deleteRole(Integer[] roleIds);
    //角色菜單功能
    void addRoleMenu(Integer roleId,Integer[] MenuIds);
    List<Integer> getMenuByRoleId(Integer roleId);
}

創建RoleServiceImpl實現類

package com.booy.ssm.exam.service.impl;

import com.booy.ssm.exam.dao.PremissionDAO;
import com.booy.ssm.exam.dao.RoleDAO;
import com.booy.ssm.exam.pojo.Menu;
import com.booy.ssm.exam.pojo.Role;
import com.booy.ssm.exam.service.RoleService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

@Service
public class RoleServiceImpl implements RoleService {
    @Autowired
    private RoleDAO roleDAO;

    @Autowired
    private PremissionDAO premissionDAO;

    @Override
    public PageInfo<Role> getRoleList(Role role,int pageNum,int pageSize) {
        PageHelper.startPage(pageNum,pageSize);
        PageInfo<Role> roles = new PageInfo<>(roleDAO.getRoleListByIF(role));
        return roles;
    }
//重改方法名
    @Override
    public List<Role> getRoleList() {
       return roleDAO.getRoleList();
    }
//增刪改查

    @Override
    public void addRole(Role role) {
        roleDAO.addRole(role);
    }

    @Override
    public void updateRole(Role role) {
        roleDAO.updateRole(role);
    }

    @Override
    public void deleteRole(Integer[] roleIds) {
        for(Integer roleId:roleIds){
            roleDAO.deleteRole(roleId);
        }
    }

    @Override
    public void addRoleMenu(Integer roleId, Integer[] MenuIds) {
        premissionDAO.deleteRoleMenuByRoleId(roleId);
        for(int menu:MenuIds){
            premissionDAO.addRoleMenu(roleId,menu);
        }
    }

    @Override
    public List<Integer> getMenuByRoleId(Integer roleId) {
        List<Integer> menuIds = premissionDAO.getMenuByRoleId(roleId);
        return menuIds;
    }
}
3、controller
package com.booy.ssm.exam.controller;

import com.booy.ssm.exam.pojo.Menu;
import com.booy.ssm.exam.pojo.Role;
import com.booy.ssm.exam.service.MenuService;
import com.booy.ssm.exam.service.RoleService;
import com.booy.ssm.exam.utils.AjaxResult;
import com.booy.ssm.exam.utils.TableData;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("/sys/role.html")
public class RoleController {
    @Autowired
    private RoleService roleService;

    @Autowired
    private MenuService menuService;
    @RequestMapping
    public String user(){
        return "role";
    }

    @RequestMapping(params = "act=table")
    @ResponseBody
    public TableData<Role> role(Role role, int page,int limit){
        PageInfo<Role> roleList = roleService.getRoleList(role,page, limit);
        TableData<Role> tableData = new TableData<>(roleList.getTotal(), roleList.getList());
        return tableData;
    }

    @RequestMapping(params = "act=tree")
    @ResponseBody
    public List<Menu> MenuTree(){
        return menuService.getMenuTree(true);
    }

    @RequestMapping(params = "act=assign")
    @ResponseBody
    public AjaxResult assign(Integer roleId, Integer[] menuIds){
        AjaxResult result = new AjaxResult();
        try {
            roleService.addRoleMenu(roleId,menuIds);
            result.setStatus(true);
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage("授權失敗!");
        }
        return result;
    }
    @RequestMapping(params = "act=menuIds")
    @ResponseBody
    public List<Integer> menuIds(Integer roleId){
        List<Integer> menuIds = roleService.getMenuByRoleId(roleId);
        return menuIds;
    }
    @RequestMapping(params = "act=edit")
    @ResponseBody
    public AjaxResult edit(Role role){
        AjaxResult result = new AjaxResult();
        try {
            if(role.getId()==null){
                roleService.addRole(role);
                result.setStatus(true);
            }else{
                roleService.updateRole(role);
                result.setStatus(true);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage("編輯失敗!");
            return result;
        }
    }

    @RequestMapping(params = "act=delete")
    @ResponseBody
    public AjaxResult deleteRole(Integer[] roleIds){
        AjaxResult result = new AjaxResult();
        try {
            roleService.deleteRole(roleIds);
            result.setStatus(true);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setStatus(false);
            result.setMessage("刪除失敗!");
            return result;
        }
    }
}
4、role.jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script src="media/jquery.easyui.min.js"/>
<link rel="stylesheet" href="media/easyui.css">
<%--頂部搜索--%>
<div class="layui-form-item">
    <form class="layui-form" >
        <div class="layui-inline">
            <label class="layui-form-label">角色名:</label>
            <div class="layui-input-inline" style="width: 200px;">
                <input type="text" name="name" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-inline">
            <label class="layui-form-label">別名:</label>
            <div class="layui-input-inline" style="width: 200px;">
                <input type="text" name="remark" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-inline">
            <button class="layui-btn" lay-submit lay-filter="searchForm">查詢</button>
        </div>
    </form>

</div>
<%--動態表格--%>
<table id="roleTable" lay-filter="roleTable"></table>
<%--表格頭部工具條--%>
<script type="text/html" id="headtool">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm " lay-event="add"><i class="layui-icon">&#xe654;</i>添加</button>
        <button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="update"><i class="layui-icon">&#xe642;</i>編輯
        </button>
        <button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="delete"><i class="layui-icon">&#xe640;</i>刪除
        </button>
    </div>
</script>
<%--行工具條--%>
<script type="text/html" id="rowtool">
    <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="assign">授權</a>
    <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">編輯</a>
    <a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">刪除</a>
</script>

<%--工具條--%>
<script>
    layui.use(['table', 'form'], function () {
        //初始化table對象,當前頁面所有table都歸lay管理,以id區分
        var table = layui.table;
        var form = layui.form;

        table.render({
            id: 'roleTable'//設置渲染後的table
            , elem: '#roleTable'//根據id取首次渲染的table
            , toolbar: '#headtool'//頭部工具條
            , height: 523
            , url: '/sys/role.html?act=table' //數據接口
            , page: true //開啓分頁
            , cols: [[ //表頭
                {type: 'checkbox', fixed: 'left'}
                , {field: 'id', title: 'ID', width: 80}
                , {field: 'name', title: '角色名'}
                , {field: 'remark', title: '別名'}
                , {field: 'status', title: '狀態' ,templet: function(d){
                        return d.status==1?'<span class="layui-badge layui-bg-green">正常</span>':'<span class="layui-badge layui-bg-gray">停用</span>'
                    }}
                ,{fixed: 'right', width:200, align:'center', toolbar: '#rowtool'}
            ]]
        });
        //添加搜索渲染
        form.on('submit(searchForm)', function (data) {
            console.log(data.field);//當前容器的全部表單字段,名值對形式:{name: value}
            table.reload('roleTable', {//渲染後的tableid
                where: data.field //設定異步數據接口的額外參數
                //,height: 300
            });
            return false;
        });
        //監聽頭部工具條事件
        table.on('toolbar(roleTable)', function (obj) {
            var checkStatus = table.checkStatus(obj.config.id);
            var data = checkStatus.data;//獲取選中行數據
            switch (obj.event) {
                case 'add':
                    if (data.length>0) {
                        return;
                    }
                    openidielayer(data);//null
                    break;
                case 'delete':
                    if (data.length<1) {
                        return;
                    }
                    layer.confirm('你確定要刪除嗎?', {
                        btn: ['確定','取消'] //按鈕
                    }, function(index){
                        var ids = new Array();
                        for (var i = 0; i < data.length; i++) {
                            ids.push(data[i].id)
                        }
                        $.ajax({
                            url: "/sys/role.html?act=delete",
                            method: "post",
                            data: "roleIds="+ids,//jquery獲取表單內容
                            success: function (res) {
                                if (res.status) {
                                    table.reload('roleTable', {});//刪除數據後刷新table
                                } else {
                                    layer.msg(res.message);
                                }
                            }
                        })
                        layer.close(index)
                    })

                    break;
                case 'update':
                    if (data.length != 1) {
                        layer.msg('請選中一行數據');
                        return;
                    }
                    openidielayer(data[0]);
                    break;
            }
            ;
        });
        //監聽行工具條
        table.on('tool(roleTable)', function(obj) { //注:tool 是工具條事件名,test 是 table 原始容器的屬性 lay-filter="對應的值"
            var data = obj.data; //獲得當前行數據
            var layEvent = obj.event; //獲得 lay-event 對應的值
            switch (layEvent) {
                case 'del':
                    layer.confirm('你確定要刪除嗎?', {
                        btn: ['確定','取消'] //按鈕
                    }, function(index){
                        $.ajax({
                            url: "/sys/role.html?act=delete",
                            method: "post",
                            data: "roleIds="+data.id,//獲取當前行的id
                            success: function (res) {
                                if (res.status) {
                                    table.reload('roleTable', {});//刪除數據後刷新table
                                } else {
                                    layer.msg(res.message);
                                }
                            }
                        })
                        layer.close(index)
                    })
                    break;
                case 'edit':
                    openidielayer(data);
                    break;
                case 'assign':
                    openMenulayer(data.id);
                    break;
            }
        })
        //打開彈出層,添加編輯共用
        function openidielayer(data) {//打開後往表單裏添加數據
            layer.open({
                type: 1
                , area: '500px'
                , content: $("#editFormlayer").html()
                , btn: ['確認', '取消']
                , yes: function (index, layero) {
                    $.ajax({
                        url: "/sys/role.html?act=edit",
                        method: "post",
                        data: $("#editForm").serialize(),//jquery獲取表單內容
                        success: function (res) {
                            if (res.status) {
                                table.reload('roleTable', {});//添加數據後刷新table
                                layer.close(index);
                            } else {
                                layer.msg(res.message);
                            }
                        }
                    })
                }
                , btn2: function (index, layero) {
                    layer.close(index)
                }, success: function (layero, index) {
                    console.log(data);
                    form.render();//重新渲染table
                    form.val("editForm",data);//獲取array的第0個元素渲染表單
                    form.val("editForm",{status:data.status+''});//數字轉字符串才能被渲染
                }
            });
        }

        //授權彈出層
        function openMenulayer(id) {//打開後往表單裏添加數據
            layer.open({
                type: 1
                , area: '500px'
                , content: $("#menulayer").html()
                , btn: ['確認', '取消']
                , yes: function (index, layero) {
                    var nodes = $('#menu-tree').tree('getChecked', ['checked','indeterminate']);
                    var menuIds = new Array();
                    for (var i = 0; i < nodes.length; i++) {
                        menuIds.push(nodes[i].id)
                        console.log(nodes[i].id)
                    }

                    $.ajax({
                        url: "/sys/role.html?act=assign",
                        method: "post",
                        data: "roleId="+id+"&menuIds="+menuIds,
                        success: function (res) {
                            if (res.status) {
                                layer.close(index);
                            } else {
                                layer.msg(res.message);
                            }
                        }
                    })
                }
                , btn2: function (index, layero) {
                    layer.close(index)
                }, success: function (layero, index) {
                    $.ajax({
                        url: "sys/role.html?act=menuIds",
                        data:"roleId="+id,
                        success:function (res) {
                            $('#menu-tree').tree({//打開成功後加載樹
                                url: "sys/role.html?act=tree",//數據接口
                                checkbox: true,
                                onLoadSuccess:function (node, data) {//選中用戶已有數據res
                                    var tree = $('#menu-tree');
                                    $.each(res, function (i,obj) {
                                        var node = tree.tree('find', obj);
                                        if(node!=null&&tree.tree('isLeaf',node.target)){//只選中子節點
                                            tree.tree('check', node.target);
                                        }
                                    })

                                }
                            })
                        }
                    })


                }
            });
        }
    });
</script>
<%--添加功能彈出層--%>
<script type="text/html" id="editFormlayer">
    <form class="layui-form" style="width:80%;padding-top: 20px" id="editForm" lay-filter="editForm">
        <input type="hidden" name="id">
        <div class="layui-form-item">
            <label class="layui-form-label">角色名:</label>
            <div class="layui-input-block">
                <input type="text" name="name" required lay-verify="required" placeholder="請輸入角色名" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">別名:</label>
            <div class="layui-input-block">
                <input type="text" name="remark" required lay-verify="required" placeholder="請輸入別名" autocomplete="off"
                       class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">狀態</label>
            <div class="layui-input-block">
                <input type="radio" name="status" value="1" title="有效">
                <input type="radio" name="status" value="0" title="無效">
            </div>
        </div>
    </form>
</script>
<%--授權功能彈出層--%>
<script type="text/html" id="menulayer">
    <ul id="menu-tree" class="easyui-tree">
    </ul>
</script>

頁面效果
在這裏插入圖片描述
github地址:https://github.com/booy123/ssmdemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章