电商后台管理项目d01

1、项目技术栈
  • Vue
  • Vue-Router
  • Element-UI
  • Axios
  • Echarts
2、项目初始化
  1. 安装Vue脚手架
  2. 通过脚手架创建项目(手动配置)
  3. 路由配置
  4. 配置Element-UI,安装[babel-plugin-component],实现按需导入
  5. 配置Axios
  6. 实现Axios的发呢改装
3、Element-UI的按需引入。

在目录src下新建一个util文件夹,在util文件中新建一个elementUI文件。在main.js文件中导入。

import './util/elemt'

elementUI中,实现按需导入。

import Vue from 'vue';
import {
  Pagination,
  Dialog,
  Autocomplete,
  Dropdown,
  DropdownMenu,
  DropdownItem,
  Menu,
  Submenu,
  MenuItem,
  MenuItemGroup,
  Input,
  InputNumber,
  Radio,
  RadioGroup,
  RadioButton,
  Checkbox,
  CheckboxButton,
  CheckboxGroup,
  Switch,
  Select,
  Option,
  OptionGroup,
  Button,
  ButtonGroup,
  Table,
  TableColumn,
  DatePicker,
  TimeSelect,
  TimePicker,
  Popover,
  Tooltip,
  Breadcrumb,
  BreadcrumbItem,
  Form,
  FormItem,
  Tabs,
  TabPane,
  Tag,
  Tree,
  Alert,
  Slider,
  Icon,
  Row,
  Col,
  Upload,
  Progress,
  Spinner,
  Badge,
  Card,
  Rate,
  Steps,
  Step,
  Carousel,
  CarouselItem,
  Collapse,
  CollapseItem,
  Cascader,
  ColorPicker,
  Transfer,
  Container,
  Header,
  Aside,
  Main,
  Footer,
  Timeline,
  TimelineItem,
  Link,
  Divider,
  Image,
  Calendar,
  Backtop,
  PageHeader,
  CascaderPanel,
  Loading,
  MessageBox,
  Message,
  Notification
} from 'element-ui';

Vue.use(Pagination);
Vue.use(Dialog);
Vue.use(Autocomplete);
Vue.use(Dropdown);
Vue.use(DropdownMenu);
Vue.use(DropdownItem);
Vue.use(Menu);
Vue.use(Submenu);
Vue.use(MenuItem);
Vue.use(MenuItemGroup);
Vue.use(Input);
Vue.use(InputNumber);
Vue.use(Radio);
Vue.use(RadioGroup);
Vue.use(RadioButton);
Vue.use(Checkbox);
Vue.use(CheckboxButton);
Vue.use(CheckboxGroup);
Vue.use(Switch);
Vue.use(Select);
Vue.use(Option);
Vue.use(OptionGroup);
Vue.use(Button);
Vue.use(ButtonGroup);
Vue.use(Table);
Vue.use(TableColumn);
Vue.use(DatePicker);
Vue.use(TimeSelect);
Vue.use(TimePicker);
Vue.use(Popover);
Vue.use(Tooltip);
Vue.use(Breadcrumb);
Vue.use(BreadcrumbItem);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Tabs);
Vue.use(TabPane);
Vue.use(Tag);
Vue.use(Tree);
Vue.use(Alert);
Vue.use(Slider);
Vue.use(Icon);
Vue.use(Row);
Vue.use(Col);
Vue.use(Upload);
Vue.use(Progress);
Vue.use(Spinner);
Vue.use(Badge);
Vue.use(Card);
Vue.use(Rate);
Vue.use(Steps);
Vue.use(Step);
Vue.use(Carousel);
Vue.use(CarouselItem);
Vue.use(Collapse);
Vue.use(CollapseItem);
Vue.use(Cascader);
Vue.use(ColorPicker);
Vue.use(Transfer);
Vue.use(Container);
Vue.use(Header);
Vue.use(Aside);
Vue.use(Main);
Vue.use(Footer);
Vue.use(Timeline);
Vue.use(TimelineItem);
Vue.use(Link);
Vue.use(Divider);
Vue.use(Image);
Vue.use(Calendar);
Vue.use(Backtop);
Vue.use(PageHeader);
Vue.use(CascaderPanel);
Vue.use(Loading.directive);

Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;
Vue.prototype.$notify = Notification;
Vue.prototype.$message = Message;
4、路由配置:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
  routes: [{
      path: '/',
      redirect: '/login',
    },
     {
      path: '/login',
      name: 'login',
      component: () => import( /* webpackChunkName: "login_home-welcome" */ './views/Login.vue')
    },]
    },
  ]
})

在确保路由正确的情况下,也要对路由实现优化以及路由重定向。

5、Axios的封装

第一步:封装基本路径

首先:实现基准路径的简单封装。在utle文件中新建index.js文件。在index.js文件中。

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(axios)
// 设置请求基准路径
const Serve = axios.create({
    baseURL: 'https://www.liulongbin.top:8888/api/private/v1',
    timeout: 5000,
})
export default Serve

