1 Star 0 Fork 0

ccc3gc / ccc-System

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
AFL-3.0

药企内部管理平台

1.环境搭建

需要安装以下软件:

如果开发一些软件java需要哪些必须的软件?
    0. JDK11
    1. IDEA--->IDE
    2. 数据库管理系统软件  MySQL8.0+
    3. Navicat
    4. Axure Vscode
    5. typora----> md文件
    6. Git---> Gitee 码云 Github

2. 技术选型

前后端分离方式
    
前端:
  html
  css
  js(重点)  es6语法
  Vue(指令)+组件(暂且不用)+Axios  交互
后端:
  SpringBoot2(Spring+SpringMVC) +Mybatis3.*+其它
  注解开发        

3.项目搭建

3.1 前端项目

使用VScode创建前端项目:
1. 创建前端页面  

3.2 后端项目

创建springboot:
    1. 使用idea创建maven工程
    2. 使用项目引导器  
        
maven: 项目构建工具
    1.管理依赖(jar)
       1.1 本机有本地仓库
       1.2 从配置的云仓库下载
    2.创建聚合工程  

3.2.1 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>com.by.medical</groupId>
    <artifactId>zhenggong-medical-system</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <!--引入项目需要的依赖-->
    <!--1.引入父级项目依赖-->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.6.4</version>
    </parent>

    <!--2.引入web-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>


</project>

3.2.2 application.yml

server:
  port: 9000

3.2.3 启动类

@SpringBootApplication
public class MedicalApplication {//启动类----> 运行run

    public static void main(String[] args) {
        SpringApplication.run(MedicalApplication.class,args);
    }
}

3.2.4 测试

在浏览器/postman发起请求测试 http://127.0.0.1:9000/hello

@RestController
public class HelloController {

    @GetMapping("/hello")
    public Map hello(){
        return Map.of("name","炭治郎");
    }
}

3.2.5 持久层

集成数据库

1.1 在pom.xml文件中 引入mybatis相关的依赖

1.mybatis
2.mysql的驱动
3.数据库连接池(druid)  
4.lombok    
<?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>com.by.medical</groupId>
    <artifactId>zhenggong-medical-system</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <!--引入项目需要的依赖-->
    <!--1.引入父级项目依赖-->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.6.4</version>
    </parent>


    <dependencies>

        <!--2.引入web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--3.引入mybatis相关依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


    </dependencies>



</project>

1.2 在application.yml文件中,配置连接数据库相关的信息

server:
  port: 9000
#配置数据库相关
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/medical
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      initial-size: 10
      max-active: 20
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.by.medical.mapper

1.3 修改项目核心配置 加载mapper

@SpringBootApplication
@MapperScan("com.by.medical.mapper")
public class MedicalApplication {//启动类----> 运行run

    public static void main(String[] args) {
        SpringApplication.run(MedicalApplication.class,args);
    }
}

3.2.6 SysUser

@Data
public class SysUser implements java.io.Serializable{

  private Integer id;
  private String userTrueName;
  private String password;
  private String jobNumber;
  private Integer gender;
  private String phone;
  private String email;
  private String wechat;
  private String qqNumber;
  private String userImage;
  private String address;
  private Integer deptId;
  private Integer jobId;
  private Integer regionId;
  private Integer provinceId;
  private Integer groupId;
  private Integer productGroupId;
  private java.time.LocalDateTime createTime;
  private java.time.LocalDateTime updateTime;
  
}

3.2.7 创建mapper接口

在项目中 新建mapper包

@Mapper
public interface SysUserMapper {

    List<SysUser> queryAll();

}

3.2.8 创建mapper映射

在项目的resources目录下,创建mapper目录,将mapper映射文件存储在mapper目录下

<?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.by.medical.mapper.SysUserMapper">

    <select id="queryAll" resultType="com.by.medical.bean.SysUser">
        SELECT * FROM sys_user
    </select>
</mapper>

3.2.9 修改application.yml

server:
  port: 9000
