SSM 单体框架 - 前端开发:用户和权限模块

2020-10-09 11:26:16 浏览数 (1)

用户管理

分页 & 条件查询用户数据

查询条件

代码语言:javascript复制
1. 用户手机号
2. 注册时间,包含开始日期和结束日期
日期选择器组件

在查询条件中使用了 Element UI 中的日期选择器:https://element.eleme.cn/#/zh-CN/component/date-picker#mo-ren-xian-shi-ri-qi

在测试项目中创建一个 TestDate.vue 组件,复制代码到页面

代码语言:javascript复制
<template>
  <div>
    <div class="block">
      <span class="demonstration">带快捷选项</span>
      <el-date-picker
        v-model="dateTime"
        type="daterange"
        align="right"
        unlink-panels
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        :picker-options="pickerOptions"
      ></el-date-picker>
      <el-button type="primary" @click="getDate">查询</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      pickerOptions: {
        shortcuts: [
          {
            text: "最近一周",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit("pick", [start, end]);
            }
          },
          {
            text: "最近一个月",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
              picker.$emit("pick", [start, end]);
            }
          },
          {
            text: "最近三个月",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
              picker.$emit("pick", [start, end]);
            }
          }
        ]
      },
      dateTime: ""
    };
  },
  methods: {
    getDate() {
      const params = {};
      params.startCreateTime = this.dateTime[0];
      params.startCreateTime.setHours(0);
      params.startCreateTime.setMinutes(0);
      params.startCreateTime.setSeconds(0);
      params.endCreateTime = this.dateTime[1];
      params.endCreateTime.setHours(23);
      params.endCreateTime.setMinutes(59);
      params.endCreateTime.setSeconds(59);
      console.log(params);
    }
  }
};
</script>
功能实现

Users.vue

数据部分

代码语言:javascript复制
// 数据部分
return {
    pickerOptions, // 日期选择器选项设置
    total: 0, // 总条数
    size: 10, // 每页显示条数
    page: 1, // 当前页
    filter,
    users: [],
    loading: false,
    allocAdminId: "",
    allocDialogVisible: false,
    allocRoleIds: [],
    allRoleList: []
};

JS 部分

代码语言:javascript复制
created() {
    // 初始化用户数据
    this.loadUsers();
}
// 方法 1: 加载用户数据
loadUsers() {
    this.loading = true;
    // 设置参数
    const params = { currentPage: this.page, pageSize: this.size };
    // 过滤条件
    if (this.filter.username) params.username = this.filter.username;
    // 设置日期参数
    if (this.filter.resTime) {
        params.startCreateTime = this.filter.resTime[0];
        params.startCreateTime.setHours(0);
        params.startCreateTime.setMinutes(0);
        params.startCreateTime.setSeconds(0);

        params.endCreateTime = this.filter.resTime[1];
        params.endCreateTime.setHours(23);
        params.endCreateTime.setMinutes(59);
        params.endCreateTime.setSeconds(59);
    }
    // 请求后台接口
    return axios.post("/user/findAllUserByPage", params)
        .then((res) => {
        // 用户数据
        this.users = res.data.content.list;
        this.total = res.data.content.total;
        this.loading = false;
    }).catch((err) => {
        this.$message("获取数据失败! ! !");
    });
},
用户状态设置

状态按钮

代码语言:javascript复制
<el-button size="mini" type="text" @click="handleToggleStatus(scope.row)">{{ scope.row.status == "ENABLE" ? "禁用" : "启用" }}</el-button>

JS 部分

代码语言:javascript复制
// 修改用户状态
handleToggleStatus(item) {
    return axios
        .get("/user/updateUserStatus", {
        params: {
            id: item.id,
            status: item.status
        }
    })
        .then(res => {
        // debugger;
        console.log(res.data.content);
        item.status = res.data.content;
    })
        .catch(err => {
        this.$message.error("状态修改失败! ! !");
    });
},

权限管理

角色管理
展示 & 查询角色列表

角色组件是 Roles.vue,在该组件中对角色信息进行管理

功能实现

