【第24讲到第44讲】基于vue + spring boot学生宿舍管理系统

第24讲 菜单管理列表制作
1、api下新建menu.js
import http from '@/utils/http'
export const getMenuListApi = async() =>{
    
    
    return await http.get("/api/menu/list",null)
}
2、sysMenuList.vue
<template>
  <el-main>
    <el-form size="small">
      <el-form-item>
        <el-button type="primary" icon="el-icon-plus">新增</el-button>
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      :data="tableData"
      border
      stripe
      row-key="id"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="date" label="日期" sortable width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" sortable width="180">
      </el-table-column>
      <el-table-column prop="address" label="地址"> </el-table-column>
    </el-table>
  </el-main>
</template>

<script>
import {
    
    getMenuListApi} from '@/api/menu'
export default {
    
    
  data() {
    
    
    return {
    
    
      //表格高度
      tableHeight: 0,
      //表格数据
      tableData: [
        {
    
    
          id: 1,
          date: "2016-05-02",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1518 弄",
        },
        {
    
    
          id: 2,
          date: "2016-05-04",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1517 弄",
        },
        {
    
    
          id: 3,
          date: "2016-05-01",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1519 弄",
          children: [
            {
    
    
              id: 31,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
            {
    
    
              id: 32,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
          ],
        },
        {
    
    
          id: 4,
          date: "2016-05-03",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1516 弄",
        },
      ],
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created(){
    
    
    this.getList()
  },
  methods:{
    
    
    async getList(){
    
    
      let res = await getMenuListApi()
      if(res && res.code == 200){
    
    
        console.log(res)
        // this.tableData = res.data
      }
    }
  }
};
</script>

<style lang="scss" scoped>
</style>
第25讲 新增菜单制作
<template>
  <el-main>
    <el-form size="small">
      <el-form-item>
        <el-button @click="addBtn" type="primary" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      :data="tableData"
      border
      stripe
      row-key="id"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="date" label="日期" sortable width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" sortable width="180">
      </el-table-column>
      <el-table-column prop="address" label="地址"> </el-table-column>
    </el-table>
    <!-- 新增、编辑 -->
    <sys-dialog
      :title="dialog.title"
      :height="dialog.height"
      :width="dialog.width"
      :visible="dialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
          style="margin-right:8px;"
        >
          <el-row>
            <el-col :span="24" :offset="0">
              <el-form-item label="菜单类型">
                <el-radio-group v-model="addModel.type">
                  <el-radio :label="'0'">目录</el-radio>
                  <el-radio :label="'1'">菜单</el-radio>
                  <el-radio :label="'2'">按钮</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="上级菜单">
                <!-- <el-input type="hidden" v-model="addModel.parentId"></el-input> -->
                <el-input v-model="addModel.parentName"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单名称">
                <el-input v-model="addModel.title"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col v-if="addModel.type != '2'" :span="12" :offset="0">
              <el-form-item label="菜单图标">
                <el-input v-model="addModel.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col v-if="addModel.type == '1'" :span="12" :offset="0">
              <el-form-item label="路由名称">
                <el-input v-model="addModel.name"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row v-if="addModel.type == '1'">
            <el-col :span="12" :offset="0">
              <el-form-item label="路由地址">
                <el-input v-model="addModel.path"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="组件路径">
                <el-input v-model="addModel.url"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="权限字段">
                <el-input v-model="addModel.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单序号">
                <el-input v-model="addModel.orderNum"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
     getMenuListApi } from "@/api/menu";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表单验证规则
      rules: {
    
    },
      //表单数据
      addModel: {
    
    
        editType: "", // 0:新增 1:编辑
        menuId: "",
        type: "",
        parentId: "",
        title: "",
        code: "",
        name: "",
        path: "",
        url: "",
        icon: "",
        parentName: "",
        orderNum: "",
      },
      //弹框属性
      dialog: {
    
    
        width:650,
        title: "",
        height: 280,
        visible: false,
      },
      //表格高度
      tableHeight: 0,
      //表格数据
      tableData: [
        {
    
    
          id: 1,
          date: "2016-05-02",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1518 弄",
        },
        {
    
    
          id: 2,
          date: "2016-05-04",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1517 弄",
        },
        {
    
    
          id: 3,
          date: "2016-05-01",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1519 弄",
          children: [
            {
    
    
              id: 31,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
            {
    
    
              id: 32,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
          ],
        },
        {
    
    
          id: 4,
          date: "2016-05-03",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1516 弄",
        },
      ],
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增菜单";
      this.dialog.visible = true;
    },
    //弹框确定
    onConfirm() {
    
    
      this.dialog.visible = false;
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    async getList() {
    
    
      let res = await getMenuListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        // this.tableData = res.data
      }
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第26讲选择上级菜单
1、SysMenuService接口添加如下方法
List<SysMenu> parentList();

实现类

 @Override
    public List<SysMenu> parentList() {
    
    
        String[] types = {
    
    "0","1"};
        QueryWrapper<SysMenu> query = new QueryWrapper<>();
        query.lambda().in(SysMenu::getType, Arrays.asList(types)).orderByAsc(SysMenu::getOrderNum);
        List<SysMenu> menuList = this.baseMapper.selectList(query);
        //组装顶级菜单,防止无数据的时候,没有菜单
        SysMenu menu = new SysMenu();
        menu.setMenuId(0L);
        menu.setParentId(-1L);
        menu.setTitle("顶级菜单");
        menuList.add(menu);
        List<SysMenu> list = MakeTree.makeMenuTree(menuList, -1L);
        return list;
    }
2、控制器
//上级菜单列表
    @GetMapping("/parent")
    public ResultVo getParentList(){
    
    
        List<SysMenu> list = sysMenuService.parentList();
        return ResultUtils.success("查询成功",list);
    }
3、api/menu.js
//上级菜单树
export const getParentMenuListApi = async() =>{
    
    
    return await http.get("/api/menu/parent",null)
}
4、上级菜单弹框
<template>
  <el-main>
    <el-form size="small">
      <el-form-item>
        <el-button @click="addBtn" type="primary" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      :data="tableData"
      border
      stripe
      row-key="id"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="date" label="日期" sortable width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" sortable width="180">
      </el-table-column>
      <el-table-column prop="address" label="地址"> </el-table-column>
    </el-table>
    <!-- 新增、编辑 -->
    <sys-dialog
      :title="dialog.title"
      :height="dialog.height"
      :width="dialog.width"
      :visible="dialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
          style="margin-right: 8px"
        >
          <el-row>
            <el-col :span="24" :offset="0">
              <el-form-item label="菜单类型">
                <el-radio-group v-model="addModel.type">
                  <el-radio :label="'0'">目录</el-radio>
                  <el-radio :label="'1'">菜单</el-radio>
                  <el-radio :label="'2'">按钮</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="上级菜单">
                <!-- <el-input type="hidden" v-model="addModel.parentId"></el-input> -->
                <el-input
                  @click.native="selectParent"
                  readonly
                  v-model="addModel.parentName"
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单名称">
                <el-input v-model="addModel.title"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col v-if="addModel.type != '2'" :span="12" :offset="0">
              <el-form-item label="菜单图标">
                <el-input v-model="addModel.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col v-if="addModel.type == '1'" :span="12" :offset="0">
              <el-form-item label="路由名称">
                <el-input v-model="addModel.name"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row v-if="addModel.type == '1'">
            <el-col :span="12" :offset="0">
              <el-form-item label="路由地址">
                <el-input v-model="addModel.path"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="组件路径">
                <el-input v-model="addModel.url"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="权限字段">
                <el-input v-model="addModel.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单序号">
                <el-input v-model="addModel.orderNum"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
    <!-- 上级部门弹框 -->
    <sys-dialog
      :title="parentDialog.title"
      :width="parentDialog.width"
      :height="parentDialog.height"
      :visible="parentDialog.visible"
      @onClose="parentClose"
      @onConfirm="parentConfirm"
    >
      <div slot="content">
        <el-tree
          ref="parentTree"
          :data="parentData"
          node-key="menuId"
          :props="defaultProps"
          empty-text="暂无数据"
          :show-checkbox="false"
          :highlight-current="true"
          default-expand-all
          :expand-on-click-node="false"
          @node-click="handleNodeClick"
        ></el-tree>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
     getMenuListApi, getParentMenuListApi } from "@/api/menu";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      defaultProps: {
    
    
        children: "children",
        label: "title",
      },
      //上级菜单树数据
      parentData: [],
      //上级弹框属性
      parentDialog: {
    
    
        width: 300,
        title: "选择上级菜单",
        height: 450,
        visible: false,
      },
      //表单验证规则
      rules: {
    
    },
      //表单数据
      addModel: {
    
    
        editType: "", // 0:新增 1:编辑
        menuId: "",
        type: "",
        parentId: "",
        title: "",
        code: "",
        name: "",
        path: "",
        url: "",
        icon: "",
        parentName: "",
        orderNum: "",
      },
      //弹框属性
      dialog: {
    
    
        width: 650,
        title: "",
        height: 280,
        visible: false,
      },
      //表格高度
      tableHeight: 0,
      //表格数据
      tableData: [
        {
    
    
          id: 1,
          date: "2016-05-02",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1518 弄",
        },
        {
    
    
          id: 2,
          date: "2016-05-04",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1517 弄",
        },
        {
    
    
          id: 3,
          date: "2016-05-01",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1519 弄",
          children: [
            {
    
    
              id: 31,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
            {
    
    
              id: 32,
              date: "2016-05-01",
              name: "王小虎",
              address: "上海市普陀区金沙江路 1519 弄",
            },
          ],
        },
        {
    
    
          id: 4,
          date: "2016-05-03",
          name: "王小虎",
          address: "上海市普陀区金沙江路 1516 弄",
        },
      ],
      selectNode: {
    
    
        id: "",
        title: "",
      },
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //上级树节点点击事件
    handleNodeClick(node) {
    
    
      console.log(node);
      this.selectNode.id = node.menuId
      this.selectNode.title = node.title
    },
    parentConfirm() {
    
    
      this.addModel.parentId = this.selectNode.id
      this.addModel.parentName = this.selectNode.title
      this.parentDialog.visible = false;
    },
    parentClose() {
    
    
      this.parentDialog.visible = false;
    },
    //选择上级菜单
    async selectParent() {
    
    
      let res = await getParentMenuListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.parentData = res.data;
      }
      console.log(this.parentData);
      this.parentDialog.visible = true;
    },
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增菜单";
      this.dialog.visible = true;
    },
    //弹框确定
    onConfirm() {
    
    
      this.dialog.visible = false;
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    async getList() {
    
    
      let res = await getMenuListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        // this.tableData = res.data
      }
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第27讲 菜单新增对接
1、api/menu.js添加如下方法
export const addMenuApi = async(parm) =>{
    
    
    return await http.post("/api/menu",parm)
}
//编辑
export const editMenuApi = async(parm) =>{
    
    
    return await http.put(parm)
}
2、sysMenuList.vue
<template>
  <el-main>
    <el-form size="small">
      <el-form-item>
        <el-button @click="addBtn" type="primary" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      :data="tableData"
      border
      stripe
      row-key="menuId"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="title" label="菜单名称"> </el-table-column>
      <el-table-column prop="title" label="菜单图标">
        <template slot-scope="scope">
          <i :class="scope.row.icon"></i>
        </template>
      </el-table-column>
      <el-table-column prop="name" label="菜单类型"> 
        <template slot-scope="scope">
          <el-tag v-if="scope.row.type == '0'">目录</el-tag>
          <el-tag type="success" v-if="scope.row.type == '1'">菜单</el-tag>
          <el-tag type="danger" v-if="scope.row.type == '2'">按钮</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="name" label="路由名称"> </el-table-column>
      <el-table-column prop="path" label="路由地址"> </el-table-column>
    </el-table>
    <!-- 新增、编辑 -->
    <sys-dialog
      :title="dialog.title"
      :height="dialog.height"
      :width="dialog.width"
      :visible="dialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
          style="margin-right: 8px"
        >
          <el-row>
            <el-col :span="24" :offset="0">
              <el-form-item prop="type" label="菜单类型">
                <el-radio-group v-model="addModel.type">
                  <el-radio :label="'0'">目录</el-radio>
                  <el-radio :label="'1'">菜单</el-radio>
                  <el-radio :label="'2'">按钮</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item prop="parentName" label="上级菜单">
                <!-- <el-input type="hidden" v-model="addModel.parentId"></el-input> -->
                <el-input
                  @click.native="selectParent"
                  v-model="addModel.parentName"
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="title" label="菜单名称">
                <el-input v-model="addModel.title"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col v-if="addModel.type != '2'" :span="12" :offset="0">
              <el-form-item label="菜单图标">
                <el-input v-model="addModel.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col v-if="addModel.type == '1'" :span="12" :offset="0">
              <el-form-item prop="name" label="路由名称">
                <el-input v-model="addModel.name"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row v-if="addModel.type == '1'">
            <el-col :span="12" :offset="0">
              <el-form-item prop="path" label="路由地址">
                <el-input v-model="addModel.path"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="url" label="组件路径">
                <el-input v-model="addModel.url"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="权限字段">
                <el-input v-model="addModel.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单序号">
                <el-input v-model="addModel.orderNum"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
    <!-- 上级菜单 -->
    <sys-dialog
      :title="parentDialog.title"
      :width="parentDialog.width"
      :height="parentDialog.height"
      :visible="parentDialog.visible"
      @onClose="parentClose"
      @onConfirm="parentConfirm"
    >
      <div slot="content">
        <el-tree
          :data="treeData"
          :props="defaultProps"
          @node-click="handleNodeClick"
        ></el-tree>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
    
  getMenuListApi,
  getParentMenuListApi,
  addMenuApi,
  editMenuApi,
} from "@/api/menu";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      defaultProps: {
    
    
        children: "children",
        label: "title",
      },
      //上级菜单数据
      treeData: [],
      //上级弹框属性
      parentDialog: {
    
    
        width: 300,
        title: "选择上级菜单",
        height: 450,
        visible: false,
      },
      //表单验证规则
      rules: {
    
    
        type: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择菜单类型",
          },
        ],
        parentName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择上级菜单",
          },
        ],
        title: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写菜单名称",
          },
        ],
        name: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写路由名称",
          },
        ],
        path: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写路由地址",
          },
        ],
        url: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写组件路径",
          },
        ],
      },
      //表单数据
      addModel: {
    
    
        editType: "", // 0:新增 1:编辑
        menuId: "",
        type: "",
        parentId: "",
        title: "",
        code: "",
        name: "",
        path: "",
        url: "",
        icon: "",
        parentName: "",
        orderNum: "",
      },
      //弹框属性
      dialog: {
    
    
        width: 650,
        title: "",
        height: 280,
        visible: false,
      },
      //表格高度
      tableHeight: 0,
      //表格数据
      tableData: [],
      //树选择的数据
      selectNode: {
    
    
        id: "",
        title: "",
      },
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //上级菜单树点击事件
    handleNodeClick(node) {
    
    
      console.log(node);
      this.selectNode.id = node.menuId;
      this.selectNode.title = node.title;
    },
    //上级菜单确定事件
    parentConfirm() {
    
    
      this.addModel.parentId = this.selectNode.id;
      this.addModel.parentName = this.selectNode.title;
      this.parentDialog.visible = false;
      console.log(this.addModel);
    },
    //上级菜单关闭事件
    parentClose() {
    
    
      this.parentDialog.visible = false;
    },
    //选择上级菜单事件
    async selectParent() {
    
    
      //查询上级菜单树数据
      let res = await getParentMenuListApi();
      if (res && res.code == 200) {
    
    
        this.treeData = res.data;
      }
      this.parentDialog.visible = true;
    },
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增菜单";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      this.addModel.editType = "0";
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addRef.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.editType == "0") {
    
    
            res = await addMenuApi(this.addModel);
          } else {
    
    
            res = await editMenuApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            this.getList();
            this.dialog.visible = false;
          }
        }
      });
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    async getList() {
    
    
      let res = await getMenuListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.tableData = res.data;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第28讲 菜单删除接口对接
1、api/menu.js添加如下方法
import http from '@/utils/http'
//列表
export const getMenuListApi = async() =>{
    
    
    return await http.get("/api/menu/list",null)
}
//上级列表
export const getParentMenuListApi = async() =>{
    
    
    return await http.get("/api/menu/parent",null)
}
//新增
export const addMenuApi = async(parm) =>{
    
    
    return await http.post("/api/menu",parm)
}
//编辑
export const editMenuApi = async(parm) =>{
    
    
    return await http.put("/api/menu",parm)
}
//删除
export const deleteMenuApi = async(parm) =>{
    
    
    return await http.delete("/api/menu",parm)
}
2、sysMenuList.vue
<template>
  <el-main>
    <el-form size="small">
      <el-form-item>
        <el-button @click="addBtn" type="primary" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      :data="tableData"
      border
      stripe
      row-key="menuId"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column prop="title" label="菜单名称"> </el-table-column>
      <el-table-column prop="title" label="菜单图标">
        <template slot-scope="scope">
          <i :class="scope.row.icon"></i>
        </template>
      </el-table-column>
      <el-table-column prop="name" label="菜单类型">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.type == '0'">目录</el-tag>
          <el-tag type="success" v-if="scope.row.type == '1'">菜单</el-tag>
          <el-tag type="danger" v-if="scope.row.type == '2'">按钮</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="name" label="路由名称"> </el-table-column>
      <el-table-column prop="path" label="路由地址"> </el-table-column>
      <el-table-column label="操作" align="center" width="180">
        <template slot-scope="scope">
          <el-button
            type="primary"
            icon="el-icon-edit"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            type="danger"
            icon="el-icon-delete"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 新增、编辑 -->
    <sys-dialog
      :title="dialog.title"
      :height="dialog.height"
      :width="dialog.width"
      :visible="dialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
          style="margin-right: 8px"
        >
          <el-row>
            <el-col :span="24" :offset="0">
              <el-form-item prop="type" label="菜单类型">
                <el-radio-group v-model="addModel.type">
                  <el-radio :label="'0'">目录</el-radio>
                  <el-radio :label="'1'">菜单</el-radio>
                  <el-radio :label="'2'">按钮</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item prop="parentName" label="上级菜单">
                <!-- <el-input type="hidden" v-model="addModel.parentId"></el-input> -->
                <el-input
                  @click.native="selectParent"
                  v-model="addModel.parentName"
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="title" label="菜单名称">
                <el-input v-model="addModel.title"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col v-if="addModel.type != '2'" :span="12" :offset="0">
              <el-form-item label="菜单图标">
                <el-input v-model="addModel.icon"></el-input>
              </el-form-item>
            </el-col>
            <el-col v-if="addModel.type == '1'" :span="12" :offset="0">
              <el-form-item prop="name" label="路由名称">
                <el-input v-model="addModel.name"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row v-if="addModel.type == '1'">
            <el-col :span="12" :offset="0">
              <el-form-item prop="path" label="路由地址">
                <el-input v-model="addModel.path"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="url" label="组件路径">
                <el-input v-model="addModel.url"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="权限字段">
                <el-input v-model="addModel.code"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item label="菜单序号">
                <el-input v-model="addModel.orderNum"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
    <!-- 上级菜单 -->
    <sys-dialog
      :title="parentDialog.title"
      :width="parentDialog.width"
      :height="parentDialog.height"
      :visible="parentDialog.visible"
      @onClose="parentClose"
      @onConfirm="parentConfirm"
    >
      <div slot="content">
        <el-tree
          ref="parentTree"
          :data="treeData"
          node-key="menuId"
          :props="defaultProps"
          empty-text="暂无数据"
          :show-checkbox="false"
          :highlight-current="true"
          default-expand-all
          :expand-on-click-node="false"
          @node-click="handleNodeClick"
        >
          <div slot-scope="{ node, data }">
            <!-- 如果没有下级,显示文档图标 -->
            <span v-if="data.children.length == 0">
              <i style="margin-left: 3px" class="el-icon-document"></i>
            </span>
            <!-- 有下级,判断是否展开 -->
            <span v-else @click.stop="openBtn(data)">
              <i
                v-if="data.open"
                style="margin-left: 3px"
                class="el-icon-plus"
              ></i>
              <i v-else style="margin-left: 3px" class="el-icon-minus"></i>
            </span>
            <span style="margin-left: 3px">{
    
    {
    
     node.label }}</span>
          </div>
        </el-tree>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
    
  getMenuListApi,
  getParentMenuListApi,
  addMenuApi,
  editMenuApi,
  deleteMenuApi,
} from "@/api/menu";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      defaultProps: {
    
    
        children: "children",
        label: "title",
      },
      //上级菜单数据
      treeData: [],
      //上级弹框属性
      parentDialog: {
    
    
        width: 300,
        title: "选择上级菜单",
        height: 450,
        visible: false,
      },
      //表单验证规则
      rules: {
    
    
        type: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择菜单类型",
          },
        ],
        parentName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择上级菜单",
          },
        ],
        title: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写菜单名称",
          },
        ],
        name: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写路由名称",
          },
        ],
        path: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写路由地址",
          },
        ],
        url: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写组件路径",
          },
        ],
      },
      //表单数据
      addModel: {
    
    
        editType: "", // 0:新增 1:编辑
        menuId: "",
        type: "",
        parentId: "",
        title: "",
        code: "",
        name: "",
        path: "",
        url: "",
        icon: "",
        parentName: "",
        orderNum: "",
      },
      //弹框属性
      dialog: {
    
    
        width: 650,
        title: "",
        height: 280,
        visible: false,
      },
      //表格高度
      tableHeight: 0,
      //表格数据
      tableData: [],
      //树选择的数据
      selectNode: {
    
    
        id: "",
        title: "",
      },
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //编辑
    editBtn(row) {
    
    
      //设置弹框属性
      this.dialog.title = "编辑菜单";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      //把要编辑的数据放到表单绑定的数据里面,回显
      this.$objCoppy(row, this.addModel);
      this.addModel.editType = "1";
    },
    //删除按钮
    async deleteBtn(row) {
    
    
      const confirm = await this.$myconfirm("确定删除该数据吗?");
      if (confirm) {
    
    
        let res = await deleteMenuApi({
    
     menuId: row.menuId });
        if (res && res.code == 200) {
    
    
          this.$message({
    
     type: "success", message: res.msg });
          this.getList();
        }
      }
    },
    //上级部门节点加号和减号点击事件
    openBtn(data) {
    
    
      data.open = !data.open;
      this.$refs.parentTree.store.nodesMap[data.menuId].expanded = !data.open;
    },
    //上级菜单树点击事件
    handleNodeClick(node) {
    
    
      console.log(node);
      this.selectNode.id = node.menuId;
      this.selectNode.title = node.title;
    },
    //上级菜单确定事件
    parentConfirm() {
    
    
      this.addModel.parentId = this.selectNode.id;
      this.addModel.parentName = this.selectNode.title;
      this.parentDialog.visible = false;
      console.log(this.addModel);
    },
    //上级菜单关闭事件
    parentClose() {
    
    
      this.parentDialog.visible = false;
    },
    //选择上级菜单事件
    async selectParent() {
    
    
      //查询上级菜单树数据
      let res = await getParentMenuListApi();
      if (res && res.code == 200) {
    
    
        this.treeData = res.data;
      }
      this.parentDialog.visible = true;
    },
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增菜单";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      this.addModel.editType = "0";
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addRef.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.editType == "0") {
    
    
            res = await addMenuApi(this.addModel);
          } else {
    
    
            res = await editMenuApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            this.$message({
    
     type: "success", message: res.msg });
            this.getList();
            this.dialog.visible = false;
          }
        }
      });
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    async getList() {
    
    
      let res = await getMenuListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.tableData = res.data;
      }
    },
  },
};
</script>