第二步:封装API

src中新建API文件夹,在API文件中新建Login.js文件,文件名称尽量与组件或页面名称对应。方便日后的维护。

import request from '../util/index'
export default {
    login(form) {
        return request({
            url: '/login',
            method: "post",
            data: form,
        })
    }
}

导入封装好的基准路径,在这里写好请求的路径以及方式。

第三步:使用

Login.vue页面中导入封装好对应的API文件

import LoginApi from '../api/Login'

在数据请求时:

 login() {
      this.$refs.loginRef.validate(async (valid) => {
        if (!valid) return;
        let { data: res } = await LoginApi.login(this.loginForm)
        if (res.meta.status !== 200) {
          this.$message.error(res.meta.msg);
        }
        sessionStorage.token = res.data.token;
        this.$router.push("/home");
        this.$message.success(res.meta.msg);
      });
    },

做好这些,基本可以开始了…

6、实现登录功能

分析:

​ 输入账号和密码,发送数据,发送时携带token值,对token值进行保存,在后续的请求中用户会通过token值确定用户账号身份。从而实现数据的请求。

**步骤1、**在view中新建Login页面。

**步骤2、**在Login页面中:

<template>
  <div id="login">
    <div class="login-wraper">
      <div class="img"><img src="../assets/logo.png" alt="" /></div>
      <div class="from">
        <el-form
          :model="loginForm"
          :rules="loginRules"
          ref="loginRef"
          class="demo-ruleForm"
        >
          <el-form-item prop="username">
            <el-input
              prefix-icon="el-icon-user-solid"
              v-model="loginForm.username"
            ></el-input>
          </el-form-item>
          <el-form-item prop="password">
            <el-input
              type="password"
              prefix-icon="el-icon-lock"
              v-model="loginForm.password"
            ></el-input>
          </el-form-item>
          <el-form-item class="but">
            <el-button type="primary" @click="login">登录</el-button>
            <el-button type="info" @click="remove">重置</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>
<script>
import LoginApi from '../api/Login'
export default {
  data() {
    return {
      loginForm: {
        username: "",
        password: "",
      },
      loginRules: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
        ],
        password: [
          { required: true, message: "请输入用户密码", trigger: "blur" },
          {
            min: 3,
            max: 10,
            message: "密码长度在3到10个字符",
            trigger: "blur",
          },
        ],
      },
    };
  },
  methods: {
    login() {
      this.$refs.loginRef.validate(async (valid) => {
        if (!valid) return;
        let { data: res } = await LoginApi.login(this.loginForm)
        if (res.meta.status !== 200) {
          this.$message.error(res.meta.msg);
        }
        sessionStorage.token = res.data.token;
        this.$router.push("/home");
        this.$message.success(res.meta.msg);
      });
    },
    remove() {
      this.$refs.loginRef.resetFields();
    },
  },
};
</script>
<style lang='scss' scoped>
#login {
  width: 100%;
  height: 100%;
  background-image: url("../assets/1.jpg");
  background-size: 100%;
  .login-wraper {
    width: 400px;
    height: 300px;
    background-color: rgba(0, 0, 0, 0.4);
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(50%, -50%);
    border-radius: 10px;
    box-shadow: 0px 0px 10px 0px #cccc;
    .img {
      width: 120px;
      height: 120px;
      padding: 10px;
      background-color: rgb(54, 52, 52);
      border-radius: 50%;
      box-shadow: 0px 0px 10px 0px #fff;
      position: absolute;
      left: 50%;
      transform: translate(-50%, -50%);
      img {
        width: 100%;
        height: 100%;
        background-color: rgb(163, 163, 163);
        border-radius: 50%;
      }
    }
    .el-form {
      width: 100%;
      position: absolute;
      bottom: 0;
      padding: 20px;
      box-sizing: border-box;
      .el-form-item {
        width: 100%;
        .el-input {
          width: 100%;
        }
      }
    }
    .but {
      display: flex;
      justify-content: flex-end;
    }
  }
}
</style>

步骤三:

封装的基准路径中设置请求头

// 请求响应器
Serve.interceptors.request.use(config => {
    config.headers.Authorization = sessionStorage.token;
    return config
})

通过请求拦截器设置请求头。

步骤四:

main.js中设置路由守卫

// 路由导航守卫
router.beforeEach((to, from, next) => {
  if (to.path == '/login') {
    next()
  } else {
    if (sessionStorage.token) {
      next()
    } else {
      next('/login')
    }
  }
})

判断如果当前本地中没有token值,则说明用户没有登录,则跳转到登录页面。

步骤五:

添加Home.vue页面

设置Home页的初始状态。

<template>
    <div>
        <el-button type="info" @click="logout"> 退出 </el-button>
    </div>
</template>