1) 数据部分

代码语言:javascript复制
data() {
    return {
        listQuery: { name: "" },
        list: null,
        listLoading: false,
        dialogVisible: false,
        role: Object.assign({}, defaultRole),
        isEdit: false
    };
},

2) 钩子函数,调用 loadRoles,获取角色数据

代码语言:javascript复制
created() {
    // 获取角色列表
    this.loadRoles();
},
代码语言:javascript复制
// 获取角色数据
loadRoles() {
    return axios.post("/role/findAllRole", this.listQuery)
        .then(res => {
        this.list = res.data.content;
        this.listLoading = false;
    }).catch(err => {});
},

3) 请求携带的参数是:listQuery

代码语言:javascript复制
<el-input v-model="listQuery.name" class="input-width" placeholder="角色名称" clearable></el-input>
代码语言:javascript复制
// 条件查询
handleSearchList() {
    this.loadRoles();
},
添加 & 修改角色

1) 页面部分

代码语言:javascript复制
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加角色</el-button>

2) 打开添加角色窗口的方法

代码语言:javascript复制
// 添加角色弹窗
handleAdd() {
    // 打开对话框
    this.dialogVisible = true;
    // false 修改操作
    this.isEdit = false;
    this.role = Object.assign({}, defaultRole);
},

3) 添加角色对话框,使用 v-model 进行双向数据绑定

代码语言:javascript复制
<!-- 添加&修改 角色对话框 -->
<el-dialog :title="isEdit?'编辑角色':'添加角色'" :visible.sync="dialogVisible" width="40%">
    <el-form :model="role" label-width="150px" size="small">
        <el-form-item label="角色名称:">
            <el-input v-model="role.name" style="width: 250px"></el-input>
        </el-form-item>
        <el-form-item label="角色编码:">
            <el-input v-model="role.code" style="width: 250px"></el-input>
        </el-form-item>
        <el-form-item label="描述:">
            <el-input v-model="role.description" type="textarea" :rows="5" style="width: 250px"></el-input>
        </el-form-item>
    </el-form>
    <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false" size="small">取 消</el-button>
        <el-button type="primary" @click="handleSave()" size="small">确 定</el-button>
    </span>
</el-dialog>

4) 添加角色方法

代码语言:javascript复制
// 添加 & 修改角色
handleSave() {
    axios
        .post("/role/saveOrUpdateRole", this.role)
        .then(res => {
        this.dialogVisible = false;
        this.loadRoles();
    })
        .catch(error => {
        this.$message.error("保存课程信息失败! ! !");
    });
},

5) 修改角色的方法

修改按钮,点击传递当前行数据对象

代码语言:javascript复制
<el-button size="mini" type="text" @click="handleUpdate(scope.row)">编辑</el-button>

显示对话框,回显数据

代码语言:javascript复制
// 修改角色弹窗
handleUpdate(row) {
    this.dialogVisible = true;
    this.isEdit = true;
    // 回显数据
    this.role = Object.assign({}, row);
},

修改角色,还是调用的 handleSave 方法

删除角色
代码语言:javascript复制
<el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>

使用到了 Element UI 中的 MessageBox 弹框:https://element.eleme.cn/#/zh-CN/component/message-box#options

代码语言:javascript复制
handleDelete(row) {
    this.$confirm("是否要删除该角色?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
    }).then(() => {
        axios("/role/deleteRole?id="   row.id)
            .then(res => {
            this.loadRoles();
        })
            .catch(err => {
            this.$message.error("操作失败! ! !");
        });
    });
},
为角色分配菜单
需求分析

为角色分配菜单,一个角色可以拥有多个菜单权限

一个菜单权限也可以被多个角色拥有

角色与菜单之间的关系是多对多

点击分配菜单,页面展示效果

前端要实现的效果:

  • 第一步:获取到所有的菜单数据,在树形控件中进行展示
  • 第二步:将当前角色拥有的菜单权限,勾选上
菜单展示功能实现

1) 分配菜单按钮,点击传递当前行数据