<style lang="scss" scoped>
::v-deep .el-tree {
    
    
  // 将每一行的设置为相对定位 方便后面before after 使用绝对定位来固定位置
  .el-tree-node {
    
    
    position: relative;
    padding-left: 10px;
  }
  // 子集像右偏移 给数线留出距离
  .el-tree-node__children {
    
    
    padding-left: 20px;
  }
  //这是竖线
  .el-tree-node :last-child:before {
    
    
    height: 40px;
  }
  .el-tree > .el-tree-node:before {
    
    
    border-left: none;
  }
  .el-tree > .el-tree-node:after {
    
    
    border-top: none;
  }
  //这自定义的线 的公共部分
  .el-tree-node:before,
  .el-tree-node:after {
    
    
    content: "";
    left: -4px;
    position: absolute;
    right: auto;
    border-width: 1px;
  }
  .tree :first-child .el-tree-node:before {
    
    
    border-left: none;
  }
  // 竖线
  .el-tree-node:before {
    
    
    border-left: 1px dotted #d9d9d9;
    bottom: 0px;
    height: 100%;
    top: -25px;
    width: 1px;
  }
  //横线
  .el-tree-node:after {
    
    
    border-top: 1px dotted #d9d9d9;
    height: 20px;
    top: 14px;
    width: 24px;
  }
  .el-tree-node__expand-icon.is-leaf {
    
    
    width: 8px;
  }
  //去掉elementui自带的展开按钮  一个向下的按钮,打开时向右
  .el-tree-node__content > .el-tree-node__expand-icon {
    
    
    display: none;
  }
  //每一行的高度
  .el-tree-node__content {
    
    
    line-height: 30px;
    height: 30px;
    padding-left: 10px !important;
  }
}
//去掉最上级的before  after 即是去电最上层的连接线
::v-deep .el-tree > div {
    
    
  &::before {
    
    
    display: none;
  }
  &::after {
    
    
    display: none;
  }
}
</style>
第29讲 用户分配角色
1、api/user.js添加getRoleListApi 和 getRoleByUserIdApi()和方法
//角色列表
export const getRoleListApi = async()=>{
    
    
  return await http.get("/api/user/roleList")
}
//根据用户Id查询角色
export const getRoleByUserIdApi = async(parm)=>{
    
    
  return await http.get("/api/user/getRoleByUserId",parm)
}
2、新建SysRoleMenu实体类
package com.itmk.web.sys_role_menu.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
@TableName("sys_role_menu")
public class SysRoleMenu {
    
    
    @TableId(type = IdType.AUTO)
    private Long roleMenuId;
    private Long roleId;
    private Long menuId;
}