#配置数据库相关
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/medical
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      initial-size: 10
      max-active: 20
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.by.medical.bean
  mapper-locations: mapper/*.xml

3.3 建库建表

  1. 创建数据库
  1. 创建表
CREATE TABLE `sys_user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '用户id,自增',
  `user_true_name` varchar(255) DEFAULT NULL COMMENT '用户真实姓名',
  `password` varchar(255) DEFAULT NULL COMMENT '用户密码  初始化密码123456',
  `job_number` varchar(255) DEFAULT NULL COMMENT '用户工号',
  `gender` tinyint(1) DEFAULT '0' COMMENT '用户性别 0 男  1 女 默认男',
  `phone` varchar(255) DEFAULT NULL COMMENT '用户手机号码',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  `wechat` varchar(255) DEFAULT NULL COMMENT '微信号',
  `qq_number` varchar(255) DEFAULT NULL COMMENT 'qq号',
  `user_image` varchar(255) DEFAULT NULL COMMENT '用户头像',
  `address` varchar(255) DEFAULT NULL COMMENT '联系地址',
  `dept_id` int DEFAULT NULL COMMENT '部门id',
  `job_id` int DEFAULT NULL COMMENT '职位id',
  `region_id` int DEFAULT NULL COMMENT '大区id',
  `province_id` int DEFAULT NULL COMMENT '地区id',
  `group_id` int DEFAULT NULL COMMENT '组id',
  `product_group_id` int DEFAULT NULL COMMENT '产品组id',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '新增时间',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4. 实现需求

4.1 登录

分析:
   根据手机号+密码工号+密码

4.1.1 页面

  <form id="loginForm">
                    <div class="dataform">
                        <div class="input-warp gap">
                            <span class="input-icon iconfont icon-yonghu1"></span>
                            <input id="userName" type="text" class="inputs" placeholder="请输入手机号/工号" maxlength="64"
                                v-model="phoneOrNo">
                        </div>
                        <div class="input-warp gap">
                            <span class="input-icon iconfont icon-baomi"></span>
                            <input class="inputs" type="password" placeholder="请输入密码" id="password" maxlength="20"
                                v-model="pass">
                        </div>
                        <div class="btn-warp gap">
                            <div class="text-center">
                                <button id="btnLogin" type="button" class="btn btn-block lgbtn blue"
                                    @click="userLogin">登录</button>
                            </div>
                        </div>
                        <div class="gap">
                            <div class="pull-right" style="margin-top: 6px">
                                <a href="javascript:;" class="link">忘记密码?</a>
                                <!-- <span class="split-space">|</span>
                                <a href="/page/user/register.html" class="link">新用户注册</a> -->
                            </div>
                        </div>
                        <!-- <div class="biggap third-party-title">
                            <h5 class="text-center"><span>第三方账号登录</span></h5>
                        </div> -->
                        <!-- <div class="third-auth">
                            <a title="用钉钉登录" class="dt" href="javascript:;"></a>
                            <a title="用微信账户登录" class="wx" href="javascript:;"></a>
                            <a title="用QQ账户登录" class="qq" href="javascript:;"></a>
                        </div> -->
                    </div>
                </form>
 <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.10/vue.js"></script>
 <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.js"></script>  
<script>
        // vue+axios
        //1.引入vue.js  axios.js
        //2.获得用户登录的手机号/工号和密码
        //3.发起异步请求(把数据提交到后端)
        let vue = new Vue({
            el: "#main",
            data: {
                phoneOrNo: "",
                pass: "",
                errorMsg:"",
            },
            methods: {
                userLogin() {
                    //发起异步请求
                    //axios.get(`请求路径`,`参数`);
                    axios.get(`http://127.0.0.1:9000/api/user/login?phoneOrNo=` + this.phoneOrNo + `&pass=` + this.pass)
                        .then(response => {
                            //response就是服务器响应的数据
                            //console.log("成功的回调结果:");
                            //console.log(response.data);

                            let {status,msg,data} = response.data;
                            if(status=="200"){
                                //登录成功
                                //页面跳转
                                location.replace("http://www.baidu.com");
                            }else{
                                this.errorMsg = msg;
                                this.phoneOrNo = "";
                                this.pass = "";
                            }
                        }).catch(error => {
                            console.log(error);
                        });
                }
            },
        });

    </script>

4.1.2 controller

 @GetMapping("/login")
    public AxiosResult<SysUser> userLogin(@RequestParam("phoneOrNo") String phoneOrNo, @RequestParam("pass") String pass){
        //String phoneOrNo,String pass  是前端提交数据
        SysUser sysUser = sysUserMapper.userLogin(phoneOrNo,pass);
        if(sysUser==null){
            return AxiosResult.error(StatusEnum.USER_LOGIN_ERROR);
        }
        return AxiosResult.success(sysUser);
    }

4.1.3 mapper

@Mapper
public interface SysUserMapper {

    List<SysUser> queryAll();

    SysUser userLogin(@Param("phoneOrNo") String phoneOrNo, @Param("pass") String pass);
}

4.1.4 映射

修改SysUserMapper.xml内容

<select id="userLogin" resultType="com.by.medical.bean.SysUser">
    SELECT * FROM sys_user
    WHERE
    (`password`= #{pass} AND job_number= #{phoneOrNo})
    OR
    (phone=#{phoneOrNo} AND `password`=#{pass});
</select>

4.1.5 页面跳转

用户登录成功之后,应该要跳转到首页

1.创建index.html  ok
2.在首页上展示个人部分信息(用户真实名称用户头像)
3.核心: 在index.html中获得并展示用户信息?
       3.1 获得  登录成功之后  服务器响应完整的用户对象信息  
       3.2 困难点  把login.html内容传到index.html

1.URLSearchParams

解决方案:
   1. 在跳转的时候 拼接想要的数据  在index.html  使用URLSearchParams
let { userTrueName, userImage } = data;
//页面跳转
location.replace("/medical-page/page/index.html?name="
                 + encodeURIComponent(userTrueName)
                 + "&image=" + encodeURIComponent(userImage));
2. 在index.html获得页面跳转并携带的数据
  2.1 在index.html 引入vue.js  axios.js
  2.2 利用vue的生命周期中钩子函数  获得数据	
 <script>
    let vue = new Vue({
      el: "#container",
      data: {
        userImage:"",
        userName:"",
      },
      created() {
        //获得登录页面跳转到首页携带的数据
        let params = new URLSearchParams(window.location.search);
        this.userName = params.get("loginName");
        this.userImage = "http://127.0.0.1:9000"+params.get("image");
      },

    });
  </script>

==2. 缓存==

//浏览器缓存:
//localStorage----> 永远都不会丢失  除非自动删除
//sessionStorage----> session会话  session过期 关闭浏览器

在login.html里面

//2.第二种方式  将数据存储缓存  localStorage
let {userTrueName, userImage,id } = data;
//将数据转换成json数据字符串存储
let userObj = {name:userTrueName,image:userImage,id:id};
localStorage.setItem("loginUserInfo",JSON.stringify(userObj));
location.replace("/medical-page/page/index.html");

在index.html中

<script>
    let vue = new Vue({
        el: "#container",
        data: {
            userImage:"",
            userName:"",
        },
        created() {
            //获得登录页面跳转到首页携带的数据
            //1.第一种方式获得
            // let params = new URLSearchParams(window.location.search);
            // this.userName = params.get("loginName");
            // this.userImage = "http://127.0.0.1:9000"+params.get("image");

            //2.使用缓存技术
            let loginUserInfoStr = localStorage.getItem("loginUserInfo");
            //json字符串转换成对象
            let userObj = JSON.parse(loginUserInfoStr);
            this.userName = userObj.name;
            this.userImage = "http://127.0.0.1:9000"+userObj.image;

        },

    });
</script>
//2.第二种方式  将数据存储缓存  localStorage
let {userTrueName, userImage,id } = data;
//将数据转换成json数据字符串存储
let userObj = {name:userTrueName,image:userImage,id:id};
localStorage.setItem("loginUserInfo",JSON.stringify(userObj));
location.replace("/medical-page/page/index.html");

4.2 查询所有用户

  1. 创建用户列表页面 medical-page/page/user/list.html
  1. 从缓存localStorage获得存储的用户信息(真实名称,用户头像)
  1. 查询用户信息
  1. 查询单表(sys_user)是不对的,数据不完整。

4.2.1 表设计

//发现在信息展示里面,用户信息中,要展示用户所在的部门名称,用户职位
CREATE TABLE `dept` (
  `id` int NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(255) DEFAULT NULL,
  `dept_loc` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `job_info` (
  `id` int NOT NULL AUTO_INCREMENT,
  `job_name` varchar(255) DEFAULT NULL COMMENT '职务名称  角色',
  `job_desc` varchar(255) DEFAULT NULL COMMENT '职务描述',
  `job_status` tinyint(1) DEFAULT '1' COMMENT '0 false  1 true',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

4.2.2 编写sql

-- 查询用户信息,并展示用户所在的部门名称,与职位名称
SELECT 
u.*,d.dept_name,j.job_name
FROM 
sys_user AS u,dept AS d,job_info AS j
WHERE
u.dept_id=d.id AND u.job_id=j.id;

4.2.3 VO

//mapper返回是xxxDTO
//service里面将DTO转换成VO
@Data
public class SysUserVO implements Serializable {

    private Integer id;
    private String userTrueName;
    private String password;
    private String jobNumber;
    private Integer gender;
    private String phone;
    private String email;
    private String wechat;
    private String qqNumber;
    private String userImage;
    private String address;
    private Integer deptId;
    private Integer jobId;
    private Integer regionId;
    private Integer provinceId;
    private Integer groupId;
    private Integer productGroupId;
    private java.time.LocalDateTime createTime;
    private java.time.LocalDateTime updateTime;
    private String deptName;
    private String jobName;

}

4.2.4 controller

//查询用户信息,查询部门名称+职位名称
@GetMapping("/query")
public AxiosResult<List<SysUserVO>> queryUserAndDeptAndJob(){
    return AxiosResult.success(sysUserMapper.queryUserAndDeptAndJob());
}

4.2.5 mapper

@Mapper
public interface SysUserMapper {

    List<SysUser> queryAll();

    SysUser userLogin(@Param("phoneOrNo") String phoneOrNo, @Param("pass") String pass);

    List<SysUserVO> queryUserAndDeptAndJob();
}
<select id="queryUserAndDeptAndJob" resultType="com.by.medical.vo.SysUserVO">
    SELECT
    u.*,d.dept_name,j.job_name
    FROM
    sys_user AS u,dept AS d,job_info AS j
    WHERE
    u.dept_id=d.id AND u.job_id=j.id
</select>

4.2.6 js

<script>
    let vue = new Vue({
        el: "#container",
        data: {
            trueName: "",
            userImage: "",
            userList: {},

        },
        methods: {
            queryAllUserInfo() {
                //查询数据库的所有的用户信息  展示list.html
                //异步查询
                axios.get(`http://127.0.0.1:9000/api/user/query`)
                    .then(response => {
                    let { status, msg, data } = response.data;
                    this.userList = data;
                });
            },
        },
        created() {
            let infoStr = localStorage.getItem("loginUserInfo");
            let userObj = JSON.parse(infoStr);
            this.trueName = userObj.name;
            this.userImage = "http://127.0.0.1:9000" + userObj.image;

            //查询所有用户信息
            this.queryAllUserInfo();
        },
    });
</script>

4.2.7 展示数据

<tbody>
    <tr v-for="(user,index) in userList" :key="index">
        <td>
            <input type="checkbox" name="" id=" ">
        </td>
        <td>{{user.jobNumber}}</td>
        <td class="hidden-phone">{{user.userTrueName}}</td>
        <td>{{user.phone}}</td>
        <td><span class="label label-info label-mini">{{user.deptName}}</span></td>
        <td><span class="label label-success label-mini">{{user.jobName}}</span></td>
        <td>{{user.createTime}}</td>
        <td>
            <button class="btn btn-success btn-xs" data-toggle="modal"
                    data-target="#myModal">
                <i class="fa fa-check">用户详情</i>
            </button>
            <button class="btn btn-primary btn-xs" data-toggle="modal"
                    data-target="#editModal">
                <i class="fa fa-pencil">编辑</i>
            </button>
            <button class="btn btn-danger btn-xs">
                <i class="fa fa-trash-o ">删除</i>
            </button>
            <button class="btn btn-warning btn-xs">
                <i class="fa fa-pencil ">修改密码</i>
            </button>
        </td>
    </tr>
</tbody>

4.3 新增用户

1. 修改list.html页面  创建新增用户模态框
2. 用户录入数据-----> 获得用户动态录入的所有的数据----->vue   v-model  insertUserObj
3. 发起异步请求-----> 持久化保存数据库-----> 编写insert 存储到sys_user
4. 操作deptjob_info  完成查询所有部门+职位      

4.3.1 查询所有部门信息

SELECT * FROM dept

实体类

@Data
public class Dept implements Serializable {
    private Integer id;
    private String deptName;
    private String deptLoc;
}

controller

@RestController
@RequestMapping("/api/dept/")
public class DeptController {


    @Autowired
    private DeptMapper deptMapper;
    //查询所有部门
    @GetMapping("/query")
    public AxiosResult<List<Dept>>  queryAllDept(){
        return AxiosResult.success(deptMapper.queryAllDept());
    }
}

mapper接口

@Mapper
public interface DeptMapper {
    List<Dept> queryAllDept();
}

mapper映射文件

<?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.by.medical.mapper.DeptMapper">
    
    <select id="queryAllDept" resultType="dept">
        SELECT * FROM dept
    </select>
</mapper> 

4.3.2 查询所有的职位

select * from  job_info;
@Data
public class JobInfo implements Serializable {

    private Integer id;
    private String jobName;
    private String jobDesc;
    private Boolean jobStatus;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

}
@RestController
@RequestMapping("/api/job/")
public class JobInfoController {

    @Autowired
    private JobInfoMapper jobInfoMapper;
    
    @GetMapping("/query")
    public AxiosResult<List<JobInfo>>  queryAllJobInfo(){
        return AxiosResult.success(jobInfoMapper.queryAllJobInfo());
    }
}
@Mapper
public interface JobInfoMapper {
    List<JobInfo> queryAllJobInfo();
}
<mapper namespace="com.by.medical.mapper.JobInfoMapper">

    <select id="queryAllJobInfo" resultType="com.by.medical.bean.JobInfo">
        select * from job_info
    </select>
</mapper>

4.3.3 页面展示

在页面上展示所有部门,所有职位信息。

1.新增点击事件 2.编写js函数

<div class="hr-line-dashed"></div>
<div class="form-group">
    <label class="col-sm-2 control-label">部门</label>
    <div class="col-sm-6">
        <select class="form-control m-b" name="account"
                @click="queryAllDept">
            <option selected>请选择</option>
            <option v-for="(dept,index) in deptList" :key="index" :value="dept.id"
                    >{{dept.deptName}}</option>
        </select>
    </div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
    <label class="col-sm-2 control-label">职位</label>
    <div class="col-sm-6">
        <select class="form-control m-b" name="account" 
                @click="queryAllJobInfo">
            <option>请选择</option>
            <option v-for="(job,index) in jobList" :key="index" :value="job.id">{{job.jobName}}</option>
        </select>
    </div>
</div>
<script>
    let vue = new Vue({
        el: "#container",
        data: {
            trueName: "",
            userImage: "",
            userList: {},
            deptList:{},
            jobList:{},


        },
        methods: {
            queryAllUserInfo() {
                //查询数据库的所有的用户信息  展示list.html
                //异步查询
                axios.get(`http://127.0.0.1:9000/api/user/query`)
                    .then(response => {
                    let { status, msg, data } = response.data;
                    this.userList = data;
                });
            },

            queryAllDept(){
                axios.get(`http://127.0.0.1:9000/api/dept/query`).then(response=>{
                    let {status,msg,data} = response.data;
                    if(status=="200"){
                        this.deptList = data;
                    }
                });
            },
            queryAllJobInfo(){
                axios.get(`http://127.0.0.1:9000/api/job/query`).then(response=>{
                    let {status,msg,data} = response.data;
                    if(status=="200"){
                        this.jobList = data;
                    }
                });
            }
        },
        created() {
            let infoStr = localStorage.getItem("loginUserInfo");
            let userObj = JSON.parse(infoStr);
            this.trueName = userObj.name;
            this.userImage = "http://127.0.0.1:9000" + userObj.image;

            //查询所有用户信息
            this.queryAllUserInfo();
        },
    });
</script>

4.3.4 新增成功

<div id="modal-form" class="modal fade" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body" style="margin-left: 30px;">
                <div class="row">
                    <div class="col-lg-12">
                        <div class="ibox float-e-margins">
                            <div class="ibox-title">
                                <h5 style="font-weight: bolder;">新增用户</h5>
                                <button type="button" class="close" data-dismiss="modal"
                                        style="margin-top: -30px;" aria-hidden="true">&times;</button>
                            </div>
                            <br>
                            <div class="ibox-content">
                                <form method="get" class="form-horizontal">
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label">姓名:</label>
                                        <div class="col-sm-6">
                                            <input type="text" v-model="insertUserObj.userTrueName" class="form-control">
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label">密码:</label>
                                        <div class="col-sm-6">
                                            <input type="text" v-model="insertUserObj.password" disabled value="123456"
                                                   class="form-control">(系统生成初始密码)
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group"><label class="col-sm-2 control-label">工号</label>

                                        <div class="col-sm-6">
                                            <input type="text" v-model="insertUserObj.jobNumber" class="form-control">
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label">性别<br />
                                        </label>

                                        <div class="col-sm-10">
                                            <div class="radio-inline">
                                                <label>
                                                    <input type="radio" checked="" value="0"
                                                           v-model="insertUserObj.gender"
                                                           name="gender">
                                                </label>
                                            </div>
                                            <div class="radio-inline">
                                                <label>
                                                    <input type="radio" value="1"  v-model="insertUserObj.gender"
                                                           name="gender">
                                                </label>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group"><label class="col-sm-2 control-label">联系方式</label>

                                        <div class="col-sm-6">
                                            <input type="text"  v-model="insertUserObj.phone" class="form-control"></div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-lg-2 control-label">Email</label>
                                        <div class="col-lg-6">
                                            <input type="text"  v-model="insertUserObj.email" class="form-control">
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-lg-2 control-label">微信账号</label>
                                        <div class="col-lg-6">
                                            <input type="text"  v-model="insertUserObj.wechat" class="form-control">
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-lg-2 control-label">QQ账号</label>
                                        <div class="col-lg-6">
                                            <input type="text"  v-model="insertUserObj.qqNumber" class="form-control">
                                        </div>
                                    </div>                                           
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label">部门</label>
                                        <div class="col-sm-6">
                                            <select class="form-control m-b" name="account"
                                                    @click="queryAllDept"  v-model="insertUserObj.deptId">
                                                <option value="-1">请选择</option>
                                                <option v-for="(dept,index) in deptList" :key="index" :value="dept.id"
                                                        >{{dept.deptName}}</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label">职位</label>
                                        <div class="col-sm-6">
                                            <select class="form-control m-b" name="account" 
                                                    @click="queryAllJobInfo"  v-model="insertUserObj.jobId">
                                                <option value="-1">请选择</option>
                                                <option v-for="(job,index) in jobList" :key="index" :value="job.id">{{job.jobName}}</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-group"><label class="col-sm-2 control-label">联系地址</label>

                                        <div class="col-sm-6">
                                            <input type="text"  v-model="insertUserObj.address" class="form-control" >
                                        </div>
                                    </div>
                                    <div class="hr-line-dashed"></div>
                                    <div class="form-group">
                                        <div class="col-sm-4 col-sm-offset-2">
                                            <button class="btn btn-danger" type="reset">重置</button>
                                            <button class="btn btn-primary" @click="insertUser" type="button">提交</button>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
insertUser(){
    //获得用户录入的信息
    //异步调用
    axios.post(`http://127.0.0.1:9000/api/user/insert`,this.insertUserObj)
        .then(response=>{
        location.replace("/medical-page/page/user/list.html");
    });
}
},
//新增用户
@PostMapping("/insert")
public AxiosResult<?> insertUser(@RequestBody SysUser sysUser){
    //System.out.println("获得新增用户信息:"+sysUser);
    sysUserMapper.insertUser(sysUser);
    return AxiosResult.success();
}

@Mapper
public interface SysUserMapper {

    void insertUser(SysUser sysUser);
}
<insert id="insertUser">
    INSERT INTO sys_user
    (user_true_name, `password`, job_number, gender, phone, email, wechat, qq_number, address, dept_id, job_id)
    VALUES
    (#{userTrueName},#{password},#{jobNumber},#{gender},#{phone},#{email},#{wechat},#{qqNumber},#{address},#{deptId},#{jobId})
</insert>

4.4 查看详情

查询单个用户信息。

1.在list.html页面中,找到"用户详情"按钮,新增事件click
2.在vue实例中创建新的函数queryUserById(uid)
3.编写sql    
SELECT 
u.*,d.dept_name,j.job_name
FROM 
sys_user AS u,dept AS d,job_info AS j
WHERE
u.id = #{id} AND u.dept_id=d.id AND u.job_id=j.id;

controller

//查询单个用户
//http://127.0.0.1:9000/api/user/query/13
@GetMapping("/query/{uid}")
public AxiosResult<SysUserVO> queryUserById(@PathVariable int uid){
    return AxiosResult.success(sysUserMapper.queryById(uid));
}

mapper接口

  SysUserVO queryById(int uid);

mapper映射

<select id="queryById" resultType="com.by.medical.vo.SysUserVO">
    SELECT
    u.*,d.dept_name,j.job_name
    FROM
    sys_user AS u,dept AS d,job_info AS j
    WHERE
    u.id = #{id} AND u.dept_id=d.id AND u.job_id=j.id;
</select>

html

<div class="modal-body">
    <div class="ibox-content">
        <div style="font-size: 15px;">
            <div style="margin-top: 80px;position: fixed;">
                <img :src="queyUserObj.userImage" alt="" style="width: 100px; height: 100px; text-align: center;">
            </div>

            <div style="position:relative; margin-left: 180px;">
                姓名: {{queyUserObj.userTrueName}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                工号: {{queyUserObj.jobNumber}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                性别: {{queyUserObj.gender==0?'男':'女'}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                <br>
                联系方式: {{queyUserObj.phone}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                邮箱: {{queyUserObj.email}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                微信账号: {{queyUserObj.wechat}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                <br>
                QQ号: {{queyUserObj.qqNumber}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                部门: {{queyUserObj.deptName}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                职位: {{queyUserObj.jobName}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
                <br>

                联系地址: {{queyUserObj.address}} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>
            </div>
        </div>
    </div>
</div>
MySQL数据库中有哪些约束自带索引?
    1.1 主键约束
    1.2 外键约束
    1.3 唯一性约束
    
 index: 本质是就是数据结构建议创建index 提高查询的性能Btree

4.5 修改用户

分析:
   修改个人信息
   查看个人原来信息的前提下进行修改设置新的数据
   update
   在开发中执行修改删除前提都要查询           
1.修改与新增共用1个模态框
2.在修改按钮上新增一个click事件调用编写过的queryUserById(id) 
3.会影响新增功能在新增按钮上新增一个点击事件addBtn  清除insertUserObj对象里面的数据
4.需要在页面:
   <input type="hidden" v-model="insertUserObj.id">
       
5.在后端代码里面 根据用户是否提交id  判断 执行新增? 修改?       
    <update id="updateUser">
        update sys_user
        set user_true_name=#{userTrueName},
            `password`=#{password},
            job_number=#{jobNumber},
            gender=#{gender},
            phone=#{phone},
            email=#{email},
            wechat=#{wechat},
            qq_number=#{qqNumber},
            address=#{address},
            dept_id=#{deptId},
            job_id=#{jobId}
        where id = #{id}
    </update>
//新增/修改用户
@PostMapping("/insertOrUpdate")
public AxiosResult<?> insertUser(@RequestBody SysUser sysUser){
    Integer id = sysUser.getId();
    if(id==null){
        sysUserMapper.insertUser(sysUser);
    }else{
        //执行修改
        sysUserMapper.updateUser(sysUser);
    }

    return AxiosResult.success();
}
  void updateUser(SysUser sysUser);

4.6 删除用户

根据id删除。(id 主键列 自带索引 根据id操作数据 比根据普通列操作 性能高)

下午作业
    实现删除用户功能
实现方式:
    方式1.可以纯前端实现(js实现)----->不要编写后端代码
        1.1 编写table标签编写js实现删除一行内容
        
    方式2.可以纯后端代码实现(编写后端接口)
        2.1 编写后端代码删除数据库1行记录
        
    方式3. 前后端开发(既写前端页面又写后端代码)     

4.6.1 删除单个

deleteUserById(uid) {
    if (window.confirm("确认要删除吗?" + uid)) {
        //发起异步请求 调用后端接口 删除数据库数据
        axios.delete(`http://127.0.0.1:9000/api/user/delete/` + uid)
            .then(response => {
            //页面继续跳转list.html
            location.replace = "/medical-page/page/user/list.html";
        });
    }
},
  //根据id删除用户
    @DeleteMapping("/delete/{uid}")
    public AxiosResult<?> deleteById(@PathVariable int uid){
        sysUserMapper.deleteUserById(uid);
        return AxiosResult.success();
    }
void deleteUserById(int uid);
<delete id="deleteUserById">
    DELETE FROM sys_user WHERE id=#{id}
</delete>

问题: 不能删除当前登录者

deleteUserById(uid) {
    //判断删除的用户是否是登录者
    //是  不能删除 
    //如何判断呢?  登录成功之后  localstorage
    //从缓存里面获得用户id
    let loginUserInfoStr = localStorage.getItem("loginUserInfo");
    let loginUserObj = JSON.parse(loginUserInfoStr);
    let loginUserId = loginUserObj.id;
    if(uid==loginUserId){
        alert("无法删除用户");
        return;
    }

    if (window.confirm("确认要删除吗?" + uid)) {
        //发起异步请求 调用后端接口 删除数据库数据
        axios.delete(`http://127.0.0.1:9000/api/user/delete/` + uid)
            .then(response => {
            //页面继续跳转list.html
            location.replace("/medical-page/page/user/list.html");
        });
    }
},

4.6.2 批量删除

deleteUserBatch(){
    //1.获得用户选择要删除的多行记录id---->获得多个id
    //===> 获得多个复选框的value属性的数据
    let idArary = [];
    let checkboxArray = document.querySelectorAll(".myCheckBox");
    checkboxArray.forEach(checkBox=>{
        if(checkBox.checked){
            idArary.push(checkBox.value);
        }
    });
    let flag = false;
    idArary.forEach(id=>{
        if(id==this.loginUserId){
            flag = true;
        }
    });
    if(flag){
        alert("无法删除登录者的信息");
        return;
    }
    //可以删除
    idArary.forEach(uid=>{
        axios.delete(`http://127.0.0.1:9000/api/user/delete/` + uid);
    });
    location.replace("/medical-page/page/user/list.html");
},

4.7 分页

分页查询用户信息

每页展示5条记录。

SELECT u.*,
               d.dept_name,
               j.job_name
        FROM sys_user AS u,
             dept AS d,
             job_info AS j
        WHERE u.dept_id = d.id
          AND u.job_id = j.id
        ORDER BY u.id DESC
        LIMIT (page-1)*size,size

4.7.1 前端页面

1.在页面上手动引入zpageNav.js
2.在页面引入分页标签    
<script>
    let vue = new Vue({
        el: "#container",
        data: {
            trueName: "",
            userImage: "",
            userList: {},
            deptList: {},
            jobList: {},
            insertUserObj: {},
            queyUserObj: {},
            loginUserId: 0,
            //分页相关的数据
            page: 1,  //显示的是哪一页
            pageSize: 5, //每一页显示的数据条数
            total: 30, //记录总数
            maxPage: 6,  //最大页数
        },
        methods: {
            //pagehandler方法 跳转到page页
            pageHandler(page) {
                this.page = page;
                //发起异步请求  分页查询用户信息
                this.queryAllUserInfo();
            },
            deleteUserBatch() {
                //1.获得用户选择要删除的多行记录id---->获得多个id
                //===> 获得多个复选框的value属性的数据
                let idArary = [];
                let checkboxArray = document.querySelectorAll(".myCheckBox");
                checkboxArray.forEach(checkBox => {
                    if (checkBox.checked) {
                        idArary.push(checkBox.value);
                    }
                });
                let flag = false;
                idArary.forEach(id => {
                    if (id == this.loginUserId) {
                        flag = true;
                    }
                });
                if (flag) {
                    alert("无法删除登录者的信息");
                    return;
                }
                //可以删除
                idArary.forEach(uid => {
                    axios.delete(`http://127.0.0.1:9000/api/user/delete/` + uid);
                });
                location.replace("/medical-page/page/user/list.html");
            },
            deleteUserById(uid) {
                //判断删除的用户是否是登录者
                //是  不能删除 
                //如何判断呢?  登录成功之后  localstorage
                //从缓存里面获得用户id
                if (uid == this.loginUserId) {
                    alert("无法删除用户");
                    return;
                }

                if (window.confirm("确认要删除吗?" + uid)) {
                    //发起异步请求 调用后端接口 删除数据库数据
                    axios.delete(`http://127.0.0.1:9000/api/user/delete/` + uid)
                        .then(response => {
                        //页面继续跳转list.html
                        location.replace("/medical-page/page/user/list.html");
                    });
                }
            },
            addBtn() {
                this.insertUserObj = {
                    password: "123456",
                    deptId: "-1",
                    jobId: "-1",
                    gender: "0",
                };
            },
            queryAllUserInfo() {
                //查询数据库的所有的用户信息  展示list.html
                //异步查询
                axios.get(`http://127.0.0.1:9000/api/user/query/` + this.page + `/` + this.pageSize)
                    .then(response => {
                    let { data } = response.data;
                    let { page, maxPage, total, list } = data;
                    this.page = page;
                    this.maxPage = maxPage;
                    this.total = total;
                    this.userList = list;
                });
            },
            queryAllDept() {
                axios.get(`http://127.0.0.1:9000/api/dept/query`).then(response => {
                    let { status, msg, data } = response.data;
                    if (status == "200") {
                        this.deptList = data;
                    }
                });
            },
            queryAllJobInfo() {
                axios.get(`http://127.0.0.1:9000/api/job/query`).then(response => {
                    let { status, msg, data } = response.data;
                    if (status == "200") {
                        this.jobList = data;
                    }
                });
            },
            insertOrUpdateUser() {
                //获得用户录入的信息
                //异步调用
                axios.post(`http://127.0.0.1:9000/api/user/insertOrUpdate`, this.insertUserObj)
                    .then(response => {
                    location.replace("/medical-page/page/user/list.html");
                });
            },
            queryUserById(uid) {
                //发起异步请求  根据id查询用户信息  查询数据库完整内容
                axios.get(`http://127.0.0.1:9000/api/user/query/` + uid)
                    .then(response => {
                    let { data } = response.data;
                    data.userImage = `http://127.0.0.1:9000` + data.userImage;
                    this.queyUserObj = data;
                    console.log(this.queyUserObj);
                    this.insertUserObj = data;
                    this.queryAllDept();
                    this.queryAllJobInfo();
                });

            },
        },
        created() {
            let infoStr = localStorage.getItem("loginUserInfo");
            let userObj = JSON.parse(infoStr);
            this.trueName = userObj.name;
            this.userImage = "http://127.0.0.1:9000" + userObj.image;
            let loginUserId = userObj.id;
            this.loginUserId = loginUserId;
            //查询所有用户信息
            //this.queryAllUserInfo();

            //created  表示页面加载完毕,立即执行
            this.pageHandler(1);
        },
    });
</script>

4.7.2 后端代码

引入分页依赖。 pageHelper.jar

<!-- 分页依赖 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>
@Data
public class PageResult<T> {
    private int page;
    private int maxPage; //总页数
    private int total;//总记录数
    private List<T> list;
}
//查询用户信息,查询部门名称+职位名称
@GetMapping("/query/{page}/{size}")
public AxiosResult<PageResult<SysUserVO>> queryUserAndDeptAndJob(@PathVariable("page") int page,@PathVariable("size") int size) {
    PageHelper.startPage(page, size);
    List<SysUserVO> userVOList = sysUserMapper.queryUserAndDeptAndJob();

    PageInfo<SysUserVO> pageInfo = new PageInfo<>(userVOList);
    
    PageResult<SysUserVO> pageResult = new PageResult<>();
    pageResult.setList(userVOList);
    pageResult.setPage(page);
    pageResult.setMaxPage(pageInfo.getPages());
    pageResult.setTotal(pageInfo.getTotal());

    return AxiosResult.success(pageResult);
}

4.8 条件+分页

4.8.1 前端页面

1.在搜索框中成功展示部门以及职位信息 
2.找到搜索按钮创建点击事件
 <button type="button" class="btn btn-theme" @click="queryAllUserInfo">搜索</button>
        //分页查询用户信息
 3.在queryAllUserInfo函数中:
满足实现的功能: 条件+分页
    queryAllUserInfo() {
    //查询数据库的所有的用户信息  展示list.html
    //异步查询
    //分页+条件
    //1.参数: 分页数据   条件的数据
    let params = {
        params:{
            page: this.page,
            pageSize: this.pageSize,
            deptId: this.searchParams.deptId,
            jobId: this.searchParams.jobId,
            nameParam: this.searchParams.nameParam,
            phoneOrNumber: this.searchParams.phoneOrNumber,
        }
    };
    axios.get(`http://127.0.0.1:9000/api/user/query`,params)
              .then(response => {
                  // let { data } = response.data;
                  // let { page, maxPage, total, list } = data;
                  // this.page = page;
                  // this.maxPage = maxPage;
                  // this.total = total;
                  // this.userList = list;
              });
              },         

调试之后,前端js:

queryAllUserInfo() {
    //查询数据库的所有的用户信息  展示list.html
    //异步查询
    //分页+条件
    //1.参数: 分页数据   条件的数据
    let params = {
        params:{
            page: this.page,
            pageSize: this.pageSize,
            deptId: this.searchParams.deptId,
            jobId: this.searchParams.jobId,
            nameParam: this.searchParams.nameParam,
            phoneOrNumber: this.searchParams.phoneOrNumber,
        }
    };
    axios.get(`http://127.0.0.1:9000/api/user/query`, params)
        .then(response => {
        let { data } = response.data;
        let { page, maxPage, total, list } = data;
        this.page = page;
        this.maxPage = maxPage;
        this.total = total;
        this.userList = list;

        //搜索相关的信息清除
        this.searchParams.deptId=-1;
        this.searchParams.jobId=-1;
        this.searchParams.nameParam="";
        this.searchParams.phoneOrNumber="";
    });
},
<tr v-show="userList.length==0">
    <td colspan="8">没有找到合适的数据</td>
</tr>

4.8.2 后端代码

在controller:
//查询用户信息,查询部门名称+职位名称
@GetMapping("/query")
public AxiosResult<PageResult<SysUserVO>> queryUserAndDeptAndJob(SearchParamVO searchParamVO) {
    int page = searchParamVO.getPage();
    int size = searchParamVO.getPageSize();

    PageHelper.startPage(page, size);
    //传递条件数据,满足sql编写
    List<SysUserVO> userVOList = sysUserMapper.queryUserAndDeptAndJob(searchParamVO);

    PageInfo<SysUserVO> pageInfo = new PageInfo<>(userVOList);
    PageResult<SysUserVO> pageResult = new PageResult<>();
    pageResult.setList(userVOList);
    pageResult.setPage(page);
    pageResult.setMaxPage(pageInfo.getPages());
    pageResult.setTotal(pageInfo.getTotal());

    return AxiosResult.success(pageResult);
}
//在mapper接口
 List<SysUserVO> queryUserAndDeptAndJob(SearchParamVO searchParamVO);
//在mapper的映射文件中
 <select id="queryUserAndDeptAndJob" resultType="com.by.medical.vo.SysUserVO">
        SELECT u.*,
               d.dept_name,
               j.job_name
        FROM sys_user AS u,
             dept AS d,
             job_info AS j
        WHERE (u.dept_id = d.id AND u.job_id = j.id)
        <if test="deptId!=-1">
            AND u.dept_id=#{deptId}
        </if>
        <if test="jobId!=-1">
            AND u.job_id=#{jobId}
        </if>
        <if test="nameParam!=null and nameParam!=''">
            AND u.user_true_name LIKE '%${nameParam}%'
        </if>
        <if test="phoneOrNumber!=null and phoneOrNumber!=''">
            AND (u.phone=#{phoneOrNumber}  OR u.job_number=#{phoneOrNumber})
        </if>
        ORDER BY u.id DESC
    </select>

问题: 在用户表中,存在很多外键列。外键列的数据不与我们主表里面的主键列的数据一致的话,我们进行普通关联查询,无法查询到的。为了显示所有用户信息,建议使用外连接。

<select id="queryUserAndDeptAndJob" resultType="com.by.medical.vo.SysUserVO">
        SELECT u.*,
        d.dept_name,
        j.job_name
        FROM sys_user AS u left join dept d on d.id = u.dept_id
        left join job_info AS j on u.job_id = j.id
        <where>
            <if test="deptId!=-1">
                AND u.dept_id=#{deptId}
            </if>
            <if test="jobId!=-1">
                AND u.job_id=#{jobId}
            </if>
            <if test="nameParam!=null and nameParam!=''">
                AND u.user_true_name LIKE '%${nameParam}%'
            </if>
            <if test="phoneOrNumber!=null and phoneOrNumber!=''">
                AND (u.phone=#{phoneOrNumber} OR u.job_number=#{phoneOrNumber})
            </if>
        </where>
        ORDER BY u.id DESC
    </select>

4.9 用户头像上传

4.9.1 前端页面

不单独编写上传页面,在新增/修改的模态框的form表单中,新增上传文件元素即可。

<!-- 从模板中复制文件上传按钮 -->
<link rel="stylesheet" href="/medical-page/lib/file-uploader/css/jquery.fileupload.css">
<link rel="stylesheet" href="/medical-page/lib/file-uploader/css/jquery.fileupload-ui.css">
<div class="form-group">
    <label class="col-sm-2 control-label">用户头像</label>
    <div class="col-sm-8">
        <div class="col-sm-2">
            <span class="btn btn-success fileinput-button">
                <i class="glyphicon glyphicon-upload" style="color: white;" ></i>
                <span style="color: white;">upload</span>
                <input type="file" accept="image/jpeg">
            </span>
        </div>
        <div class="col-sm-2">
            <img  style="width: 100px; height: 100px; margin-left: 80px;">
        </div>                                                  
    </div>
</div>
uploadImage() {
    let element = document.getElementById("uploadFile");
    let uploadFile = element.files[0];
    //调用后台接口 执行异步上传
    //1.post
    //2.multipart/form-data  请求头
    //3.FormData
    let param = new FormData();
    param.append('uploadFile', uploadFile);//二进制文件
    let config = {
        headers: { 'Content-type': 'multipart/form-data' },
    };
    axios.post(`http://127.0.0.1:9000/api/user/upload`, param, config).then(response => {
        let { status, data } = response.data;
        console.log(data);
    });
},
    // uploadImage(e){
    //     //上传头像
    //     //1.获得用户选择的头像内容(文件内容)
    //     //2.将二进制文件内容 传递到后端接口
    //     //3.js请求接口
    //     // 3.1 post
    //     // 3.2 multipart/form-data
    //     // 3.3 文件数据封装FormData
    //     let uploadFile = e.target.files[0];
    //     let formData = new FormData();
    //     formData.append("uploadFile",uploadFile);

    //     let config={
    //         headers:{
    //             'content-type':'multipart/form-data',
    //         }
    //     };
    //     //异步上传
    //     axios.post(`http://127.0.0.1:9000/api/user/upload`,formData,config);
    // },

4.9.2 后端代码

//上传用户头像
@PostMapping("/upload")
public AxiosResult<String> uploadUserImage(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
    String originalFilename = uploadFile.getOriginalFilename();//获得上传文件名称以及后缀
    //正常开发中:
    //1.存储外部服务器 (nginx)
    //2.云端服务器(OSS) 对象存储服务器

    //后端维护用户上传的图片(本地磁盘)
    //把用户每天上传的图片 存储到以日期为准一个目录中
    //弊端: 前端页面不能正常访问存储本地磁盘文件
    //解决: 需要添加映射处理
    //        String realPath = request.getServletContext().getRealPath("/");
    //        System.out.println(realPath);
    String curDateStr = LocalDate.now().toString();
    File targetDir = new File(UPLOAD_USER_DIR, curDateStr);
    if (!targetDir.exists()) {
        targetDir.mkdirs();
    }

    String extension = StringUtils.getFilenameExtension(originalFilename);
    String name = UUID.randomUUID().toString().replaceAll("-", "");
    String targetFileName = name + "." + extension;
    uploadFile.transferTo(new File(targetDir, targetFileName));

    return AxiosResult.success("/upload/user/"+curDateStr+"/"+targetFileName);
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**")
                .addResourceLocations("file:F:\\beiyou\\zhenggong_IT\\workspace\\zhenggong-medical-system\\src\\main\\resources\\static\\upload\\user");
    }
}

4.10 数据导出/导入

数据导出: 将数据库/缓存数据导出至excel文件。

数据导入: 将excel文件里面的sheet中的信息导入数据库,永久存储。

4.10.1 导出

需求:
   将数据库里面所有用户信息导出到指定的exel文件中
常用的技术:
   easyexcel  入门容易  

前端页面

<div class="form-group">
    <button class="btn btn-warning" @click="exportUserInfo">
        <i class="fa fa-download"></i>导出
    </button>
</div>
exportUserInfo() {
    //异步请求
    myAxios.post(`http://127.0.0.1:9000/api/user/export`).then(response => {
        //创建下载提示  将成功返回的内容下载到文件中
        //后端响应的类型 应该是字节 blob
        let data = response.data;
        const blob = new Blob([data], {
            type: "application/vnd.ms-excel;charset=utf-8",
        });
        //blob就是获得到字节内容
        let downloadLink = document.createElement("a");
        downloadLink.href = window.URL.createObjectURL(blob);
        downloadLink.download = "导出用户信息.xlsx";
        downloadLink.click();//自动下载到 "导出用户信息.xlsx" 

        //问题: 下载文件成功  可能会出现文件打不开
        //原因: axios发起异步请求  默认返回的json的数据  也就是说获得的data是json的数据
    });
},
let myAxios = axios.create({
    responseType:"blob",
});

后端

<!--引入easyexcel的依赖-->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>
//导出---->将用户信息导出到指定的文件中
@PostMapping("/export")
public void exportUserInfo(HttpServletResponse response) throws IOException {
    //获得导出的所有用户信息
    List<SysUser> sysUserList = sysUserMapper.queryAll();
    ExportParams params = new ExportParams();
    params.setTitle("用户列表信息");
    params.setSheetName("用户sheet");
    Workbook workbook = ExcelExportUtil.exportExcel(params, SysUser.class, sysUserList);
    //配置响应
    response.setCharacterEncoding("UTF-8");
    response.setHeader("content-type","application/vnd.ms-excel");
    //服务器响应的不再是json数据,而是流文件(excel)文件
    String fileName = URLEncoder.encode("userInfo", StandardCharsets.UTF_8);
    response.setHeader("Content-Disposition","attachment;filename="+fileName+".xlsx");
    workbook.write(response.getOutputStream());

    workbook.close();
}
@Data
public class SysUser implements java.io.Serializable{

  private Integer id;
  //name的数据是对应的excel文件里面的列名
  //orderNum是用来定义excel的第n列  从0开始
  //width用来定义列的宽度
  @Excel(name = "用户名",orderNum = "1",width = 20)
  private String userTrueName;
  private String password;
  @Excel(name = "工号",orderNum = "0")
  private String jobNumber;
  @Excel(name = "性别",replace = {"男_0","女_1"},orderNum = "2")
  private Integer gender;
  @Excel(name = "手机号",orderNum = "3",width = 20)
  private String phone;
  @Excel(name = "邮箱账号",orderNum = "4",width = 20)
  private String email;
  @Excel(name = "微信",orderNum = "5",width = 20)
  private String wechat;
  private String qqNumber;
  private String userImage;
  @Excel(name = "家庭地址",orderNum = "6",width = 20)
  private String address;
  private Integer deptId;
  private Integer jobId;
  private Integer regionId;
  private Integer provinceId;
  private Integer groupId;
  private Integer productGroupId;
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  @Excel(name = "入职时间",orderNum = "7",exportFormat = "yyyy-MM-dd HH:mm:ss",width = 30)
  private java.time.LocalDateTime createTime;
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private java.time.LocalDateTime updateTime;

}

4.10.2 导入

导入:
  将指定的excel文件里面用户信息保存到数据库中
  excel文件内容是有模板要求(数据必须按照模板要求)-----> 将excel文件进行上传----> 调用sql 将数据存储    

前端(页面,js)

<div class="form-group">
    <span class="btn btn-success fileinput-button">
        <i class="glyphicon glyphicon-upload" style="color: white;"></i>
        <span style="color: white;">导入</span>
        <input type="file" class="" accept=".xls,.xlsx" @change="importUserInfo">
    </span>
</div>
importUserInfo(event) {
    //异步调用接口   实现导入
    //1.获得用户选择的文件
    //2.将文件内容传到服务器
    //3.调用接口
    //等价于====> 文件上传
    // FormData  post  multipart/form-data

    let importFile = event.target.files[0];
    let formData = new FormData();
    formData.append("importFile", importFile);
    let config = {
        headers: {
            "content-type": "multipart/form-data",
        }
    };
    axios.post(`http://127.0.0.1:9000/api/user/import`, formData, config).then(response => {
        location.replace("/medical-page/page/user/list.html");
    });
},

后端

//导入----->将excel文件数据存储到数据库
@PostMapping("/import")
public AxiosResult<?> importUserInfo(MultipartFile importFile) throws Exception {
    //将页面提价的文件数据转成多个用户对象
    //        System.out.println(importFile.getOriginalFilename());
    //        System.out.println(importFile.getBytes().length);
    //        System.out.println(importFile.getSize());
    //将文件里面的每一行内容转换SysUser对象
    //调用mapper接口  实现批量插入用户信息
    //前提:
    // 1.引入easypoi的依赖  2.SysUser的相关的属性使用@Excel修饰
    // 2.excel文件里面的列名不能随便写 必须与SysUser类中@Excel的name的值一致
    //importExcel(InputStream inputstream, Class<?> pojoClass,ImportParams params)
    //inputstream 文件内容
    ImportParams importParams = new ImportParams();
    importParams.setStartSheetIndex(0);//指定读取第index+1个sheet
    importParams.setTitleRows(1);//标题
    importParams.setHeadRows(1);//头部 列

    List<SysUser> sysUserList = ExcelImportUtil.importExcel(importFile.getInputStream(), SysUser.class, importParams);
    //将流的内容转换成指定的类型对象
    //sysUserList.forEach(System.out::println);
    //批量新增
    sysUserList.forEach(sysUser -> sysUser.setPassword("123456"));
    //sysUserList.forEach(System.out::println);
    sysUserMapper.insertUserBatch(sysUserList);
    return AxiosResult.success();
}
void insertUserBatch(List<SysUser> sysUserList);
<insert id="insertUserBatch">
    INSERT INTO sys_user
    (user_true_name, `password`, job_number, gender, phone, email, wechat, qq_number, address, dept_id, job_id)
    values
    <foreach collection="sysUserList" item="user" separator=",">
        (#{user.userTrueName}, #{user.password}, #{user.jobNumber}, #{user.gender}, #{user.phone}, #{user.email},
        #{user.wechat}, #{user.qqNumber},
        #{user.address}, #{user.deptId}, #{user.jobId})
    </foreach>

</insert>

4.11 密码加密

MD5: MessageDigest  信息摘要算法
  不可逆的
Base64      

修改密码 只能修改个人密码。

前端页面

<div class="form-group">
    <button class="btn btn-warning"  @click="editPassBtn"
            data-toggle="modal" data-target="#editPassModal" >
        <i class="fa fa-download"></i>修改密碼
    </button>
</div>
updatePass(){
    let params = {
        id:this.loginUserId,
        newPass:this.newPass,
    };
    console.log(params);
    axios.post(`http://127.0.0.1:9000/api/user/pass`,params).then(response=>{

        //跳轉到login.html
        //清除緩存裡面個人信息
        //localStorage.clear();
        localStorage.removeItem("loginUserInfo");
        location.replace("/medical-page/login.html");
    });
},

后端

public class MD5Util {
    private MD5Util(){}

    private static final String SALT = "%$**&7lISA^^^^524";

    /**
     * 密码加密
     * @param sourceStr 源密码
     * @return 加密之后的数据
     */
    public static String encodeStr(@NonNull String sourceStr){
        try {
            sourceStr+=SALT;
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(sourceStr.getBytes());
            byte[] bytes = messageDigest.digest();//加密的数据
            //换算
            //将字节转换成16进制里面字符
            //一个字节转换成2个16进制的字符
            BigInteger bigInteger = new BigInteger(1,bytes);
            return bigInteger.toString(16);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

}
//修改密码
@PostMapping("/pass")
public AxiosResult<?> updatePass(@RequestBody Map<String,Object> map){
    Object id = map.get("id");
    Object newPass = map.get("newPass");//用户设置的新密码  是明文数据
    //对密码加密
    String encodeStr = MD5Util.encodeStr(newPass.toString());
    sysUserMapper.updatePass((int)id,encodeStr);
    return AxiosResult.success();
}

注意: 登录,新增,导入的功能中,也要对密码加密。

代码略

4.12 登录拦截(略)

<!--引入jwt-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.3.0</version>
</dependency>
public class TokenService {

    private static final String SECRET = "^%$LISA(($#$";

    //生成token
    public String createToken(long adminId) {
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        return JWT.create()
            .withIssuer("lisa")
            .withSubject("登录成功的token")
            .withClaim("adminId",adminId)
            //.withExpiresAt(new Date(System.currentTimeMillis() + Duration.ofMinutes(2).toMillis()))
            .sign(algorithm);
    }

    //验证token
    public DecodedJWT verifyToken(String token) {
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm)
            .withIssuer("lisa")
            .build();
        return verifier.verify(token);
    }
}
@Component //注入容器
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //token验证
        //在项目中  获得请求头里面的token
        String method = request.getMethod();
        if ("OPTIONS".equals(method)) {
            return true;
        }
        String authorization = request.getHeader("Authorization");
        if (!StringUtils.hasLength(authorization)) {//是空
            //建议抛出异常  进行全局异常处理
            throw new RuntimeException("用户未登录");
            //return false;
        }
        String[] array = authorization.split(" ");
        if (array.length == 0) {
            throw new RuntimeException("用户未登录");
        }
        if (!array[0].startsWith("Basic")) {
            throw new RuntimeException("用户未登录");
        }
        try {
            tokenService.verifyToken(array[1]);
        } catch (Exception e) {
            throw new RuntimeException("用户未登录", e);
        }
        return true;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**")
                .addResourceLocations("file:F:\\beiyou\\zhenggong_IT\\workspace\\zhenggong-medical-system\\src\\main\\resources\\static\\upload\\user");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/user/login")
                .order(0);
    }
}