代码语言:javascript复制
<el-button size="mini" type="text" @click="handleSelectMenu(scope.row)">分配菜单</el-button>

2) 路由导航到 allocMenu

代码语言:javascript复制
// 为角色分配菜单
handleSelectMenu(row) {
    this.$router.push({ path: "/allocMenu", query: { roleId: row.id } });
},

3) routes.js

代码语言:javascript复制
{
    path: "allocMenu",
    name: "AllocMenu",
    component: () => import(
        /* webpackChunkName: 'allocMenu' */
        "../views/PermissionManage/AllocMenu"
    ),
    meta: { requireAuth: true, title: "角色菜单管理" }
},

4) 在 AllocMenu.vue 组件中完成为角色分配菜单操作

5) 数据部分

代码语言:javascript复制
data() {
    return {
        // 菜单数据
        menuTreeList: [],
        // 被选中的菜单
        checkedMenuId: [],
        // 树形结构子节点设置
        defaultProps: {
            children: "subMenuList",
            label: "name"
        },
        roleId: null
    };
},

6) 钩子函数

代码语言:javascript复制
// 钩子函数
created() {
    // 获取路由携带的 id
    this.roleId = this.$route.query.roleId;
    // 获取菜单列表
    this.treeList();
    // 获取角色所拥有的菜单信息
    this.getRoleMenu(this.roleId);
},
代码语言:javascript复制
// 方法 1:获取菜单列表,使用树形控件展示
treeList() {
    axios.get("/role/findAllMenu").then(res => {
        console.log(res.data.content);
        // 获取树形控件所需数据
        this.menuTreeList = res.data.content.parentMenuList;
    });
},

// 方法 2:获取当前角色所拥有菜单列表 id
getRoleMenu(roleId) {
    axios.get("/role/findMenuByRoleId?roleId="   roleId).then(res => {
        console.log(res.data.content);
        // 将已有菜单权限设置为选中
        this.$refs.tree.setCheckedKeys(res.data.content);
    });
},
分配菜单功能实现

分配菜单按钮

代码语言:javascript复制
<div style="margin-top: 20px" align="center">
    <el-button type="primary" @click="handleSave()">保存</el-button>
    <el-button @click="handleClear()">清空</el-button>
</div>

方法

代码语言:javascript复制
// 方法 3:修改角色所拥有的菜单列表
handleSave() {
    // debugger;
    // 获取所有被选中的节点
    const checkedNodes = this.$refs.tree.getCheckedNodes();
    // 定义常量保存被选中的菜单 id
    const checkedMenuIds = [];
    if (checkedNodes != null && checkedNodes.length > 0) {
        // 遍历获取节点对象
        for (let i = 0; i < checkedNodes.length; i  ) {
            const checkedNode = checkedNodes[i];
            // 保存菜单列表 id
            checkedMenuIds.push(checkedNode.id);
            // 判断:当前节点为子节点 && 其父 ID 在数组没有出现过,就保存这个父 Id
            if (
                checkedNode.parentId !== -1 &&
                checkedMenuIds.filter(item => checkedNode.parentId).length === 0
            ) {
                checkedMenuIds.push(checkedNode.parentId);
            }
        }
    }
    this.$confirm("是否分配菜单?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
    }).then(() => {
        // 准备参数
        const params = {
            // 角色 ID
            roleId: this.roleId,
            // 当前角色拥有的菜单权限 ID
            menuIdList: checkedMenuIds
        };
        // 请求后台
        axios.post("/role/RoleContextMenu", params)
            .then(res => {
            this.$router.back();
        }).catch(err => {
            this.$message.error("权限分配失败! ! !");
        });
    });
},
菜单管理

菜单组件是 Menus.vue,在该组件中对菜单信息进行管理

展示菜单列表

需求分析:菜单列表的展示是带有分页的

功能实现

1) 数据部分

代码语言:javascript复制
data() {
    return {
        // 总条数
        total: 0,
        // 每页显示条数
        size: 10,
        // 当前页
        page: 1,
        // 广告数据
        list: [],
        listLoading: true,
        // 菜单父 id
        parentId: 0
    };
},