<script>
export default {
  methods: {
    logout() {
      sessionStorage.removeItem("token");
      if (!sessionStorage.token) {
        this.$router.push("/login");
      }
    }
  }
}
</script>
<style lang='less' scoped>
</style>

通过本地清除,在点击退出按钮时能够成功退出。

7、完成首页部分

功能要求

1、首页布局

2、侧边栏导航

3、welcome页面。

1、首页布局

<template>
  <div id="home">
    <el-container>
      <el-header>
      </el-header>
      <el-container>
        <el-aside width="200px">
        </el-aside>
        <el-main>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

通过elment-ui中的布局容器对页面进行布局

2、实现侧边栏的导航功能

首先:封装对应的API

import request from '../util/index'
export default {
    asside(form) {
        return request({
            url: '/menus',
            method: "get",
            
        })
    }
}

页面实现

<template>
  <div id="home">
    <el-container>
      <el-header>
        <div class="headerLeft">
          <img src="../assets/logo2.png" alt="" />
          <span>电商管理后台</span>
        </div>
        <div class="headerRight">
          <el-button type="info" @click="quit">退出</el-button>
        </div>
      </el-header>
      <el-container>
        <el-aside :width="has ? '64px' : '200px'">
          <div class="hasSwith"><span @click="has = !has">|||</span></div>
          <el-row>
            <el-col :span="24">
              <el-menu
                :default-active="$route.path"
                class="el-menu-vertical-demo"
                background-color="#333744"
                unique-opened
                text-color="#fff"
                active-text-color="#ffd04b"
                :collapse="has"
                collapse-transition
                router >
                <el-submenu
                  :index="item.id + ''"
                  v-for="item in menusList"
                  :key="item.id">
                  <template slot="title">
                    <i :class="iconObj[item.id]" class="icon"></i>
                    <span>{{ item.authName }}</span>
                  </template>
                  <el-menu-item
                    :index="'/' + it.path"
                    v-for="it in item.children"
                    :key="it.id" >
                    <i class="el-icon-menu"></i>
                    <span>{{ it.authName }}</span>
                  </el-menu-item>
                </el-submenu>
              </el-menu>
            </el-col>
          </el-row>
        </el-aside>
        <el-main>
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script>
import assideApi from "../api/Asside";
export default {
  data() {
    return {
      menusList: [],
      iconObj: {//图标
        125: "iconfont icon-users",
        103: "iconfont icon-tijikongjian",
        101: "iconfont icon-shangpin",
        102: "iconfont icon-danju",
        145: "iconfont icon-baobiao",
      },
      has: false,
    };
  },
  created() {
    this.menus();
  },
  methods: {
    // 退出登录
    quit() {
      sessionStorage.removeItem("token");
      if (!sessionStorage.token) {
        this.$router.push("/login");
      }
    },
    async menus() {//向后台发送请求
      const { data: res } = await assideApi.asside();
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.$message.success(res.meta.msg);
      this.menusList = res.data;
    },
  },
};
</script>
<style lang='scss' scoped>
.icon {
  margin-right: 10px;
}
#home {
  width: 100%;
  height: 100%;
}
.el-container {
  width: 100%;
  height: 100%;
}
.el-header {
  display: flex;
  justify-content: space-between;
  background-color: #373d41;
  color: #333;
  line-height: 60px;
  height: 60px;
  width: 100%;
  .headerLeft {
    color: aliceblue;
    display: flex;
    align-items: center;
    span {
      margin-left: 10px;
      font-size: 26px;
      font-family: "楷体";
      font-weight: 900;
    }
  }
}
.el-aside {
  text-align: center;
  background-color: #333744;
  color: #333;
  height: 100%;
  .hasSwith {
    height: 30px;
    width: 100%;
    color: aliceblue;
    background-color: #4a5064;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

.el-main {
  width: 100%;
  height: 100%;
  background-color: #eaedf1;
  color: #333;
}
</style>

3、welcome页面。

components中创建Welcome.vue页面,

<template>
  <div id="replace">
    <h1>欢迎来到我的世界!</h1>
  </div>
</template>
<script>
export default {}
</script>
<style lang='scss' scoped>
</style>

router.js配置相应的路由。

{
      path: '/home',
      name: 'home',
      component: () => import( /* webpackChunkName: "login_home-welcome" */ './views/Home.vue'),
      redirect: '/welcome',
      children: [{
        path: '/welcome',
        name: "welcome",
        component: () => import( /* webpackChunkName: "login_home-welcome" */ './components/welcome.vue'),
      }, 

这里的welcome值是一个欢迎页面,所以只需在页面简单写一个welcome,配置路由即可,但是,页面路由根据项目需求是要定义为重定向的。

路由配置这里要将之后的页面作为二级路由封装在home页的路由下。

8、用户管理
用户列表

创建users.vue文件,配置路由

  {
      path: '/home',
      name: 'home',
      component: () => import( /* webpackChunkName: "login_home-welcome" */ './views/Home.vue'),
      redirect: '/welcome',
      children: [{
        path: '/welcome',
        name: "welcome",
        component: () => import( /* webpackChunkName: "login_home-welcome" */ './components/welcome.vue'),
      }, {
        path: '/users',
        name: "users",
        component: () => import( /* webpackChunkName: "users" */ './components/users.vue'),
      },
    },

注意:

作为home的子路由,写在了children

在写用户列表之前,先封装一个面包屑导航。方便在多个组件中的应用

同样在componets中新建bread.vue组件

在需要写面包屑导航的页面的路由下面写

{
        path: '/users',
        name: "users",
        component: () => import( /* webpackChunkName: "users" */ './components/users.vue'),
        meta: {
          bread: ['用户管理', '用户列表']
        }
      },

在组件中

<template>
  <div id="bread">
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/welcome' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item v-for="(item, index) in breadList" :key="index">
        <span> {{ item }}</span>
      </el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>
<script>
export default {
  data() {
    return {
      breadList: [],
    };
  },
  mounted() {
    this.breadList = this.$route.meta.bread;
  },
};
</script>
<style lang='scss' scoped>
</style>

在用到的组件中通过组件注册的方式进行使用

<template>
   <Bread />
</template>
<script>
import Bread from "./bread/bread";
components: {
    Bread,
  },
</script>

完整页面

<template>
  <div id="users">
    <!-- 导入面包屑 -->
    <Bread />
    <!-- 卡片视图 -->
    <el-card>
      <!-- 用户搜索区域 -->
      <el-row :gutter="20">
        <el-col :span="8">
          <el-input
            placeholder="请输入内容"
            v-model="queryInfo.query"
            class="input-with-select"
            clearable>
            <el-button
              slot="append"
              icon="el-icon-search"
              @click="getList"></el-button>
          </el-input>
        </el-col>
        <el-col :span="4">
            <el-button type="primary" @click="addUser">添加用户</el-button></el-col>
      </el-row>
      <!-- 表格区块 -->
      <el-table :data="userList" border style="margin-top: 15px">
        <el-table-column label="#" type="index"> </el-table-column>
        <el-table-column prop="username" label="姓名"> </el-table-column>
        <el-table-column prop="email" label="邮箱"> </el-table-column>
        <el-table-column prop="mobile" label="电话"> </el-table-column>
        <el-table-column prop="role_name" label="角色"> </el-table-column>
        <el-table-column prop="mg_state" label="状态">
          <template slot-scope="scope">
            <!-- 修改用户状态 -->
            <el-switch
              v-model="scope.row.mg_state"
              active-color="#13ce66"
              inactive-color="#ccc"
              @change="stat(scope.row)">
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <!-- 操作区域 -->
          <template v-slot="scope">
            <!-- 编辑区域、 -->
            <el-button
              size="mini"
              type="primary"
              icon="el-icon-edit"
              @click="edit(scope.row)"></el-button>
            <!-- 删除按钮 -->
            <el-button
              size="mini"
              type="danger"
              icon="el-icon-delete"
              @click="del(scope.row)"></el-button>
            <!-- 设置角色-->
            <el-button
              size="mini"
              type="warning"
              icon="el-icon-setting"
              @click="sett(scope.row)"></el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页区域 -->
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="queryInfo.pagenum"
        :page-sizes="[1, 3, 6, 9]"
        :page-size="queryInfo.pagesize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
      </el-pagination>
    </el-card>
    <!-- 编辑用户对话框 -->
    <el-dialog title="提示" :visible.sync="editVisible" width="45%">
      <el-form
        :model="editList"
        ref="editRef"
        :rules="rulesFrom"
        class="demo-ruleForm">
        <el-form-item label="用户名" prop="username" label-width="75px">
          <el-input v-model="editList.username" disabled></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email" label-width="75px">
          <el-input v-model="editList.email"></el-input>
        </el-form-item>
        <el-form-item label="手机" prop="mobile" label-width="75px">
          <el-input v-model="editList.mobile"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editVisible = false">取 消</el-button>
        <el-button type="primary" @click="editAdd">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 分配权限对话框 -->
    <el-dialog title="分配角色" :visible.sync="settVisible" width="30%">
      <p>
        <span>当前的用户:{{ list.username }}</span>
      </p>
      <p>
        <span>当前的角色:{{ list.role_name }}</span>
      </p>
      <div>
        <span>分配新的角色:</span>
        <el-select v-model="set" placeholder="请选择">
          <el-option
            v-for="item in setting"
            :key="item.id"
            :label="item.roleName"
            :value="item.id">
          </el-option>
        </el-select>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="set">取 消</el-button>
        <el-button type="primary" @click="settAdd">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 添加用户对话框 -->
    <el-dialog title="添加用户" :visible.sync="addUsersVisible" width="40%">
      <el-form
        :model="addUsersForm"
        :rules="rulesFrom"
        ref="addUsersRef"
        label-width="80px"
        class="demo-ruleForm">
        <el-form-item label="用户名称" prop="username">
          <el-input v-model="addUsersForm.username"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input v-model="addUsersForm.password"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="addUsersForm.email"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="mobile">
          <el-input v-model="addUsersForm.mobile"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addUsersVisible = false">取 消</el-button>
        <el-button type="primary" @click="addDialog">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import Bread from "./bread/bread";
import UsersApi from "../api/Users";
export default {
  components: {
    Bread,
  },
  data() {
    const checkEmail = (rule, value, callback) => {
      const re = /^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-z]{2,}$/;
      if (!re.test(value)) {
        callback(new Error("请输入正确的邮箱规则"));
      } else {
        callback();
      }
    };
    const checkMoabile = (rule, value, callback) => {
      const hua = /^1\d{10}$|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/;
      if (!hua.test(value)) {
        callback(new Error("请输入正确的电话格式"));
      } else {
        callback();
      }
    };
    return {
      // 添加用户对话框初始状态
      addUsersVisible: false,
      addUsersForm: {
        username: "",
        password: "",
        email: "",
        mobile: "",
      },
      editVisible: false,
      set: "",
      settVisible: false, //分配权限列表对话框
      setting: [], //分配角色数据
      list: {}, //点击分配权限当前行信息
      queryInfo: {
        query: "",
        pagenum: 1,
        pagesize: 3,
      },
      total: 0,
      userList: [], //所有数据
      editList: {},
      rulesFrom: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
          { min: 2, max: 10, message: "请输入2到10位的字符", trigger: "blur" },
        ],
        password: [
          { required: true, message: "请输入用户密码", trigger: "blur" },
          { min: 6, max: 10, message: "请输入2到10位的字符", trigger: "blur" },
        ],
        email: [
          { required: true, message: "请输入用户邮箱", trigger: "blur" },
          {
            validator: checkEmail,
            message: "请输入正确的邮箱地址",
            trigger: "blur",
          },
        ],
        mobile: [
          { required: true, message: "请输入用户手机号", trigger: "blur" },
          {
            validator: checkMoabile,
            message: "请输入正确的手机号",
            trigger: "blur",
          },
        ],
      },
    };
  },
  created() {
    this.getList();
  },
  methods: {
    // 点击添加用户按钮
    addUser() {
      this.addUsersVisible = true;
    },
    // 点击对话框确认按钮添加用户
    async addDialog() {
      const { data: res } = await UsersApi.addFormApi(this.addUsersForm);
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.addUsersVisible = false;
      this.getList();
    },
    // 点击删除
    async del(delList) {
      const confirmText = await this.$confirm(
        "此操作将永久删除该文件, 是否继续?",
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      ).catch((err) => err);
      if (confirmText !== "confirm") {
        this.$message.info("已取消删除");
      }
      const { data: res } = await UsersApi.del(delList.id);
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.getList();
    },
    async edit(scope) {
      const { data: res } = await UsersApi.editId(scope.id);
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.editList = res.data;
      this.editVisible = true;
    },
    editAdd() {
      this.$refs.editRef.validate(async (valid) => {
        if (!valid) return;
        const { data: res } = await UsersApi.edit(
          this.editList.id,
          this.editList
        );
        if (res.meta.status !== 200) {
          this.$message.error(res.meta.msg);
        }
        this.$message.success(res.meta.msg);
        this.getList();
      });
      this.editVisible = false;
    },
    async stat(scope) {
      const { data: res } = await UsersApi.state(scope.id, scope.mg_state);
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.$message.success(res.meta.msg);
      this.getList();
    },
    // 获取所有用户
    async getList() {
      const { data: res } = await UsersApi.list({ params: this.queryInfo });
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.userList = res.data.users;
      this.total = res.data.total;
    },
    // 分页
    handleSizeChange(val) {
      this.queryInfo.pagesize = val;
      this.getList();
    },
    handleCurrentChange(val) {
      this.queryInfo.pagenum = val;
      this.getList();
    },
    // 点击分配权限按钮
    async sett(scope) {
      this.list = scope;
      this.settVisible = true;
      const { data: res } = await UsersApi.set();
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.setting = res.data;
    },
    // 点击确定按钮,分配角色
    async settAdd() {
      if (this.set === "") {
        this.$message({
          message: "请选择要分配的角色",
          type: "warning",
        });
      } else {
        const { data: res } = await UsersApi.settAd(this.list.id, {
          rid: this.list.id,
        });
        if (res.meta.status !== 200) {
          this.$message.error(res.meta.msg);
        }
        this.settVisible = false;
        this.getList();
        this.set = "";
      }
    },
  },
};
</script>
<style lang='scss' scoped>
.el-card {
  margin-top: 15px;
}
</style>
9、权限管理
1、角色管理

首先,封装对应的API

import Serve from '../../util/index'
export default {
    // 分配权限
    alloc() {
        return Serve.get(`rights/tree`)
    },
    allocSubmit(allocatId, rids) {
        return Serve.post(`roles/${allocatId}/rights`, rids);
    },
    //  角色列表数据信息
    getRolesListApi() {
        return Serve.get(`roles`);
    },
    remove(id) {
        return Serve.delete(`/roles/${id.id}`);
    },
    // 编辑用户
    queryId(item) {
        return Serve.get(`roles/${item.id}`);
    },
    sumbit(roleId, editList) {
        return Serve.put(`roles/${roleId}`, editList);
    },
    // tag标签删除
    delTags(id, item) {
        return Serve.delete(`roles/${id}/rights/${item}`);
    },
    // 添加用户
    add(addForm){
        return Serve.post(`roles`, addForm);
    }
}

建立页面,配置路由

  {
        path: '/roles',
        name: "roles",
        component: () => import( /* webpackChunkName: "jurisdiction" */ './components/jurisdiction/roles.vue'),
        meta: {
          bread: ['权限管理', '角色列表']
        }
      },

页面代码:

<template>
  <div class="roles">
    <Bread />
    <!-- 卡片式图 -->
    <el-card>
      <el-button type="primary" @click="addRoleDia = true">添加角色</el-button>
      <!-- 表格区域 -->
      <el-table :data="tableData" border style="width1: 100%">
        <el-table-column type="expand">
          <!-- 展开行信息 -->
          <template v-slot="scope">
            <!-- 一级权限 -->
            <el-row
              :class="[
                'tags',
                'rowBottom',
                'rowleft',
                index1 === 0 ? 'rowTop' : '',
              ]"
              v-for="(item, index1) in scope.row.children"
              :key="item.id"
            >
              <el-col :span="8">
                <el-tag
                  closable
                  effect="dark"
                  @close="removeTag(scope.row, item)"
                >
                  {{ item.authName }}
                </el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <el-col :span="16">
                <el-row
                  :class="[
                    'rowleft',
                    'tags',
                    'rowRigth ',
                    index2 == 0 ? 'rowBottom' : 'rowTop',
                  ]"
                  v-for="(item2, index2) in item.children"
                  :key="item2.id"
                >
                  <!-- 二级权限 -->
                  <el-col :span="8" class="rowRight">
                    <el-tag
                      type="success"
                      closable
                      @close="removeTag(scope.row, item2)"
                      >{{ item2.authName }}</el-tag
                    >
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <!-- 三级权限 -->
                  <el-col :span="15" class="rowRight tags">
                    <el-tag
                      type="warning"
                      closable
                      v-for="item3 in item2.children"
                      :key="item3.id"
                      @close="removeTag(scope.row, item3)"
                      >{{ item3.authName }}</el-tag
                    >
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
          </template>
        </el-table-column>
        <el-table-column type="index" label="#"> </el-table-column>
        <el-table-column prop="roleName" label="姓名"> </el-table-column>
        <el-table-column prop="roleDesc" label="角色描述"> </el-table-column>
        <!-- 操作区域、 -->
        <el-table-column label="操作">
          <!-- 操作区域 -->
          <template v-slot="scope">
            <!-- 编辑区域、 -->
            <el-button
              size="mini"
              type="primary"
              icon="el-icon-edit"
              @click="editQuery(scope.row)"
              >编辑</el-button
            >
            <!-- 删除按钮 -->
            <el-button
              size="mini"
              type="danger"
              icon="el-icon-delete"
              @click="del(scope.row)"
              >删除</el-button
            >
            <!-- 设置角色-->
            <el-button
              size="mini"
              type="warning"
              icon="el-icon-setting"
              @click="alloc(scope.row)"
              >分配角色</el-button
            >
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <!-- 添加角色对话框 -->
    <el-dialog title="添加角色" :visible.sync="addRoleDia" width="30%"
      ><el-form
        :model="addruleForm"
        :rules="rules"
        ref="addRef"
        label-width="80px"
        class="demo-ruleForm"
      >
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="addruleForm.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="addruleForm.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addRoleDia = false">取 消</el-button>
        <el-button type="primary" @click="addSubmit">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 编辑当前行角色对话框 -->
    <el-dialog title="修改角色" :visible.sync="editRilesDia" width="30%"
      ><el-form
        :model="editList"
        :rules="rules"
        ref="editRef"
        label-width="80px"
        class="demo-ruleForm"
      >
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="editList.roleName"></el-input>
        </el-form-item>
        <el-form-item label="角色描述" prop="roleDesc">
          <el-input v-model="editList.roleDesc"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editRilesDia = false">取 消</el-button>
        <el-button type="primary" @click="editSubmit">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 分配权限对话框 -->
    <el-dialog
      title="分配角色"
      @close="allDia"
      :visible.sync="allocationDialog"
      width="40%"
    >
      <!-- 
        default-expand-all:默认展开所有节点
        node-key:节点的唯一标识
        props:配置选项
        default-checked-keys:默认勾选的节点
     -->
      <el-tree
        :data="list"
        show-checkbox
        node-key="id"
        default-expand-all
        :default-checked-keys="deafks"
        :props="defaultProps"
        ref="treeRef"
      >
      </el-tree>

      <span slot="footer" class="dialog-footer">
        <el-button @click="allocationDialog = false">取 消</el-button>
        <el-button type="primary" @click="allocationSubmit">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