3、新建SysRoleMenuMapper接口
package com.itmk.web.sys_role_menu.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
    
    
}

映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itmk.web.sys_role_menu.mapper.SysRoleMenuMapper">

</mapper>
4、新建SysRoleMenuService接口
package com.itmk.web.sys_role_menu.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysRoleMenuService extends IService<SysRoleMenu> {
    
    
}

实现类

package com.itmk.web.sys_role_menu.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;
import com.itmk.web.sys_role_menu.mapper.SysRoleMenuMapper;
import com.itmk.web.sys_role_menu.service.SysRoleMenuService;
import org.springframework.stereotype.Service;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Service
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
    
    
}

5、新建SysUserRole实体类
package com.itmk.web.sys_user_role.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
@TableName("sys_user_role")
public class SysUserRole {
    
    
    @TableId(type = IdType.AUTO)
    private Long userRoleId;
    private Long userId;
    private Long roleId;
}

6、SysUserRoleMapper接口
package com.itmk.web.sys_user_role.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itmk.web.sys_user_role.entity.SysUserRole;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
    
    
}

映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itmk.web.sys_user_role.mapper.SysUserRoleMapper">

</mapper>
7、新建SysUserRoleService接口
package com.itmk.web.sys_user_role.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.sys_user_role.entity.SysUserRole;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysUserRoleService extends IService<SysUserRole> {
    
    
}

实现类

package com.itmk.web.sys_user_role.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.sys_user_role.entity.SysUserRole;
import com.itmk.web.sys_user_role.mapper.SysUserRoleMapper;
import com.itmk.web.sys_user_role.service.SysUserRoleService;
import org.springframework.stereotype.Service;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Service
public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {
    
    
}

8、SysUserService新增add和edit方法
package com.itmk.web.sys_user.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.sys_user.entity.PageParm;
import com.itmk.web.sys_user.entity.SysUser;

public interface SysUserService extends IService<SysUser> {
    
    
    IPage<SysUser> list(PageParm parm);
    //新增
    void add(SysUser user);
    //编辑
    void edit(SysUser user);
}

实现类

package com.itmk.web.sys_user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.sys_user.entity.PageParm;
import com.itmk.web.sys_user.entity.SysUser;
import com.itmk.web.sys_user.mapper.SysUserMapper;
import com.itmk.web.sys_user.service.SysUserService;
import com.itmk.web.sys_user_role.entity.SysUserRole;
import com.itmk.web.sys_user_role.service.SysUserRoleService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    
    
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Override
    public IPage<SysUser> list(PageParm parm) {
    
    
        //构造分页对象
        IPage<SysUser> page = new Page<>();
        page.setCurrent(parm.getCurrentPage());
        page.setSize(parm.getPageSize());
        //构造查询条件
        QueryWrapper<SysUser> query = new QueryWrapper<>();
        if(StringUtils.isNotEmpty(parm.getNickName())){
    
    
            query.lambda().like(SysUser::getNickName,parm.getNickName());
        }
        if(StringUtils.isNotEmpty(parm.getPhone())){
    
    
            query.lambda().like(SysUser::getPhone,parm.getPhone());
        }
        return this.baseMapper.selectPage(page,query);
    }

    @Override
    @Transactional
    public void add(SysUser user) {
    
    
        //新增用户
        int insert = this.baseMapper.insert(user);
        //保存角色
        if(insert > 0){
    
    
            SysUserRole role = new SysUserRole();
            role.setRoleId(user.getRoleId());
            role.setUserId(user.getUserId());
            sysUserRoleService.save(role);
        }
    }

    @Override
    @Transactional
    public void edit(SysUser user) {
    
    
        //编辑
        int i = this.baseMapper.updateById(user);
        //角色 :先删除,再插入
        if(i > 0){
    
    
            //先删除原来的角色
            QueryWrapper<SysUserRole> query = new QueryWrapper<>();
            query.lambda().eq(SysUserRole::getUserId,user.getUserId());
            sysUserRoleService.remove(query);
            //保存
            SysUserRole role = new SysUserRole();
            role.setUserId(user.getUserId());
            role.setRoleId(user.getRoleId());
            sysUserRoleService.save(role);
        }
    }
}

9、SysUserController控制器
package com.itmk.web.sys_user.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.sys_role.entity.SysRole;
import com.itmk.web.sys_role.service.SysRoleService;
import com.itmk.web.sys_user.entity.PageParm;
import com.itmk.web.sys_user.entity.SysUser;
import com.itmk.web.sys_user.service.SysUserService;
import com.itmk.web.sys_user_role.entity.SysUserRole;
import com.itmk.web.sys_user_role.service.SysUserRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.List;

@RestController
@RequestMapping("/api/user")
public class SysUserController {
    
    
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysUserRoleService sysUserRoleService;

    //新增用户
    @PostMapping
    public ResultVo addUser(@RequestBody SysUser user) {
    
    
        //判断账户是否被占用
        QueryWrapper<SysUser> query = new QueryWrapper<>();
        query.lambda().eq(SysUser::getUsername, user.getUsername());
        SysUser one = sysUserService.getOne(query);
        if (one != null) {
    
    
            return ResultUtils.error("账户被占用!");
        }
        user.setIsAdmin("0");
        user.setCreateTime(new Date());
        //入库处理
        sysUserService.add(user);
        return ResultUtils.success("新增用户成功!");
    }

    //编辑用户
    @PutMapping
    public ResultVo editUser(@RequestBody SysUser user) {
    
    
        //判断账户是否被占用
        QueryWrapper<SysUser> query = new QueryWrapper<>();
        query.lambda().eq(SysUser::getUsername, user.getUsername());
        SysUser one = sysUserService.getOne(query);
        if (one != null && one.getUserId() != user.getUserId()) {
    
    
            return ResultUtils.error("账户被占用!");
        }
        user.setUpdateTime(new Date());
        //更新处理
        sysUserService.edit(user);
        return ResultUtils.success("编辑用户成功!");
    }

    //删除用户
    @DeleteMapping("/{userId}")
    public ResultVo deleteUser(@PathVariable("userId") Long userId) {
    
    
        boolean remove = sysUserService.removeById(userId);
        if (remove) {
    
    
            return ResultUtils.success("删除成功!");
        }
        return ResultUtils.error("删除失败!");
    }

    //列表查询
    @GetMapping("/list")
    public ResultVo getList(PageParm parm) {
    
    
        IPage<SysUser> list = sysUserService.list(parm);
        //密码不显示
        list.getRecords().stream().forEach(item -> {
    
    
            item.setPassword("");
        });
        return ResultUtils.success("查询成功", list);
    }

    //查询角色列表
    @GetMapping("/roleList")
    public ResultVo getRoleList(){
    
    
        List<SysRole> list = sysRoleService.list();
        return ResultUtils.success("查询成功",list);
    }

    //查询用户对应的角色
    @GetMapping("/getRoleByUserId")
    public ResultVo getRoleByUserId(Long userId){
    
    
        QueryWrapper<SysUserRole> query = new QueryWrapper<>();
        query.lambda().eq(SysUserRole::getUserId,userId);
        SysUserRole one = sysUserRoleService.getOne(query);
        return ResultUtils.success("查询成功",one);
    }
}

第30讲 登录页面制作