2) 钩子函数

代码语言:javascript复制
created() {
    // 获取菜单列表
    this.loadMenuList();
},
代码语言:javascript复制
// 方法 1:加载菜单列表数据
loadMenuList() {
    this.listLoading = true;
    return axios
        .get("/menu/findAllMenu", {
        params: {
            currentPage: this.page,
            pageSize: this.size
        }
    })
        .then(res => {
        this.list = res.data.content.list;
        this.total = res.data.content.total;
        this.listLoading = false;
    })
        .catch(error => {
        this.$message.error("数据获取失败! ! !");
    });
},
新增 & 修改菜单
路由跳转流程

1) 新增按钮,点击跳转

代码语言:javascript复制
<el-button class="btn-add" @click="handleAddMenu()" size="mini">添加菜单</el-button>
代码语言:javascript复制
// 新增菜单跳转
handleAddMenu() {
    this.$router.push("/addMenu");
},

2)AddMenu.vue 组件中引入了 MenuDetail

代码语言:javascript复制
<template>
<menu-detail :is-edit='false'></menu-detail>
</template>
<script>
    import MenuDetail from './MenuDetail'
    export default {
        name: 'addMenu',
        title: '添加菜单',
        components: { MenuDetail }
    }
</script>

3) MenuDetail.vue 中完成菜单的新增与修改操作

需求分析

在打开新增菜单页面后,需要展示一个下拉框,下拉框中的数据是所有的顶级父菜单

功能实现

1) 数据部分

代码语言:javascript复制
data() {
    return {
        // 菜单对象
        menu,
        // 下拉列表数据
        selectMenuList: [],
        rules
    };
},

2) 钩子函数

在钩子函数中会进行判断,如果是修改操作,就根据 ID 查询当前菜单信息,以及父菜单信息

如果是新增操作,则只查询父类菜单信息即可

代码语言:javascript复制
created() {
    if (this.isEdit) {
        // 修改,回显菜单信息
        const id = this.$route.query.id;
        // 获取当前菜单和父菜单信息
        this.findMenuInfoById(id);
    } else {
        // 新增
        this.menu = {};
        // 获取父类菜单信息
        this.findMenuInfoById(-1);
    }
},
代码语言:javascript复制
// 方法 1: 添加或修改的回显,下拉父菜单
findMenuInfoById(id) {
    axios.get("/menu/findMenuInfoById?id="   id)
        .then(res => {
        // debugger;
        console.log(res.data);
        // 判断不为 null,修改操作需要回显
        if (res.data.content.menuInfo != null) {
            this.menu = res.data.content.menuInfo;
        }
        // 获取到父菜单信息,保存到 selectMenuList
        this.selectMenuList = res.data.content.parentMenuList.map(item => {
            return { id: item.id, title: item.name };
        });
        // -1 显示无上级菜单 (unshift 向数组的开头添加一个元素)
        this.selectMenuList.unshift({ id: -1, title: "无上级菜单" });
    }).catch(err => {
        this.$message.error("数据获取失败! ! !");
    });
},

3) 点击保存

代码语言:javascript复制
<el-button type="primary" @click="handleSave()">提交</el-button>
代码语言:javascript复制
// 保存菜单
handleSave() {
    this.$refs.form.validate(valid => {
        if (!valid) return false;
        axios.post("/menu/saveOrUpdateMenu", this.menu)
            .then(res => {
            this.$router.back();
        }).catch(error => {
            this.$message.error("保存课程信息失败! ! !");
        });
    });
}
资源管理

资源组件是 Resources.vue,在该组件中对资源信息进行管理

展示 & 查询资源列表

1) 展示资源数据带有分页

2) 查询资源数据,查询条件有三个

  • 资源名称
  • 资源路径
  • 资源分类信息 - 下拉列表

3) 数据部分