// 导入面包屑导航
import Bread from "../bread/bread";
import RolesApi from "../../api/jurisdiction/Roles";
export default {
  components: {
    Bread,
  },
  data() {
    return {
      //添加用户信息
      addruleForm: {
        roleName: "",
        roleDesc: "",
      },
      addRoleDia: false, //添加用户对话框的初始状态

      //表单校验规则
      rules: {
        roleName: {
          required: true,
          message: "请输入角色名称",
          trigger: "blur",
        },
        roleDesc: {
          required: true,
          message: "请输入角色描述",
          trigger: "blur",
        },
      },
      tableData: [], //所有数据

      // 编辑用户信息
      editList: {}, //点击编辑,获取当前行信息
      editRilesDia: false,

      // 分配角色数据
      allocationDialog: false,
      //树形控件
      defaultProps: {
        children: "children",
        label: "authName",
      },
      allocatId: "",
      deafks: [], //属性控件默认被选中的数组
      list: [], //所有权限数据信息
    };
  },
  created() {
    this.getRolesList();
  },
  methods: {
    // 分配权限
    // 分配权限对话框回调函数
    allDia() {
      this.deafks = [];
    },
    // 点击分配权限按钮
    async alloc(item) {
      this.allocatId = item.id;
      const { data: res } = await RolesApi.alloc();
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.list = res.data;
      /* 
        调用递归方法,进行传参,
        将点击的当前行的信息,以及保存默认勾选的节点的信息的空数组传入
      */
      this.getKeys(item, this.deafks);
      this.allocationDialog = true;
    },
    // 通过递归的方式,获取角色下所有三级权限的id,并且保存到defkeys中去
    getKeys(node, arr) {
      // 判断当前信息中没有children属性,则证明是三级节点,将其添加到新的数组中
      if (!node.children) {
        return arr.push(node.id);
      }
      /*
       如果当前行信息中没有children节点的话,
       通过遍历这个数组将所有拥有三级节点的节点添加到arr,
       也就是defks这个节点中去。
      */
      node.children.forEach((item) => this.getKeys(item, arr));
    },

    // 确定按钮提交分配权限,分配权限对话框
    async allocationSubmit() {
      /*
      getCheckedKeys	:
         若节点可被选择(即 show-checkbox 为 true),
         则返回目前被选中的节点的 key 所组成的数组
      getHalfCheckedKeys:
      	 若节点可被选择(即 show-checkbox 为 true),
         则返回目前半选中的节点的 key 所组成的数组
  */
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys(),
      ];
      // 要求是以字符串形式进行传递
      const idObj = keys.join(",");
      const { data: res } = await RolesApi.allocSubmit(this.allocatId, {
        rids: idObj,
      });
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.allocationDialog = false;
      this.$message.success("分配权限成功");
      this.getRolesList();
    },

    // 点击删除tag标签
    async removeTag(line, item) {
      const confirmText = await this.$confirm(
        "此操作将永久删除该权限, 是否继续?",
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      ).catch((err) => err);
      if (confirmText !== "confirm") {
        return this.$message.info("已取消删除!");
      } else {
        const { data: res } = await RolesApi.delTags(line.id, item.id);
        if (res.meta.status !== 200) {
          this.$message.success(res.meta.msg);
        }
        /*
          页面在进行删除操作后,需要进行一次刷新,调用所有数据的刷新的话,会关闭展开行
          在渲染中是通过插槽当前行下的chaildren进行渲染,所以通过对当前行重新赋值即可
          scope.row.children = res.data
        */
        line.children = res.data;
      }
    },

    // 点击对话框确定按钮,添加用户
    addSubmit() {
      this.$refs.addRef.validate(async (valid) => {
        if (!valid) return;
        const { data: res } = await RolesApi.add(this.addruleForm);
        if (res.meta.status !== 201) {
          this.$message.error(res.meta.msg);
        }
        this.addRoleDia = false;
        this.getRolesList();
      });
    },

    // 点击编辑用户按钮查询
    async editQuery(item) {
      const { data: res } = await RolesApi.queryId(item);
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.editList = res.data;
      this.editRilesDia = true;
    },
    // 提交编辑用户
    editSubmit() {
      this.$refs.editRef.validate(async (valid) => {
        if (!valid) return;
        const { data: res } = await RolesApi.sumbit(
          this.editList.roleId,
          this.editList
        );
        if (res.meta.status !== 200) {
          this.$message.error(res.meta.msg);
        }
        this.editRilesDia = false;
        this.getRolesList();
      });
    },

    // 点击删除当前行角色信息
    async del(id) {
      const confirmText = await this.$confirm(
        "此操作将永久删除该角色, 是否继续?",
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      ).catch((err) => err);
      if (confirmText !== "confirm") {
        return this.$message.info("已取消删除!");
      } else {
        const { data: res } = await RolesApi.remove(id);
        if (res.meta.status !== 200) {
          this.$message.success(res.meta.msg);
        }
        this.$message.success(res.meta.msg);
        this.getRolesList();
      }
    },

    // 角色列表数据信息
    async getRolesList() {
      const { data: res } = await RolesApi.getRolesListApi();
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.$message.success(res.meta.msg);
      this.tableData = res.data;
    },
  },
};
</script>
<style lang="scss" scoped>
.el-card {
  margin-top: 15px;
}
.el-table {
  margin-top: 15px;
}
.el-tag {
  margin: 10px;
}
.el-row {
  width: 100%;
  border: right 1px #eee;
}
.tags {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.rowTop {
  border-top: 1px solid #eee;
}
.rowBottom {
  border-bottom: 1px solid #eee;
}
.rowRigth {
  border-right: 1px solid #eee;
}
.rowleft {
  border-left: 1px solid #eee;
}
</style>

需要注意的是在我们点击分配权限按钮时,引发的逻辑业务

  1. 在点击按钮后首先是通过递归的方式,进行判断当前节点的状态,
  2. 对函数进行了封装。
  3. 在点击提交后,树形表格的点击事件中的是有两个参数方法的
 // 点击分配权限按钮
    async alloc(item) {
      this.allocatId = item.id;
      const { data: res } = await RolesApi.alloc();
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.list = res.data;
      /* 
        调用递归方法,进行传参,
        将点击的当前行的信息,以及保存默认勾选的节点的信息的空数组传入
      */
      this.getKeys(item, this.deafks);
      this.allocationDialog = true;
    },
    // 通过递归的方式,获取角色下所有三级权限的id,并且保存到defkeys中去
    getKeys(node, arr) {
      // 判断当前信息中没有children属性,则证明是三级节点,将其添加到新的数组中
      if (!node.children) {
        return arr.push(node.id);
      }
      /*
       如果当前行信息中没有children节点的话,
       通过遍历这个数组将所有拥有三级节点的节点添加到arr,
       也就是defks这个节点中去。
      */
      node.children.forEach((item) => this.getKeys(item, arr));
    },

    // 确定按钮提交分配权限,分配权限对话框
    async allocationSubmit() {
      /*
      getCheckedKeys	:
         若节点可被选择(即 show-checkbox 为 true),
         则返回目前被选中的节点的 key 所组成的数组
      getHalfCheckedKeys:
      	 若节点可被选择(即 show-checkbox 为 true),
         则返回目前半选中的节点的 key 所组成的数组
  */
      const keys = [
        ...this.$refs.treeRef.getCheckedKeys(),
        ...this.$refs.treeRef.getHalfCheckedKeys(),
      ];
      // 要求是以字符串形式进行传递
      const idObj = keys.join(",");
      const { data: res } = await RolesApi.allocSubmit(this.allocatId, {
        rids: idObj,
      });
      if (res.meta.status !== 200) {
        this.$message.error(res.meta.msg);
      }
      this.allocationDialog = false;
      this.$message.success("分配权限成功");
      this.getRolesList();
    },
4、权限列表

同样的是对其进行了API的封装

import Serve from '../../util/index'
export default {
    list(){
        return Serve.get(`rights/list`)
    }
}

其次就是在页面通过v-if判断条件进行相应的渲染

<template>
  <div id="rigths">
    <Bread />
    <!-- 卡片视图区域、 -->
    <el-card>
      <el-table :data="list" stripe border>
        <el-table-column type="index" label="#"> </el-table-column>
        <el-table-column prop="authName" label="权限名称"> </el-table-column>
        <el-table-column prop="path" label="路径"> </el-table-column>
        <el-table-column prop="level" label="权限等级">
          <template v-slot="scope">
            <el-tag size="small" type="primary" v-if="scope.row.level === '0'"
              >一级</el-tag
            >
            <el-tag
              size="small"
              type="success"
              v-else-if="scope.row.level === '1'"
              >二级</el-tag
            >
            <el-tag size="small" type="warning" v-else>三级</el-tag>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>
<script>
import Bread from "../bread/bread";
import RightApi from "../../api/jurisdiction/Rights";
export default {
  components: {
    Bread,
  },

  data() {
    return {
      list: [],
    };
  },
  created() {
    this.getList();
  },
  methods: {
    async getList() {
      const { data: res } = await RightApi.list();
      this.list = res.data;
    },
  },
};
</script>
<style lang='scss' scoped>
.el-card {
  margin-top: 15px;
}
</style>

想要查看剩余功能的小伙伴请点击:
电商后台管理d02

码字不易,感谢点赞!!!

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值