<template>
  <div class="logincontainer">
    <el-form
      class="loginform"
      :model="addModel"
      ref="loginForm"
      :rules="rules"
      :inline="false"
      size="medium"
    >
      <el-form-item>
        <span class="loginTitle">高校宿舍管理系统</span>
      </el-form-item>
      <el-form-item prop="username">
        <el-input
          v-model="addModel.username"
          placeholder="请输入账户"
        ></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
          v-model="addModel.password"
          placeholder="请输入密码"
        ></el-input>
      </el-form-item>
      <el-form-item prop="userType">
        <el-radio-group v-model="addModel.userType">
          <el-radio :label="0">学生</el-radio>
          <el-radio :label="1">管理</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item>
        <el-row :gutter="20">
          <el-col :span="12" :offset="0">
            <el-button class="loginbtn" type="primary" @click="onSubmit"
              >登录</el-button
            >
          </el-col>
          <el-col :span="12" :offset="0">
            <el-button class="loginbtn">取消</el-button>
          </el-col>
        </el-row>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
    
    
  data() {
    
    
    return {
    
    
      //表单数据
      addModel: {
    
    
        username: "",
        password: "",
        userType: "",
      },
      //表单验证规则
      rules: {
    
    
        username: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请输入账户",
          },
        ],
        password: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请输入密码",
          },
        ],
        userType: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请选择用户类型",
          },
        ],
      },
    };
  },
  methods: {
    
    
    onSubmit() {
    
    
      this.$refs.loginForm.validate((valid) => {
    
    
        if (valid) {
    
    
          console.log("验证通过");
          console.log(this.addModel)
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.logincontainer {
    
    
  height: 100%;
  background: #fff;
  background-image: url("../../assets/images/login_bg.png");
  display: flex;
  align-items: center;
  justify-content: center;
  background-size: 100% 100%;
  .loginform {
    
    
    height: 350px;
    width: 450px;
    background: #fff;
    padding: 35px 20px;
    border-radius: 10px;
    .loginTitle {
    
    
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 24px;
      color: #409eff;
      font-weight: 600;
      margin-bottom: 10px;
    }
    .loginbtn {
    
    
      width: 100%;
    }
  }
}
</style>
第31讲 用户登录接口开发

依赖

<jwt.version>3.10.3</jwt.version>
<dependency>
       <groupId>com.auth0</groupId>
       <artifactId>java-jwt</artifactId>
       <version>${
    
    jwt.version}</version>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
</dependency>
1、jwt工具类讲解
1.1、jwt配置文件
#jwt配置
jwt:
  #颁发者
  issuer: itmk
  #秘钥
  secret: com.itmk
  #30分钟过期
  expiration: 30
1.2、JwtUtils工具
package com.itmk.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Component
@Data
@ConfigurationProperties(prefix = "jwt")
public class JwtUtils {
    
    
    //颁发者
    private String issuer;
    //密钥
    private String secret;
    // 过期时间 分钟
    private int expiration;

    /**
     * @param map 自定义参数
     */
    public String generateToken(Map<String, String> map) {
    
    
        //设置令牌的过期时间
        Calendar instance = Calendar.getInstance();
        //设置失效时间
        instance.add(Calendar.MINUTE, expiration);
        //创建JWT builder
        JWTCreator.Builder builder = JWT.create();
        //payload
        map.forEach((k, v) -> {
    
    
            builder.withClaim(k, v);
        });
        String token = builder.withIssuer(issuer).withIssuedAt(new Date()).withExpiresAt(instance.getTime()) //指定令牌过期时间
                .sign(Algorithm.HMAC256(secret));
        return token;
    }

    /**
     * 验证令牌是否合法
     *
     * @param token
     */
    public boolean verify(String token) {
    
    
        try {
    
    
            JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
        } catch (JWTVerificationException e) {
    
    
            return false;
        }
        return true;
    }

    /**
     * 解析token
     *
     * @param token
     * @return
     */
    public DecodedJWT jwtDecode(String token) {
    
    
        try {
    
    
            return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
        } catch (SignatureVerificationException e) {
    
    
            throw new RuntimeException("token签名错误!");
        } catch (AlgorithmMismatchException e) {
    
    
            throw new RuntimeException("token算法不匹配!");
        } catch (TokenExpiredException e) {
    
    
            throw new RuntimeException("token过期!");
        } catch (Exception e) {
    
    
            throw new RuntimeException("token解析失败!");
        }
    }
}

2、登录接口
package com.itmk.web.login.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itmk.jwt.JwtUtils;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.login.entity.LoginParm;
import com.itmk.web.login.entity.LoginResult;
import com.itmk.web.user.entity.User;
import com.itmk.web.user.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@RestController
@RequestMapping("/api/login")
public class LoginController {
    
    
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private SysUserService userService;

    @PostMapping("/login")
    public ResultVo login(@RequestBody LoginParm loginParm) {
    
    
        if (StringUtils.isEmpty(loginParm.getUsername()) || StringUtils.isEmpty(loginParm.getPassword())) {
    
    
            return ResultUtils.error("用户名或密码不能为空!");
        }
        //查询用户
        QueryWrapper<User> query = new QueryWrapper<>();
        query.lambda().eq(User::getUsername, loginParm.getUsername()).eq(User::getPassword,
                DigestUtils.md5DigestAsHex(loginParm.getPassword().getBytes()));
        User user = userService.getOne(query);
        if (user == null) {
    
    
            return ResultUtils.error("用户名或密码错误!");
        }
        //生成token
        Map<String, String> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("userId", Long.toString(user.getUserId()));
        String token = jwtUtils.generateToken(map);
        //构造返回值
        LoginResult result = new LoginResult();
        result.setUserId(user.getUserId());
        result.setToken(token);
        result.setUsername(user.getUsername());
        return ResultUtils.success("登录成功", result);
    }
}

package com.itmk.web.login.entity;

import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
public class LoginParm {
    
    
    private String username;
    private String password;
    private String userType;
}

package com.itmk.web.login.entity;

import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
public class LoginResult {
    
    
    private Long userId;
    private String username;
    private String token;
}

第32讲 登录接口对接

登录流程图

在这里插入图片描述

1、api/user.js添加loginApi()方法
import request from '@/utils/request'
import http from '@/utils/http'
export function login(data) {
    
    
  return request({
    
    
    url: '/vue-admin-template/user/login',
    method: 'post',
    data
  })
}

export function getInfo(token) {
    
    
  return request({
    
    
    url: '/vue-admin-template/user/info',
    method: 'get',
    params: {
    
     token }
  })
}

export function logout() {
    
    
  return request({
    
    
    url: '/vue-admin-template/user/logout',
    method: 'post'
  })
}
//获取用户列表
export const getListApi = async(parm) =>{
    
    
  return await http.get("/api/user/list",parm)
}
//新增
export const addUserApi = async(parm) =>{
    
    
  return await http.post("/api/user",parm)
}
//编辑
export const editUserApi = async(parm) =>{
    
    
  return await http.put("/api/user",parm)
}
//删除
export const deleteUserApi = async(parm) =>{
    
    
  return await http.delete("/api/user",parm)
}
//角色列表
export const getRoleListApi = async()=>{
    
    
  return await http.get("/api/user/roleList")
}
//获取角色
export const getRoleApi = async(parm)=>{
    
    
  return await http.get("/api/user/role",parm)
}
//登录对接
export const loginApi = async(parm)=>{
    
    
  return await http.post("/api/login/login",parm)
}
2、utils下的auth.js添加存储用户id的方法
import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token'
const userIdKey = 'userId'
export function getToken() {
    
    
  return Cookies.get(TokenKey)
}

export function setToken(token) {
    
    
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
    
    
  return Cookies.remove(TokenKey)
}
//存储用户id
export function setUserId(userId) {
    
    
  return sessionStorage.setItem(userIdKey, userId)
}
export function getUserId() {
    
    
  return sessionStorage.getItem(userIdKey)
}
export function removeUserId() {
    
    
  return sessionStorage.remove(userIdKey);
}
//sessionStorage清空
export function clearSession() {
    
    
  return sessionStorage.clear()
}

3、store/user.js中的login修改为如下
 // user login
  login({
     
      commit }, userInfo) {
    
    
    const {
    
     username, password,userType } = userInfo
    return new Promise((resolve, reject) => {
    
    
      loginApi({
    
     username: username.trim(), password: password ,userType:userType}).then(response => {
    
    
        console.log(response)
        const {
    
     data } = response
        // commit('SET_TOKEN', data.token)
        commit('SET_TOKEN', 'admin-token')
        setToken(data.token)
        resolve()
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },
4、登录页面
<template>
  <div class="logincontainer">
    <el-form
      class="loginForm"
      :model="addModel"
      ref="loginForm"
      :rules="rules"
      :inline="false"
      size="normal"
    >
      <el-form-item>
        <span class="loginTitle">高校宿舍管理系统</span>
      </el-form-item>
      <el-form-item prop="username">
        <el-input
          placeholder="请输入账户"
          v-model="addModel.username"
        ></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
          placeholder="请输入密码"
          v-model="addModel.password"
        ></el-input>
      </el-form-item>
      <el-form-item prop="userType">
        <el-radio-group v-model="addModel.userType">
          <el-radio :label="0">学生</el-radio>
          <el-radio :label="1">管理员</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item>
        <el-row :gutter="20">
          <el-col :span="12" :offset="0">
            <el-button class="mybtn" type="primary" @click="onSubmit"
              >登录</el-button
            >
          </el-col>
          <el-col :span="12" :offset="0">
            <el-button class="mybtn">取消</el-button>
          </el-col>
        </el-row>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
    
    
  data() {
    
    
    return {
    
    
      addModel: {
    
    
        username: "",
        password: "",
        userType: "",
      },
      rules: {
    
    
        username: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请输入账户",
          },
        ],
        password: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请输入密码",
          },
        ],
        userType: [
          {
    
    
            trigger: "change",
            required: true,
            message: "请选择用户类型",
          },
        ],
      },
    };
  },
  methods: {
    
    
    onSubmit() {
    
    
      this.$refs.loginForm.validate((valid) => {
    
    
        if (valid) {
    
    
          this.$store.dispatch("user/login", this.addModel).then(() => {
    
    
            this.$router.push({
    
     path: this.redirect || "/" });
          });
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.logincontainer {
    
    
  height: 100%;
  background: #fff;
  background-image: url("../../assets/images/login_bg.png");
  display: flex;
  align-items: center;
  justify-content: center;
  background-size: 100% 100%;
  .loginForm {
    
    
    height: 350px;
    width: 450px;
    background: #fff;
    padding: 35px 20px;
    border-radius: 10px;
    .loginTitle {
    
    
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 24px;
      font-weight: 600;
      color: #409eff;
    }
    .mybtn {
    
    
      width: 100%;
    }
  }
}
</style>
第33讲 分配权限树回显接口开发
1、SysMenuService接口新增getMenuByUserId()方法和getMenuByRoleId()方法
	//根据用户id查询权限
    List<SysMenu> getMenuByUserId(Long userId);
    //根据角色id查询权限
    List<SysMenu> getMenuByRoleId(Long roleId);

SysMenuServiceImpl实现类

	@Override
    public List<SysMenu> getMenuByUserId(Long userId) {
    
    
        return this.baseMapper.getMenuByUserId(userId);
    }

    @Override
    public List<SysMenu> getMenuByRoleId(Long roleId) {
    
    
        return this.baseMapper.getMenuByRoleId(roleId);
    }
2、SysMenuMapper接口新增getMenuByUserId()方法和getMenuByRoleId()方法
	//根据用户id查询权限
    List<SysMenu> getMenuByUserId(@Param("userId") Long userId);
	//根据角色id查询权限
    List<SysMenu> getMenuByRoleId(@Param("roleId") Long roleId);
3、SysMenuMapper.xml映射文件
<select id="getMenuByUserId" resultType="com.itmk.web.sys_menu.entity.SysMenu">
        select m.* from  sys_user_role as ur
        left join sys_role  as r on ur.role_id = r.role_id
        left join sys_role_menu as rm on r.role_id = rm.role_id
        left join sys_menu  as m on rm.menu_id = m.menu_id
        where ur.user_id =#{
    
    userId}
        order by m.order_num asc
</select>

<select id="getMenuByRoleId" resultType="com.itmk.web.sys_menu.entity.SysMenu">
        select m.* from sys_role_menu as rm , sys_menu as m
        where rm.menu_id = m.menu_id  and rm.role_id =#{
    
    roleId}
</select>
4、新建AssignVo实体类
package com.itmk.web.sys_role.entiy;

import com.itmk.web.sys_menu.entity.SysMenu;
import lombok.Data;

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

@Data
public class AssignVo {
    
    
    //当前用户拥有的菜单
    private List<SysMenu> menuList = new ArrayList<>();
    //被分配的角色拥有的菜单id
    private Object[] checkList;
}

5、SysRoleService接口新增getAssignShow()方法

参数实体类

package com.itmk.web.sys_role.entiy;

import lombok.Data;

@Data
public class AssignParm {
    private Long userId;
    private Long roleId;
}
	//角色权限的回显
    AssignVo getAssignShow(AssignParm parm);

实现类

	@Override
    public AssignVo getAssignShow(AssignParm parm) {
    
    
        //查询当前用户的信息
        SysUser user = sysUserService.getById(parm.getUserId());
        //菜单数据
        List<SysMenu> list = null;
        if(user.getIsAdmin().equals("1")){
    
     //如果是超级管理员,拥有所有的权限
            QueryWrapper<SysMenu> query = new QueryWrapper<>();
            query.lambda().orderByAsc(SysMenu::getOrderNum);
            list = sysMenuService.list(query);
        }else{
    
    
            list = sysMenuService.getMenuByUserId(user.getUserId());
        }
        //组装树
        List<SysMenu> menuList = MakeTree.makeMenuTree(list, 0L);
        //查询角色原来的菜单
        List<SysMenu> roleList = sysMenuService.getMenuByRoleId(parm.getRoleId());
        List<Long> ids = new ArrayList<>();
        Optional.ofNullable(roleList).orElse(new ArrayList<>()).stream().filter(item -> item != null).forEach(item ->{
    
    
            ids.add(item.getMenuId());
        });
        //组装数据
        AssignVo vo = new AssignVo();
        vo.setMenuList(menuList);
        vo.setCheckList(ids.toArray());
        return vo;
    }
6、SysRoleController控制器添加getAssingShow()方法
	//查询角色权限树的回显
    @GetMapping("/getAssingShow")
    public ResultVo getAssingShow(AssignParm parm) {
    
    
        AssignVo show = sysRoleService.getAssignShow(parm);
        return ResultUtils.success("查询成功", show);
    }
第34讲 分配权限回显对接
1、api/role.js添加assignRoleApi()方法
import http from '@/utils/http'
//列表
export const getListApi = async(parm) =>{
    
    
    return await http.get("/api/role/list",parm)
}
//新增
export const addRoleApi = async(parm) =>{
    
    
    return await http.post("/api/role",parm)
}
//编辑
export const editRoleApi = async(parm) =>{
    
    
    return await http.put("/api/role",parm)
}
//删除
export const deleteRoleApi = async(parm) =>{
    
    
    return await http.delete("/api/role",parm)
}
//分配权限回显
export const assignRoleApi = async(parm)=>{
    
    
    return await http.get("/api/role/getAssingShow",parm)
}
2、sysRoleList.vue页面
<template>
  <el-main>
    <!-- 搜索栏 -->
    <el-form
      :model="listParm"
      ref="searchRef"
      label-width="80px"
      :inline="true"
      size="small"
    >
      <el-form-item>
        <el-input
          placeholder="请输入角色名称"
          v-model="listParm.roleName"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button @click="searchBtn" icon="el-icon-search">搜索</el-button>
        <el-button @click="resetBtn" style="color: #ff7670" icon="el-icon-close"
          >重置</el-button
        >
        <el-button type="primary" @click="addBtn" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table
      :height="tableHeight"
      size="small"
      :data="tableData"
      border
      stripe
    >
      <el-table-column prop="roleName" label="角色名称"></el-table-column>
      <el-table-column prop="remark" label="角色备注"></el-table-column>
      <el-table-column label="操作" align="center" width="300">
        <template slot-scope="scope">
          <el-button
            type="primary"
            size="small"
            icon="el-icon-edit"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            type="primary"
            size="small"
            icon="el-icon-edit"
            @click="assignBtn(scope.row)"
            >分配权限</el-button
          >
          <el-button
            type="danger"
            size="small"
            icon="el-icon-delete"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="listParm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="listParm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="listParm.total"
      background
    >
    </el-pagination>
    <!-- 新增编辑弹框 -->
    <sys-dialog
      :title="dialog.title"
      :visible="dialog.visible"
      :height="dialog.height"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
        >
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item prop="roleName" label="角色名称">
                <el-input v-model="addModel.roleName"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="roleType" label="角色类型">
                <el-select v-model="addModel.roleType" placeholder="请选择">
                  <el-option
                    v-for="item in options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="角色备注">
                <el-input v-model="addModel.remark"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
    <!-- 分配权限弹框 -->
    <sys-dialog
      :title="assignDialog.title"
      :height="assignDialog.height"
      :width="assignDialog.width"
      :visible="assignDialog.visible"
      @confirm="assignConfirm"
      @onClose="assingClose"
    >
      <div slot="content">
        <el-tree
          ref="assignTree"
          :data="assignTreeData"
          node-key="menuId"
          :props="defaultProps"
          empty-text="暂无数据"
          :show-checkbox="true"
          default-expand-all
          :default-checked-keys="assignTreeChecked"
        ></el-tree>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
    
  getListApi,
  addRoleApi,
  editRoleApi,
  deleteRoleApi,
  assignRoleApi,
} from "@/api/role";
import {
    
     getUserId } from "@/utils/auth";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      defaultProps: {
    
    
        children: "children",
        label: "title",
      },
      //树数据
      assignTreeData: [],
      //角色原来的权限
      assignTreeChecked: [],
      //分配权限弹框属性
      assignDialog: {
    
    
        title: "",
        height: 450,
        width: 300,
        visible: false,
      },
      //角色类型
      options: [
        {
    
    
          value: "1",
          label: "系统用户",
        },
        {
    
    
          value: "2",
          label: "学生",
        },
      ],
      //表单验证规则
      rules: {
    
    
        roleName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写角色名称",
          },
        ],
        roleType: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择角色类型",
          },
        ],
      },
      //表单数据
      addModel: {
    
    
        type: "", // 0 新增 1: 编辑
        roleId: "",
        roleName: "",
        roleType: "",
        remark: "",
      },
      //弹框属性
      dialog: {
    
    
        height: 150,
        visible: false,
        title: "",
      },
      //表格的高度
      tableHeight: 0,
      //表格数据
      tableData: [],
      //列表参数
      listParm: {
    
    
        roleName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //分配权限确定
    assignConfirm() {
    
    
      this.assignDialog.visible = false;
    },
    //分配权限取消
    assingClose() {
    
    
      this.assignDialog.visible = false;
    },
    //分配权限按钮
    async assignBtn(row) {
    
    
      //清空数据
      this.roleId = "";
      this.assignTreeData = [];
      this.assignTreeChecked = [];
      this.roleId = row.roleId;
      //设置弹框属性
      this.assignDialog.title = "为【" + row.roleName + "】分配权限";
      this.assignDialog.visible = true;
      //获取权限数据
      let parm = {
    
    
        userId: getUserId(),
        roleId: this.roleId,
      };
      let res = await assignRoleApi(parm);
      console.log(res);
      if (res && res.code == 200) {
    
    
        this.assignTreeData = res.data.menuList;
        this.assignTreeChecked = res.data.checkList;
      }
      //如果角色原来有权限
      if (this.assignTreeChecked.length > 0) {
    
    
        let newArr = [];
        this.assignTreeChecked.forEach((item) => {
    
    
          this.checked(item, this.assignTreeData, newArr);
        });
        this.assignTreeChecked = newArr;
      }
    },
    //找出所有的回显数据
    checked(id, data, newArr) {
    
    
      data.forEach((item) => {
    
    
        if (item.menuId == id) {
    
    
          //是不是末级
          if (item.children && item.children.length == 0) {
    
    
            newArr.push(item.menuId);
          }
        } else {
    
    
          if (item.children && item.children.length != 0) {
    
    
            this.checked(id, item.children, newArr);
          }
        }
      });
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addRef.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addRoleApi(this.addModel);
          } else {
    
    
            res = await editRoleApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message({
    
     type: "success", message: res.msg });
            //刷新列表
            this.getList();
            this.dialog.visible = false;
          }
        }
      });
    },
    //获取表格数据
    async getList() {
    
    
      let res = await getListApi(this.listParm);
      if (res && res.code == 200) {
    
    
        console.log(res);
        //赋值给表格
        this.tableData = res.data.records;
        this.total = res.data.total;
      }
    },
    //页数改变时触发
    currentChange(val) {
    
    },
    //页容量改变时触发
    sizeChange(val) {
    
    },
    //删除按钮
    async deleteBtn(row) {
    
    
      //提示
      const confirm = await this.$myconfirm("确定删除该数据吗?");
      if (confirm) {
    
    
        let res = await deleteRoleApi({
    
     roleId: row.roleId });
        if (res && res.code == 200) {
    
    
          //信息提示
          this.$message({
    
     type: "success", message: res.msg });
          //刷新列表
          this.getList();
        }
      }
    },
    //编辑按钮
    editBtn(row) {
    
    
      //设置弹框属性
      this.dialog.title = "编辑角色";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      this.addModel.type = "1";
      //把当前编辑的数据放到表单数据里面
      this.$objCoppy(row, this.addModel);
    },
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增角色";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      this.addModel.type = "0";
    },
    //重置按钮
    resetBtn() {
    
    
      //表单清空
      this.listParm.roleName = "";
      this.getList();
    },
    //搜索按钮
    searchBtn() {
    
    
      this.getList();
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第35讲 分配权限保存接口开发
1、SysRoleMenuMapper添加saveRoleMenu()方法
package com.itmk.web.sys_role_menu.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
    
    
    boolean saveRoleMenu(@Param("roleId") Long roleId, @Param("menuIds")List<Long> menuIds);
}