代码语言:javascript复制
// 查询条件
const listQuery = {
    currentPage: 1,
    pageSize: 5,
    name: null,
    url: null,
    categoryId: null
};
// 资源对象
const defaultResource = {
    id: null,
    name: null,
    url: null,
    categoryId: null,
    description: ""
};
代码语言:javascript复制
data() {
    return {
        // 查询条件
        listQuery,
        total: 0,
        // 资源数据
        list: [],
        // 资源分类数据
        cateList: [],
        listLoading: false,
        dialogVisible: false,
        resource: Object.assign({}, defaultResource),
        isEdit: false,
        categoryOptions: [],
        defaultCategoryId: null
    };
},

4) 钩子函数

在钩子函数中需要获取资源,以及资源分类的数据

代码语言:javascript复制
// 钩子函数
created() {
    // 获取资源数据
    this.getResourceList();
    // 获取资源分类数据
    this.getResourceCateList();
},

getResourceList() 方法获取的是资源信息

代码语言:javascript复制
// 方法 1: 获取资源数据
getResourceList() {
    this.listLoading = true;
    axios.post("/resource/findAllResource", this.listQuery)
        .then(res => {
        this.list = res.data.content.list;
        this.total = res.data.content.total;
        this.listLoading = false;
    }).catch(err => {
        this.$message.error("数据获取失败! ! !");
    });
},

getResourceCateList() 方法获取的是资源分类信息,在下拉框中展示

代码语言:javascript复制
// 方法 2: 获取资源分类数据
getResourceCateList() {
    axios.get("/ResourceCategory/findAllResourceCategory")
        .then(res => {
        this.cateList = res.data.content;
        // 遍历获取资源分类
        for (let i = 0; i < this.cateList.length; i  ) {
            const cate = this.cateList[i];
            // 将资源分类名与 id 保存到 categoryOptions 中,供下拉列表展示
            this.categoryOptions.push({ label: cate.name, value: cate.id });
        }
        this.defaultCategoryId = this.cateList[0].id;
    })
        .catch(err => {
        this.$message.error("数据获取失败! ! !");
    });
},

查询

代码语言:javascript复制
<el-button style="float:right" type="primary" @click="handleSearchList()" size="small">查询搜索</el-button>
代码语言:javascript复制
// 查询条件对象
const listQuery = {
    currentPage: 1,
    pageSize: 5,
    name: null,
    url: null,
    categoryId: null
};
// 查询按钮
handleSearchList() {
    this.getResourceList();
},
新增 & 修改资源

1) 添加按钮

代码语言:javascript复制
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>

2) 显示添加资源表单的对话框

代码语言:javascript复制
// 添加资源回显
handleAdd() {
    // 显示表单
    this.dialogVisible = true;
    // 新增为 false
    this.isEdit = false;
    // 资源对象
    this.resource = Object.assign({}, defaultResource);
    // 保存默认分类 id
    this.resource.categoryId = this.defaultCategoryId;
},

3) 资源分类信息使用下拉菜单进行展示

v-model 的值为当前被选中的 el-optionvalue 属性值

  • value 选项的值
  • label 选项的标签名
  • key 作为 value 唯一标识的键名
代码语言:javascript复制
<el-form-item label="资源分类:">
    <el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px">
        <el-option v-for="item in categoryOptions"
                   :key="item.value"
                   :label="item.label"
                   :value="item.value">
        </el-option>
    </el-select>
</el-form-item>

4) 点击保存

代码语言:javascript复制
<el-button type="primary" @click="handleSave()" size="small">确 定</el-button>
代码语言:javascript复制
// 添加 & 修改资源
handleSave() {
    axios.post("/resource/saveOrUpdateResource", this.resource)
        .then(res => {
        this.dialogVisible = false;
        this.getResourceList();
    }).catch(error => {
        this.$message.error("操作失败! ! !");
    });
},

5) 修改操作,参数是当前行数据

代码语言:javascript复制
<el-button size="mini" type="text" @click="handleUpdate(scope.row)">编辑</el-button> 

6) 回显操作

代码语言:javascript复制
// 编辑资源 回显
handleUpdate(row) {
    // debugger;
    this.dialogVisible = true;
    this.isEdit = true;
    this.resource = Object.assign({}, row);
},

0 人点赞