5. 封装接口数据

  //{ status:"200",msg:"查询成功",data:userObj}
  //{ status:"500",msg:"查询失败"}

5.1 AxiosResult

@Getter
@Setter
//@JsonSerialize
public class AxiosResult<T> implements Serializable {
    //就是封装服务器响应给前端的数据
    //T就是一个参数化数据类型   <T> 泛型  可以是任意一个引用数据类型

    private String status;
    private String msg;
    private T data;

    private AxiosResult(StatusEnum statusEnum){
        this.status = statusEnum.getStatus();
        this.msg = statusEnum.getMsg();
    }

    private AxiosResult(StatusEnum statusEnum,T data){
        this.status = statusEnum.getStatus();
        this.msg = statusEnum.getMsg();
        this.data = data;
    }

    //静态方法 success  error
    public static <T> AxiosResult<T> success() {
        return new AxiosResult<>(StatusEnum.SUCCESS);
    }

    public static <T> AxiosResult<T> success(T data) {
        return new AxiosResult<>(StatusEnum.SUCCESS,data);
    }

    public static <T> AxiosResult<T> success(StatusEnum statusEnum,T data) {
        return new AxiosResult<>(statusEnum,data);
    }

    public static <T> AxiosResult<T> error() {
        return new AxiosResult<>(StatusEnum.ERROR);
    }
    public static <T> AxiosResult<T> error(StatusEnum statusEnum) {
        return new AxiosResult<>(statusEnum);
    }
}