映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itmk.web.sys_role_menu.mapper.SysRoleMenuMapper">
    <insert id="saveRoleMenu">
        insert into sys_role_menu(role_id,menu_id) values
        <foreach collection="menuIds" item="item" index="index" separator=",">
            (#{
    
    roleId},#{
    
    item})
        </foreach>
    </insert>
</mapper>
2、SysRoleMenuService接口添加saveRoleMenu方法
package com.itmk.web.sys_role_menu.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;

import java.util.List;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SysRoleMenuService extends IService<SysRoleMenu> {
    
    
    void saveRoleMenu(Long roleId, List<Long> menuIds);
}

实现类

package com.itmk.web.sys_role_menu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.sys_role_menu.entity.SysRoleMenu;
import com.itmk.web.sys_role_menu.mapper.SysRoleMenuMapper;
import com.itmk.web.sys_role_menu.service.SysRoleMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Service
public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
    
    
    @Override
    @Transactional
    public void saveRoleMenu(Long roleId, List<Long> menuIds) {
    
    
        //先删除原来的,再插入
        QueryWrapper<SysRoleMenu> query = new QueryWrapper<>();
        query.lambda().eq(SysRoleMenu::getRoleId,roleId);
        this.baseMapper.delete(query);
        //插入新的
        this.baseMapper.saveRoleMenu(roleId,menuIds);
    }
}

3、SysRoleController控制器添加saveRoleMenu()方法
package com.itmk.web.sys_role.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.sys_role.entity.*;
import com.itmk.web.sys_role.service.SysRoleService;
import com.itmk.web.sys_role_menu.service.SysRoleMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@RestController
@RequestMapping("/api/role")
public class SysRoleController {
    
    
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    //新增
    @PostMapping
    public ResultVo addRole(@RequestBody SysRole role){
    
    
        role.setCreateTime(new Date());
        boolean save = sysRoleService.save(role);
        if(save){
    
    
            return ResultUtils.success("新增角色成功!");
        }
        return ResultUtils.error("新增角色失败!");
    }

    //编辑
    @PutMapping
    public ResultVo editRole(@RequestBody SysRole role){
    
    
        role.setUpdateTime(new Date());
        boolean save = sysRoleService.updateById(role);
        if(save){
    
    
            return ResultUtils.success("编辑角色成功!");
        }
        return ResultUtils.error("编辑角色失败!");
    }

    //删除
    @DeleteMapping("/{roleId}")
    public ResultVo deleteRole(@PathVariable("roleId") Long roleId){
    
    
        boolean remove = sysRoleService.removeById(roleId);
        if(remove){
    
    
            return ResultUtils.success("删除成功");
        }
        return ResultUtils.error("删除失败!");
    }

    //角色列表
    @GetMapping("/list")
    public ResultVo getList(RoleParm parm){
    
    
        IPage<SysRole> list = sysRoleService.list(parm);
        return ResultUtils.success("查询成功",list);
    }

    //分配权限回显
    @GetMapping("/getAssingShow")
    public ResultVo getAssingShow(AssignParm parm){
    
    
        AssignVo show = sysRoleService.getAssignShow(parm);
        return ResultUtils.success("查询成功",show);
    }

    //分配权限保存
    @PostMapping("/saveRoleMenu")
    public ResultVo saveRoleMenu(@RequestBody SaveRoleParm parm){
    
    
        sysRoleMenuService.saveRoleMenu(parm.getRoleId(),parm.getList());
        return ResultUtils.success("分配权限成功!");
    }
}

第36讲 角色分配权限保存对接
1、api/role.js添加saveAssignApi()方法
import http from '@/utils/http'
//获取列表
export const getListApi = async(parm) =>{
    
    
    return await http.get("/api/role/list",parm)
}
//新增
export const addRoleApi = async(parm) =>{
    
    
     return await http.post("/api/role",parm)
}
//编辑
export const editRoleApi = async(parm) =>{
    
    
    return await http.put("/api/role",parm)
}
//删除
export const deleteRoleApi = async(parm) =>{
    
    
    return await http.delete("/api/role",parm)
}
//获取分配权限的数据
export const assignRoleApi = async(parm)=>{
    
    
    return await http.get("/api/role/getAssingShow",parm)
}
//分配权限保存
export const saveAssignApi = async(parm)=>{
    
    
    return await http.post("/api/role/saveRoleMenu",parm)
}
2、sysRoleList.vue页面
<template>
  <el-main>
    <!-- 搜索栏 -->
    <el-form :model="listParm" ref="searchRef" :inline="true" size="small">
      <el-form-item>
        <el-input
          placeholder="请输入角色名称"
          v-model="listParm.roleName"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBrn">搜索</el-button>
        <el-button icon="el-icon-close" style="color: #ff7670" @click="resetBtn"
          >重置</el-button
        >
        <el-button type="primary" @click="addBtn" icon="el-icon-plus"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格 -->
    <el-table :height="tableHeight" :data="tableData" border stripe>
      <el-table-column prop="roleName" label="角色名称"></el-table-column>
      <el-table-column prop="remark" label="备注"></el-table-column>
      <el-table-column label="操作" align="center" width="300">
        <template slot-scope="scope">
          <el-button
            type="primary"
            icon="el-icon-edit"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            type="primary"
            icon="el-icon-edit"
            size="small"
            @click="assignBtn(scope.row)"
            >分配权限</el-button
          >
          <el-button
            type="danger"
            icon="el-icon-delete"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="listParm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="listParm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="listParm.total"
      background
    >
    </el-pagination>
    <!-- 新增、编辑 -->
    <sys-dialog
      :title="dialog.title"
      :visible="dialog.visible"
      :height="dialog.height"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addRef"
          :rules="rules"
          label-width="80px"
          size="small"
        >
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item prop="roleName" label="角色名称">
                <el-input v-model="addModel.roleName"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12" :offset="0">
              <el-form-item prop="roleType" label="角色类型">
                <el-select v-model="addModel.roleType" placeholder="请选择">
                  <el-option
                    v-for="item in options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12" :offset="0">
              <el-form-item label="角色备注">
                <el-input v-model="addModel.remark"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
    </sys-dialog>
    <!-- 分配权限弹框 -->
    <sys-dialog
      :title="assignDialog.title"
      :width="assignDialog.width"
      :height="assignDialog.height"
      :visible="assignDialog.visible"
      @onClose="assignClose"
      @onConfirm="assignConfrim"
    >
      <div slot="content">
        <el-tree
          ref="assignTree"
          :data="assignTreeData"
          node-key="menuId"
          :props="defaultProps"
          empty-text="暂无数据"
          :show-checkbox="true"
          default-expand-all
          :default-checked-keys="assignTreeChecked"
        ></el-tree>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
    
  getListApi,
  addRoleApi,
  editRoleApi,
  deleteRoleApi,
  assignRoleApi,
  saveAssignApi,
} from "@/api/role";
import {
    
     getUserId } from "@/utils/auth";
export default {
    
    
  // 注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      defaultProps: {
    
    
        children: "children",
        label: "title",
      },
      //角色id
      roleId: "",
      //树数据
      assignTreeData: [],
      //角色原来的权限
      assignTreeChecked: [],
      //定义弹框属性
      assignDialog: {
    
    
        title: "",
        height: 450,
        width: 300,
        visible: false,
      },
      //角色类型
      options: [
        {
    
    
          value: "1",
          label: "系统用户",
        },
        {
    
    
          value: "2",
          label: "学生",
        },
        {
    
    
          value: "3",
          label: "教师",
        },
      ],
      //表单验证
      rules: {
    
    
        roleName: [
          {
    
    
            required: true,
            message: "请输入角色名称",
            trigger: "blur",
          },
        ],
        roleType: [
          {
    
    
            required: true,
            message: "请选择角色类型",
            trigger: "blur",
          },
        ],
      },
      //新增表单绑定的数据
      addModel: {
    
    
        type: "", //0:新增 1:编辑
        roleId: "",
        roleName: "",
        roleType: "",
        remark: "",
      },
      //弹框属性
      dialog: {
    
    
        height: 150,
        visible: false,
        title: "",
      },
      //表格高度
      tableHeight: 0,
      //搜索栏数据
      listParm: {
    
    
        roleName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      //表格数据
      tableData: [],
    };
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  created() {
    
    
    this.getList();
  },
  methods: {
    
    
    //分配权限弹框确定
    async assignConfrim() {
    
    
      //  this.$refs.assignTree.getCheckedKeys()
      // console.log(this.$refs.assignTree.getCheckedKeys())
      // console.log(this.$refs.assignTree.getHalfCheckedKeys())
      // this.assignDialog.visible = false;
      let ids = this.$refs.assignTree
        .getCheckedKeys()
        .concat(this.$refs.assignTree.getHalfCheckedKeys());
      console.log(ids);
      let parm = {
    
    
        roleId: this.roleId,
        list: ids,
      };
      let res = await saveAssignApi(parm);
      if (res && res.code == 200) {
    
    
        this.$message.success(res.msg);
        this.assignDialog.visible = false;
      }
    },
    //分配权限弹框关闭
    assignClose() {
    
    
      this.assignDialog.visible = false;
    },
    //分配权限按钮
    async assignBtn(row) {
    
    
      console.log(row);
      this.roleId = row.roleId;
      //设置弹框的属性
      this.assignDialog.title = "为【" + row.roleName + "】分配权限";
      this.assignDialog.visible = true;
      //获取权限树数据
      let parm = {
    
    
        userId: getUserId(),
        roleId: row.roleId,
      };
      const res = await assignRoleApi(parm);
      if (res && res.code == 200) {
    
    
        //设置树的数据
        console.log(res);
        this.assignTreeData = res.data.menuList;
        this.assignTreeChecked = res.data.checkList;
      }
      //过滤回显的数据
      if (this.assignTreeChecked.length > 0) {
    
    
        let newArr = [];
        this.assignTreeChecked.forEach((item) => {
    
    
          this.checked(item, this.assignTreeData, newArr);
        });
        this.assignTreeChecked = newArr;
      }
    },
    //过滤工具
    checked(id, data, newArr) {
    
    
      data.forEach((item) => {
    
    
        if (item.menuId == id) {
    
    
          if (item.children && item.children.length == 0) {
    
    
            newArr.push(item.menuId);
          }
        } else {
    
    
          //有下级的时候,继续查找
          if (item.children && item.children.length != 0) {
    
    
            //递归算法:自己调用自己
            this.checked(id, item.children, newArr);
          }
        }
      });
    },
    //重置按钮
    resetBtn() {
    
    
      this.listParm.roleName = "";
      this.getList();
    },
    //搜索按钮
    searchBrn() {
    
    
      this.getList();
    },
    //新增按钮
    addBtn() {
    
    
      //设置弹框属性
      this.dialog.title = "新增角色";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      //设置为新增
      this.addModel.type = "0";
    },
    //弹框确定
    onConfirm() {
    
    
      //表单验证
      this.$refs.addRef.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addRoleApi(this.addModel);
          } else {
    
    
            res = await editRoleApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message({
    
     type: "success", message: res.msg });
            //刷新列表
            this.getList();
          }
        }
      });
      this.dialog.visible = false;
    },
    //弹框关闭
    onClose() {
    
    
      this.dialog.visible = false;
    },
    //获取列表
    async getList() {
    
    
      let res = await getListApi(this.listParm);
      if (res && res.code == 200) {
    
    
        //设置表格数据
        console.log(res);
        this.tableData = res.data.records;
        this.listParm.total = res.data.total;
      }
    },
    //页数改变时触发
    currentChange(val) {
    
    
      this.listParm.currentPage = val;
      this.getList();
    },
    //页容量改变时触发
    sizeChange(val) {
    
    
      this.listParm.pageSize = val;
      this.getList();
    },
    //删除按钮
    async deleteBtn(row) {
    
    
      const confirm = await this.$myconfirm("确定删除该数据吗?");
      if (confirm) {
    
    
        let res = await deleteRoleApi({
    
     roleId: row.roleId });
        if (res && res.code == 200) {
    
    
          //信息提示
          this.$message({
    
     type: "success", message: res.msg });
          //刷新列表
          this.getList();
        }
      }
    },
    //编辑按钮
    editBtn(row) {
    
    
      //设置弹框属性
      this.dialog.title = "编辑角色";
      this.dialog.visible = true;
      //清空表单
      this.$resetForm("addRef", this.addModel);
      //设置为编辑
      this.addModel.type = "1";
      //把当前要编辑的数据设置到表单数据域
      this.$objCoppy(row, this.addModel);
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第37讲 学院管理接口开发
1、数据库脚本
drop table if exists school_collage;

/*==============================================================*/
/* Table: school_collage                                        */
/*==============================================================*/
create table school_collage
(
   collage_id           int not null auto_increment comment '学院id',
   collage_name         varchar(64) comment '学院名称',
   order_num            int comment '序号',
   create_time          datetime comment '创建时间',
   primary key (collage_id)
);

2、新建实体类
package com.itmk.web.school_collage.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Date;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
@TableName("school_collage")
public class SchoolCollage {
    
    
    @TableId(type = IdType.AUTO)
    private Long collageId;
    private String collageName;
    private Integer orderNum;
    private Date createTime;
}

package com.itmk.web.school_collage.entity;

import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
public class ListParm {
    
    
    private Long currentPage;
    private Long pageSize;
    private String collageName;
}

3、mapper接口
package com.itmk.web.school_collage.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itmk.web.school_collage.entity.SchoolCollage;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SchoolCollageMapper extends BaseMapper<SchoolCollage> {
    
    
}

映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itmk.web.school_collage.mapper.SchoolCollageMapper">

</mapper>
4、service接口
package com.itmk.web.school_collage.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.school_collage.entity.SchoolCollage;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SchoolCollageService extends IService<SchoolCollage> {
    
    
}

实现类

package com.itmk.web.school_collage.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.school_collage.entity.SchoolCollage;
import com.itmk.web.school_collage.mapper.SchoolCollageMapper;
import com.itmk.web.school_collage.service.SchoolCollageService;
import org.springframework.stereotype.Service;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Service
public class SchoolCollageServiceImpl extends ServiceImpl<SchoolCollageMapper, SchoolCollage> implements SchoolCollageService {
    
    
}

5、控制器
package com.itmk.web.school_collage.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.school_collage.entity.ListParm;
import com.itmk.web.school_collage.entity.SchoolCollage;
import com.itmk.web.school_collage.service.SchoolCollageService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@RestController
@RequestMapping("/api/collage")
public class SchoolCollageController {
    
    
    @Autowired
    private SchoolCollageService schoolCollageService;

    //新增
    @PostMapping
    public ResultVo add(@RequestBody SchoolCollage schoolCollage){
    
    
        //设置创建时间
        schoolCollage.setCreateTime(new Date());
        boolean save = schoolCollageService.save(schoolCollage);
        if(save){
    
    
            return ResultUtils.success("新增学院成功!");
        }
        return ResultUtils.error("新增学院失败!");
    }

    //编辑
    @PutMapping
    public ResultVo edit(@RequestBody SchoolCollage schoolCollage){
    
    

        boolean save = schoolCollageService.updateById(schoolCollage);
        if(save){
    
    
            return ResultUtils.success("编辑学院成功!");
        }
        return ResultUtils.error("编辑学院失败!");
    }

    //删除
    @DeleteMapping("/{collageId}")
    public ResultVo delete(@PathVariable("collageId") Long collageId){
    
    
        boolean b = schoolCollageService.removeById(collageId);
        if(b){
    
    
            return ResultUtils.success("删除学院成功!");
        }
        return ResultUtils.error("删除学院失败!");

    }

    //列表
    @GetMapping("/list")
    public ResultVo getList(ListParm listParm){
    
    
        //构造查询条件
        QueryWrapper<SchoolCollage> query = new QueryWrapper<>();
        if(StringUtils.isNotEmpty(listParm.getCollageName())){
    
    
            query.lambda().like(SchoolCollage::getCollageName,listParm.getCollageName());
        }
        //构造分页对象
        IPage<SchoolCollage> page = new Page<>(listParm.getCurrentPage(),listParm.getPageSize());
        //查询
        IPage<SchoolCollage> list = schoolCollageService.page(page, query);
        return ResultUtils.success("查询成功",list);
    }

}
第38讲 新增学院接口对接
1、api下新建college.js
import http from "@/utils/http";
//新增
export const addApi = async parm => {
    
    
  return await http.post("/api/collage", parm);
};

2、sysCollogeList.vue页面
<template>
  <el-main>
    <!-- 搜索栏 -->
    <el-form :model="searchForm" label-width="80px" :inline="true" size="small">
      <el-form-item>
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院的名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageName" label="学院名称">
            <el-input
              v-model="addModel.collageName"
              placeholder="请输入学院名称"
            ></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input
              v-model="addModel.orderNum"
              placeholder="请输入序号"
            ></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import {
    
     addApi } from "@/api/college.js";
import SysDialog from "@/components/dialog/SysDialog.vue";
export default {
    
    
  //注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表单验证规则
      rules: {
    
    
        collageName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请输入学院名称",
          },
        ],
      },
      //表单绑定对象
      addModel: {
    
    
        type: "", //区分是新增还是编辑 0:新增 1:编辑
        collageId: "",
        collageName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 150,
        width: 650,
        visible: false,
      },
      //搜索表单
      searchForm: {
    
    
        collageName: "",
        currentPage: 1,
        pageSize: 10,
      },
    };
  },
  methods: {
    
    
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      //表单验证
      this.$refs.addForm.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message.success(res.msg);
            this.addDialog.visible = false;
          }
        }
      });
    },
    //新增
    addBtn() {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //设置弹框属性
      this.addDialog.title = "新增学院";
      this.addDialog.visible = true;
      //设置编辑的属性
      this.addModel.type = "0"; //新增
    },
    //重置按钮
    resetBtn() {
    
    },
    //搜索按钮
    searchBtn() {
    
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第39讲 学院列表页面对接
1、api/college.js添加listApi()方法
import http from "@/utils/http";
//新增
export const addApi = async (parm) => {
    
    
  return await http.post("/api/collage", parm);
};
//列表
export const listApi = async(parm)=>{
    
    
  return await http.get("/api/collage/list",parm)
}

2、collogeList.vue页面
<template>
  <el-main>
    <!-- 搜索栏 -->
    <el-form :model="searchForm" label-width="80px" :inline="true" size="mini">
      <el-form-item>
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院的名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 列表 -->
    <el-table :height="tableHeight" :data="tableList" border stripe>
      <el-table-column prop="collageName" label="学院名称"></el-table-column>
      <el-table-column prop="orderNum" label="序号"></el-table-column>
      <el-table-column label="操作" align="center" width="250">
        <template slot-scope="scope">
          <el-button
            type="primary"
            icon="el-icon-edit"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            type="danger"
            icon="el-icon-delete"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="searchForm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="searchForm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="searchForm.total"
      background
    >
    </el-pagination>

    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageName" label="学院名称">
            <el-input
              v-model="addModel.collageName"
              placeholder="请输入学院名称"
            ></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input
              v-model="addModel.orderNum"
              placeholder="请输入序号"
            ></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import {
    
     addApi, listApi } from "@/api/college.js";
import SysDialog from "@/components/dialog/SysDialog.vue";
export default {
    
    
  //注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表格高度
      tableHeight: 0,
      //表单验证规则
      rules: {
    
    
        collageName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请输入学院名称",
          },
        ],
      },
      //表单绑定对象
      addModel: {
    
    
        type: "", //区分是新增还是编辑 0:新增 1:编辑
        collageId: "",
        collageName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 150,
        width: 650,
        visible: false,
      },
      //搜索表单
      searchForm: {
    
    
        collageName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      tableList: [],
    };
  },
  created() {
    
    
    this.getList();
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  methods: {
    
    
    //页数改变时触发
    currentChange(page) {
    
    
      this.searchForm.currentPage = page;
      this.getList();
    },
    //页容量改变时触发
    sizeChange(size) {
    
    
      this.searchForm.pageSize = size;
      this.getList();
    },
    //删除按钮
    deleteBtn(row) {
    
    },
    //编辑按钮
    editBtn(row) {
    
    },
    //获取列表
    async getList() {
    
    
      let res = await listApi(this.searchForm);
      if (res && res.code == 200) {
    
    
        console.log(res);
        //设置表格数据
        this.tableList = res.data.records;
        //总条数
        this.searchForm.total = res.data.total;
      }
    },
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      //表单验证
      this.$refs.addForm.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message.success(res.msg);
            this.addDialog.visible = false;
          }
        }
      });
    },
    //新增
    addBtn() {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //设置弹框属性
      this.addDialog.title = "新增学院";
      this.addDialog.visible = true;
      //设置编辑的属性
      this.addModel.type = "0"; //新增
    },
    //重置按钮
    resetBtn() {
    
    
      this.searchForm.currentPage = 1;
      this.searchForm.collageName = "";
      this.getList();
    },
    //搜索按钮
    searchBtn() {
    
    
      this.getList();
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第40讲 学院编辑、删除接口对接
1、college.js添加editApi()和deleteApi()方法
import http from "@/utils/http";
//新增
export const addApi = async (parm) => {
    
    
  return await http.post("/api/collage", parm);
};
//列表
export const listApi = async(parm)=>{
    
    
  return await http.get("/api/collage/list",parm)
}
//编辑
export const editApi = async(parm)=>{
    
    
  return await http.put("/api/collage", parm)
}
//删除
export const deleteApi = async(parm)=>{
    
    
  return await http.delete("/api/collage", parm)
}
2、collogeList.vue页面
<template>
  <el-main>
    <!-- 搜索栏 -->
    <el-form :model="searchForm" label-width="80px" :inline="true" size="mini">
      <el-form-item>
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院的名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 列表 -->
    <el-table :height="tableHeight" :data="tableList" border stripe>
      <el-table-column prop="collageName" label="学院名称"></el-table-column>
      <el-table-column prop="orderNum" label="序号"></el-table-column>
      <el-table-column label="操作" align="center" width="250">
        <template slot-scope="scope">
          <el-button
            type="primary"
            icon="el-icon-edit"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            type="danger"
            icon="el-icon-delete"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="searchForm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="searchForm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="searchForm.total"
      background
    >
    </el-pagination>

    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageName" label="学院名称">
            <el-input
              v-model="addModel.collageName"
              placeholder="请输入学院名称"
            ></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input
              v-model="addModel.orderNum"
              placeholder="请输入序号"
            ></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import {
    
     addApi, listApi, editApi, deleteApi } from "@/api/college.js";
import SysDialog from "@/components/dialog/SysDialog.vue";
export default {
    
    
  //注册组件
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表格高度
      tableHeight: 0,
      //表单验证规则
      rules: {
    
    
        collageName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请输入学院名称",
          },
        ],
      },
      //表单绑定对象
      addModel: {
    
    
        type: "", //区分是新增还是编辑 0:新增 1:编辑
        collageId: "",
        collageName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 150,
        width: 650,
        visible: false,
      },
      //搜索表单
      searchForm: {
    
    
        collageName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      tableList: [],
    };
  },
  created() {
    
    
    this.getList();
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  methods: {
    
    
    //页数改变时触发
    currentChange(page) {
    
    
      this.searchForm.currentPage = page;
      this.getList();
    },
    //页容量改变时触发
    sizeChange(size) {
    
    
      this.searchForm.pageSize = size;
      this.getList();
    },
    //删除按钮
    async deleteBtn(row) {
    
    
      //信息提示
      let confirm = await this.$myconfirm("确定删除该数据吗?");
      if (confirm) {
    
    
        let res = await deleteApi({
    
    
          collageId: row.collageId,
        });
        if (res && res.code == 200) {
    
    
          //信息提示
          this.$message.success(res.msg);
          //刷新列表
          this.getList();
        }
      }
    },
    //编辑按钮
    editBtn(row) {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //把要编辑的数据设置到表单绑定的对象
      this.$objCoppy(row, this.addModel);
      //设置编辑的属性
      this.addModel.type = "1";
      //设置弹框属性
      this.addDialog.title = "编辑学院";
      this.addDialog.visible = true;
    },
    //获取列表
    async getList() {
    
    
      let res = await listApi(this.searchForm);
      if (res && res.code == 200) {
    
    
        console.log(res);
        //设置表格数据
        this.tableList = res.data.records;
        //总条数
        this.searchForm.total = res.data.total;
      }
    },
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      //表单验证
      this.$refs.addForm.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          } else {
    
    
            res = await editApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message.success(res.msg);
            this.addDialog.visible = false;
            //刷新列表
            this.getList();
          }
        }
      });
    },
    //新增
    addBtn() {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //设置弹框属性
      this.addDialog.title = "新增学院";
      this.addDialog.visible = true;
      //设置编辑的属性
      this.addModel.type = "0"; //新增
    },
    //重置按钮
    resetBtn() {
    
    
      this.searchForm.currentPage = 1;
      this.searchForm.collageName = "";
      this.getList();
    },
    //搜索按钮
    searchBtn() {
    
    
      this.getList();
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第41讲 专业管理模块接口开发
1、数据库脚本
drop table if exists school_major;

/*==============================================================*/
/* Table: school_major                                          */
/*==============================================================*/
create table school_major
(
   major_id             int not null auto_increment comment '专业id',
   collage_id           int comment '学院id',
   major_name           varchar(64) comment '专业名称',
   order_num            int comment '序号',
   primary key (major_id)
);
2、新建实体类
package com.itmk.web.school_major.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
@TableName("school_major")
public class SchoolMajor {
    
    
    @TableId(type = IdType.AUTO)
    private Long majorId;
    private Long collageId;
    private String majorName;
    private Integer orderNum;
    //学院名称,不属于专业表,需要排除
    @TableField(exist = false)
    private String collageName;
}

package com.itmk.web.school_major.entity;

import lombok.Data;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Data
public class MajorList {
    
    
    private String majorName;
    private String collageName;
    private Long currentPage;
    private Long pageSize;
}

3、新建mapper层
package com.itmk.web.school_major.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itmk.web.school_major.entity.MajorList;
import com.itmk.web.school_major.entity.SchoolMajor;
import org.apache.ibatis.annotations.Param;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SchoolMajorMapper extends BaseMapper<SchoolMajor> {
    
    
    IPage<SchoolMajor> getList(IPage<SchoolMajor> page, @Param("parm")MajorList majorList);
}

映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.itmk.web.school_major.mapper.SchoolMajorMapper">
    <select id="getList">
        select sm.*,sc.collage_name from school_major as sm
        left join school_collage as sc
        on sm.collage_id = sc.collage_id
        where 1=1
        <if test="parm.majorName !=null and parm.majorName !=''">
            and sm.major_name like concat('%',#{
    
    parm.majorName},'%')
        </if>
        <if test="parm.collageName !=null and parm.collageName !=''">
            and sc.collage_name like concat('%',#{
    
    parm.collageName},'%')
        </if>
    </select>