5.2 StatusEnum

@Getter
@AllArgsConstructor
public enum StatusEnum {
    //维护当前系统里面所有的错误码列表
    SUCCESS("200","成功"),
    ERROR("500","失败"),
    USER_LOGIN_ERROR("501","用户名或者密码有误");


    private final String status;
    private final String msg;

}

6.跨域

url:
  协议  ip  端口 
      
list.html:1 Access to XMLHttpRequest at 'http://127.0.0.1:9000/api/dept/query' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.      
@Configuration
public class CorsConfig {

    //Filter------>CorsFilter

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://127.0.0.1:5500");
        //corsConfiguration.setAllowedOrigins();
        corsConfiguration.setAllowedMethods(List.of("POST", "GET", "PUT", "DELETE", "OPTION"));
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowCredentials(false);
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
        configurationSource.registerCorsConfiguration("/**", corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(configurationSource);
        bean.setFilter(corsFilter);
        bean.setOrder(0);
        return bean;
    }

}
Academic Free License (“AFL”) v. 3.0 This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: Licensed under the Academic Free License version 3.0 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: a) to reproduce the Original Work in copies, either alone or as part of a collective work; b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; c) to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor’s reserved rights and remedies, in this Academic Free License; d) to perform the Original Work publicly; and e) to display the Original Work publicly. 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. 5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. 9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including “fair use” or “fair dealing”). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. 12) Attorneys’ Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. 16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.

简介

简单的基于SpringBoot的用户管理系统(半成品) 展开 收起
JavaScript 等 4 种语言
AFL-3.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/ccc3gc/ccc-system.git
git@gitee.com:ccc3gc/ccc-system.git
ccc3gc
ccc-system
ccc-System
master

搜索帮助