</mapper>
4、新建service层
package com.itmk.web.school_major.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itmk.web.school_major.entity.MajorList;
import com.itmk.web.school_major.entity.SchoolMajor;


/**
 * @Author java实战基地
 * @Version 3501754007
 */
public interface SchoolMajorService extends IService<SchoolMajor> {
    
    
    IPage<SchoolMajor> getList(MajorList majorList);
}

实现类

package com.itmk.web.school_major.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmk.web.school_major.entity.MajorList;
import com.itmk.web.school_major.entity.SchoolMajor;
import com.itmk.web.school_major.mapper.SchoolMajorMapper;
import com.itmk.web.school_major.service.SchoolMajorService;
import org.springframework.stereotype.Service;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@Service
public class SchoolMajorServiceImpl extends ServiceImpl<SchoolMajorMapper, SchoolMajor> implements SchoolMajorService {
    
    
    @Override
    public IPage<SchoolMajor> getList(MajorList majorList) {
    
    
        //构造分页对象
        IPage<SchoolMajor> page = new Page<>(majorList.getCurrentPage(),majorList.getPageSize());
        return this.baseMapper.getList(page,majorList);
    }
}

5、新建控制器
package com.itmk.web.school_major.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itmk.utils.ResultUtils;
import com.itmk.utils.ResultVo;
import com.itmk.web.school_major.entity.MajorList;
import com.itmk.web.school_major.entity.SchoolMajor;
import com.itmk.web.school_major.service.SchoolMajorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @Author java实战基地
 * @Version 3501754007
 */
@RestController
@RequestMapping("/api/major")
public class SchoolMajorController {
    
    
    @Autowired
    private SchoolMajorService schoolMajorService;

    //新增
    @PostMapping
    public ResultVo add(@RequestBody SchoolMajor schoolMajor){
    
    
        boolean save = schoolMajorService.save(schoolMajor);
        if(save){
    
    
            return ResultUtils.success("新增成功");
        }
        return ResultUtils.error("新增失败!");
    }

    //编辑
    @PutMapping
    public ResultVo edit(@RequestBody SchoolMajor schoolMajor){
    
    
        boolean save = schoolMajorService.updateById(schoolMajor);
        if(save){
    
    
            return ResultUtils.success("编辑成功");
        }
        return ResultUtils.error("编辑失败!");
    }

    //删除
    @DeleteMapping("/{majorId}")
    public ResultVo delete(@PathVariable("majorId") Long majorId){
    
    
        boolean save = schoolMajorService.removeById(majorId);
        if(save){
    
    
            return ResultUtils.success("删除成功");
        }
        return ResultUtils.error("删除失败!");
    }

    //查询列表
    @GetMapping("/list")
    public ResultVo getList(MajorList majorList){
    
    
        IPage<SchoolMajor> list = schoolMajorService.getList(majorList);
        return ResultUtils.success("查询成功",list);
    }

}
第42讲 新增专业页面制作与对接
1、api下新建major.js
import http from "@/utils/http";
//查询学院列表
export const getCollegeListApi = async()=>{
    
    
    return await http.get("/api/major/getCollegeList")
}
//新增
export const addApi = async(parm)=>{
    
    
    return await http.post("/api/major",parm)
}
2、新建majorList.vue页面
<template>
  <el-main>
    <!--搜索栏-->
    <el-form :model="searchForm" :inline="true" size="small">
      <el-form-item label="">
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院名称"
        ></el-input>
      </el-form-item>
      <el-form-item label="">
        <el-input
          v-model="searchForm.majorName"
          placeholder="请输入专业名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageId" label="所属学院">
            <el-select
              style="width: 100%"
              v-model="addModel.collageId"
              placeholder="请选择学院"
            >
              <el-option
                v-for="item in collogeList"
                :key="item.collageId"
                :label="item.collageName"
                :value="item.collageId"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="专业名称" prop="majorName">
            <el-input v-model="addModel.majorName"></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input v-model="addModel.orderNum"></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
     getCollegeListApi, addApi } from "@/api/major.js";
export default {
    
    
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表单验证规则
      rules: {
    
    
        collageId: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择学院",
          },
        ],
        majorName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写专业名称",
          },
        ],
      },
      //新增表单属性
      addModel: {
    
    
        type: "",
        majorId: "",
        collageId: "",
        majorName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 200,
        width: 650,
        visible: false,
      },
      searchForm: {
    
    
        majorName: "",
        collageName: "",
        currentPage: "",
        pageSize: "",
        total: 0,
      },
      collogeList: [],
    };
  },
  methods: {
    
    
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addForm.validate(async(valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          }
          if(res && res.code == 200){
    
    
            //信息提示
            this.$message.success(res.msg)
            //刷新列表
          }
          console.log(this.addModel);
          this.addDialog.visible = false;
        }
      });
    },
    //新增按钮
    async addBtn() {
    
    
      //清空表单
      this.$resetForm('addForm',this.addModel)
      //获取学院列表
      let res = await getCollegeListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.collogeList = res.data;
      }
      //显示弹框
      this.addDialog.title = "新增专业";
      this.addDialog.visible = true;
      this.addModel.type = "0";
    },
    //重置按钮
    resetBtn() {
    
    },
    //搜索按钮
    searchBtn() {
    
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第43讲 专业列表制作与对接
1、api/major.js添加listApi()
import http from "@/utils/http";
//查询学院列表
export const getCollegeListApi = async()=>{
    
    
    return await http.get("/api/major/getCollegeList")
}
//新增
export const addApi = async(parm)=>{
    
    
    return await http.post("/api/major",parm)
}
//列表
export const listApi = async(parm)=>{
    
    
    return await http.get("/api/major/list",parm)
}
2、majorList.vue页面
<template>
  <el-main>
    <!--搜索栏-->
    <el-form :model="searchForm" :inline="true" size="small">
      <el-form-item label="">
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院名称"
        ></el-input>
      </el-form-item>
      <el-form-item label="">
        <el-input
          v-model="searchForm.majorName"
          placeholder="请输入专业名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格数据 -->
    <el-table :height="tableHeight" :data="tableList" border stripe>
      <el-table-column prop="majorName" label="专业名称"></el-table-column>
      <el-table-column prop="collageName" label="所属学院"></el-table-column>
      <el-table-column prop="orderNum" label="序号"></el-table-column>
      <el-table-column label="操作" align="center" width="220">
        <template slot-scope="scope">
          <el-button
            icon="el-icon-edit"
            type="primary"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            icon="el-icon-delete"
            type="danger"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="searchForm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="searchForm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="searchForm.total"
      background
    >
    </el-pagination>

    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageId" label="所属学院">
            <el-select
              style="width: 100%"
              v-model="addModel.collageId"
              placeholder="请选择学院"
            >
              <el-option
                v-for="item in collogeList"
                :key="item.collageId"
                :label="item.collageName"
                :value="item.collageId"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="专业名称" prop="majorName">
            <el-input v-model="addModel.majorName"></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input v-model="addModel.orderNum"></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
     getCollegeListApi, addApi, listApi } from "@/api/major.js";
export default {
    
    
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表格高度
      tableHeight: 0,
      //表单验证规则
      rules: {
    
    
        collageId: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择学院",
          },
        ],
        majorName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写专业名称",
          },
        ],
      },
      //新增表单属性
      addModel: {
    
    
        type: "",
        majorId: "",
        collageId: "",
        majorName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 200,
        width: 650,
        visible: false,
      },
      searchForm: {
    
    
        majorName: "",
        collageName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      collogeList: [],
      //表格数据
      tableList: [],
    };
  },
  created() {
    
    
    this.getList();
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  methods: {
    
    
    //页数改变时触发
    currentChange(page) {
    
    
      this.searchForm.currentPage = page;
      this.getList();
    },
    //页容量改变时触发
    sizeChange(size) {
    
    
      this.searchForm.pageSize = size;
      this.getList();
    },
    //删除按钮
    deleteBtn(row) {
    
    },
    //编辑按钮
    editBtn(row) {
    
    },
    //获取列表数据
    async getList() {
    
    
      let res = await listApi(this.searchForm);
      if (res && res.code == 200) {
    
    
        console.log(res);
        //设置表格数据
        this.tableList = res.data.records;
        //设置分页
        this.searchForm.total = res.data.total;
      }
    },
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addForm.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message.success(res.msg);
            //刷新列表
          }
          console.log(this.addModel);
          this.addDialog.visible = false;
        }
      });
    },
    //新增按钮
    async addBtn() {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //获取学院列表
      let res = await getCollegeListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.collogeList = res.data;
      }
      //显示弹框
      this.addDialog.title = "新增专业";
      this.addDialog.visible = true;
      this.addModel.type = "0";
    },
    //重置按钮
    resetBtn() {
    
    
      this.searchForm.majorName = "";
      this.searchForm.collageName = "";
      this.searchForm.currentPage = 1;
      this.getList();
    },
    //搜索按钮
    searchBtn() {
    
    
      this.getList();
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
第44讲 专业编辑、删除接口对接
1、api/major.js添加editApi()和deleteApi()方法
import http from "@/utils/http";
//查询学院列表
export const getCollegeListApi = async()=>{
    
    
    return await http.get("/api/major/getCollegeList")
}
//新增
export const addApi = async(parm)=>{
    
    
    return await http.post("/api/major",parm)
}
//列表
export const listApi = async(parm)=>{
    
    
    return await http.get("/api/major/list",parm)
}
//编辑
export const editApi = async(parm)=>{
    
    
    return await http.put("/api/major",parm)
}
//删除
export const deleteApi = async(parm)=>{
    
    
    return await http.delete("/api/major",parm)
}
2、majorList.vue页面
<template>
  <el-main>
    <!--搜索栏-->
    <el-form :model="searchForm" :inline="true" size="small">
      <el-form-item label="">
        <el-input
          v-model="searchForm.collageName"
          placeholder="请输入学院名称"
        ></el-input>
      </el-form-item>
      <el-form-item label="">
        <el-input
          v-model="searchForm.majorName"
          placeholder="请输入专业名称"
        ></el-input>
      </el-form-item>
      <el-form-item>
        <el-button icon="el-icon-search" @click="searchBtn">搜索</el-button>
        <el-button style="color: #ff7670" icon="el-icon-close" @click="resetBtn"
          >重置</el-button
        >
        <el-button icon="el-icon-plus" type="primary" @click="addBtn"
          >新增</el-button
        >
      </el-form-item>
    </el-form>
    <!-- 表格数据 -->
    <el-table :height="tableHeight" :data="tableList" border stripe>
      <el-table-column prop="majorName" label="专业名称"></el-table-column>
      <el-table-column prop="collageName" label="所属学院"></el-table-column>
      <el-table-column prop="orderNum" label="序号"></el-table-column>
      <el-table-column label="操作" align="center" width="220">
        <template slot-scope="scope">
          <el-button
            icon="el-icon-edit"
            type="primary"
            size="small"
            @click="editBtn(scope.row)"
            >编辑</el-button
          >
          <el-button
            icon="el-icon-delete"
            type="danger"
            size="small"
            @click="deleteBtn(scope.row)"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      @size-change="sizeChange"
      @current-change="currentChange"
      :current-page.sync="searchForm.currentPage"
      :page-sizes="[10, 20, 40, 80, 100]"
      :page-size="searchForm.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="searchForm.total"
      background
    >
    </el-pagination>

    <!-- 新增弹框 -->
    <sys-dialog
      :title="addDialog.title"
      :height="addDialog.height"
      :width="addDialog.width"
      :visible="addDialog.visible"
      @onClose="onClose"
      @onConfirm="onConfirm"
    >
      <div slot="content">
        <el-form
          :model="addModel"
          ref="addForm"
          :rules="rules"
          label-width="80px"
          :inline="false"
          size="small"
        >
          <el-form-item prop="collageId" label="所属学院">
            <el-select
              style="width: 100%"
              v-model="addModel.collageId"
              placeholder="请选择学院"
            >
              <el-option
                v-for="item in collogeList"
                :key="item.collageId"
                :label="item.collageName"
                :value="item.collageId"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="专业名称" prop="majorName">
            <el-input v-model="addModel.majorName"></el-input>
          </el-form-item>
          <el-form-item label="序号">
            <el-input v-model="addModel.orderNum"></el-input>
          </el-form-item>
        </el-form>
      </div>
    </sys-dialog>
  </el-main>
</template>

<script>
import SysDialog from "@/components/dialog/SysDialog.vue";
import {
    
    
  getCollegeListApi,
  addApi,
  listApi,
  editApi,
  deleteApi,
} from "@/api/major.js";
export default {
    
    
  components: {
    
    
    SysDialog,
  },
  data() {
    
    
    return {
    
    
      //表格高度
      tableHeight: 0,
      //表单验证规则
      rules: {
    
    
        collageId: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请选择学院",
          },
        ],
        majorName: [
          {
    
    
            trigger: "blur",
            required: true,
            message: "请填写专业名称",
          },
        ],
      },
      //新增表单属性
      addModel: {
    
    
        type: "",
        majorId: "",
        collageId: "",
        majorName: "",
        orderNum: "",
      },
      //弹框属性
      addDialog: {
    
    
        title: "",
        height: 200,
        width: 650,
        visible: false,
      },
      searchForm: {
    
    
        majorName: "",
        collageName: "",
        currentPage: 1,
        pageSize: 10,
        total: 0,
      },
      collogeList: [],
      //表格数据
      tableList: [],
    };
  },
  created() {
    
    
    this.getList();
  },
  mounted() {
    
    
    this.$nextTick(() => {
    
    
      this.tableHeight = window.innerHeight - 220;
    });
  },
  methods: {
    
    
    //页数改变时触发
    currentChange(page) {
    
    
      this.searchForm.currentPage = page;
      this.getList();
    },
    //页容量改变时触发
    sizeChange(size) {
    
    
      this.searchForm.pageSize = size;
      this.getList();
    },
    //删除按钮
    async deleteBtn(row) {
    
    
      //信息提示
      const confirm = await this.$myconfirm("确定删除该数据吗?");
      if (confirm) {
    
    
        let res = await deleteApi({
    
    
          majorId: row.majorId,
        });
        if (res && res.code == 200) {
    
    
          //信息提示
          this.$message.success(res.msg);
          this.getList();
        }
      }
    },
    //编辑按钮
    async editBtn(row) {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //获取学院列表
      let res = await getCollegeListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.collogeList = res.data;
      }
      //把要编辑的数据设置到表单绑定的对象里面
      this.$objCoppy(row, this.addModel);
      //显示弹框
      this.addDialog.title = "编辑专业";
      this.addDialog.visible = true;
      this.addModel.type = "1";
    },
    //获取列表数据
    async getList() {
    
    
      let res = await listApi(this.searchForm);
      if (res && res.code == 200) {
    
    
        console.log(res);
        //设置表格数据
        this.tableList = res.data.records;
        //设置分页
        this.searchForm.total = res.data.total;
      }
    },
    //弹框关闭
    onClose() {
    
    
      this.addDialog.visible = false;
    },
    //弹框确定
    onConfirm() {
    
    
      this.$refs.addForm.validate(async (valid) => {
    
    
        if (valid) {
    
    
          let res = null;
          if (this.addModel.type == "0") {
    
    
            res = await addApi(this.addModel);
          } else {
    
    
            res = await editApi(this.addModel);
          }
          if (res && res.code == 200) {
    
    
            //信息提示
            this.$message.success(res.msg);
            //刷新列表
            this.getList();
          }
          console.log(this.addModel);
          this.addDialog.visible = false;
        }
      });
    },
    //新增按钮
    async addBtn() {
    
    
      //清空表单
      this.$resetForm("addForm", this.addModel);
      //获取学院列表
      let res = await getCollegeListApi();
      if (res && res.code == 200) {
    
    
        console.log(res);
        this.collogeList = res.data;
      }
      //显示弹框
      this.addDialog.title = "新增专业";
      this.addDialog.visible = true;
      this.addModel.type 

在这里插入图片描述

点击获取

完整源代码+万字文档