3 Star 10 Fork 7

小妖2小仙 / vue_oa

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

vue_oa

项目初始化

  1. 安装element-ui并导入到main.js中
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
  1. 安装node-sass sass-loader并导入默认样式文件
// 1. 安装解析scss文件的加载器
npm i node-sass sass-loader --save-dev
// 2. 将reset.scss/index.scss复制到项目文件中,并新建element.scss
// 3. 在main.js中导入全局样式文件
import '@/styles/index.scss'
  1. 删除vue-cli默认的组件HelloWord.vue以及router文件夹index.js中的组件导入和路由规则

登录页面路由跳转

  1. src目录下创建views文件夹,并创建login.vue组件

  2. router文件夹下的index.js中配置路由规则

// 导入登录组件
import login from '../views/login.vue'

// 在routes数组中配置路由对象
export default new Router({
  routes: [
    {
      path:'/login',
      name:'Login',
      component:login
    }
  ]
})
  1. 在页面中输入http://localhost:1234/#/login显示登录组件内容

使用element-ui布局登录页面

  1. 使用element-ui表单组件布局结构
<div class="login">
    <el-form ref="form" :model="form" class="container" :rules="rules">
      <el-form-item>
        <div class="avatar">
          <img src="../assets/avatar.jpg" alt="">
        </div>
      </el-form-item>
      <el-form-item prop="username">
        <el-input v-model="form.username" placeholder="账户" prefix-icon="myicon myicon-user"></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input v-model="form.password" placeholder="密码" prefix-icon="myicon myicon-key"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" class="login-btn">登录</el-button>
      </el-form-item>
    </el-form>
  </div>
  1. 书写样式
.login {
  position: fixed;
  width: 100%;
  height: 100%;
  background-color: #2f4050;

  .container {
    position: absolute;
    left: 0;
    right: 0;
    width: 400px;
    padding: 0px 40px 15px 40px;
    margin: 200px auto;
    background: white;
    .avatar {
      position: relative;
      left: 50%;
      width: 120px;
      height: 120px;
      margin-left: -60px;
      margin-top: -60px;
      box-sizing: border-box;
      border-radius: 50%;
      border: 10px solid #fff;
      box-shadow: 0 1px 5px #ccc;
      overflow: hidden;
    }
    .login-btn {
      width: 100%;
    }
  }
}
  1. 定义数据及表单验证规则
export default {
  data () {
    return {
      form: {
        username: '',
        password: ''
      },
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' }
        ]
      }
    }
  }
}

使用axios发送登录请求

  1. 安装axios
npm i axios -S
  1. 在src目录下新建api/index.js文件,封装登录请求
// 导入axios
import axios from 'axios'

// 设置请求的根路径
const baseURL = 'http://www.lovegf.cn:8888/api/private/v1/'
axios.defaults.baseURL = baseURL

// 登录验证
export const checkUser = params => {
  return axios.post('login', params).then(res => res.data)
}
  1. login.vue中的登录按钮上绑定点击事件,发送登录请求
// 导入封装的登录请求接口
import {checkUser} from '../api/index.js'

// 定义登录方法
methods: {
    loginSubmit (formName) {
      // 校验表单
      this.$refs[formName].validate(valide => {
        if (valide) {
          // 发送登录请求
          checkUser(this.form).then(res => {
            if (res.meta.status === 200) {
              // 如果成功要跳转至首页
              this.$router.push({name: 'Home'})
            } else {
              // 如果失败,展示提示信息
              this.$message({
                type: 'error',
                message: res.meta.msg
              })
            }
          })
        } else {
          console.log('校验不通过')
        }
      })
    }
}
  1. 在views文件夹中新建Home.vue文件,并配置好路由规则
import home from '../views/Home.vue'

routes: [
    {
      path:'/login',
      name:'Login',
      component:login
    },
    {
      path:'/home',
      name:'Home',
      component:home
    }
]

存储登录状态并设置axios拦截器

除登录请求外,其他所有请求发送都必须保证请求头中有登录成功返回的token值,所以需要使用拦截器给请求添加Authorization

  1. 登录成功时将后台返回的token存储起来
// 存储后台返回的登录成功凭证token
localStorage.setItem('mytoken', res.data.token)
  1. 使用axios拦截器将token添加到请求头中
// 请求拦截器,给所有的请求加上token
axios.interceptors.request.use(function (config) {
  // 取出localStorage中存储的token值
  let token = localStorage.getItem('mytoken')
  // 设置到请求头中 Authorization这个名字是后台规定的
  config.headers['Authorization'] = token
  return config
}, function (error) {
  return Promise.reject(error)
})
  1. 封装获取用户列表请求
// 获取用户列表
export const getUserList = params => {
  return axios.get('users', params).then(res => res.data)
}
  1. Home.vue的生命周期函数created中发送获取数据列表请求
created(){
    let params = {params: {query: '', pagenum: 1, pagesize: 1}}
    getUserList(params).then(res => {
    console.log(res)
    })
}

使用vue-router路由全局守卫进行路由拦截

对于需要登录的页面访问前需要判断是否已经登录,使用路由导航钩子进行拦截

  1. router/index.js中使用路由全局守卫拦截所有的路由跳转
// 注册一个全局守卫,作用是在路由跳转前,对路由进行判断,防止未登录的用户跳转到其他需要登录的页面去
router.beforeEach((to, from, next) => {
  let token = localStorage.getItem('mytoken')
  // 如果已经登录,放行
  if (token) {
    next()
  } else {
    // 如果没有登录,访问非登录页面,则跳转到登录页面
    if (to.path !== '/login') {
      next({path: '/login'})
    } else {
      // 如果没有登录,但访问的是登录页面,直接进入
      next()
    }
  }
})

首页整体布局

  1. 使用element-ui布局容器进行首页整体布局
<div class="home">
    <el-container>
        <el-aside width="200px">Aside</el-aside>
        <el-container>
        <el-header>Header</el-header>
        <el-main>Main</el-main>
        </el-container>
    </el-container>
</div>
  1. 调整样式
.home {
  height: 100%;
  .el-menu-admin:not(.el-menu--collapse) {
    width: 200px;
    min-height: 400px;
  }
  .el-container {
    height: 100%;
  }
  .el-aside {
    background-color: #545c64;
  }
  .el-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background-color: #545c64;
  }
  .logo {
    height:60px;
    background: url(../assets/logo.png);
    background-size: cover;
    background-color: #989898;
  }
  .toggle-btn {
    font-size: 36px;
    color: #989898;
    cursor: pointer;
    line-height: 60px;
  }
  .system-title {
    font-size: 28px;
    color: white;
  }
  .logout-btn {
    color: orange;
  }
}

完成侧边栏布局

  1. 使用element-ui的导航菜单进行结构布局
<!-- 侧边栏 -->
<el-aside width="200px">
    <div class="logo"></div>
    <el-menu
        default-active="2"
        class="el-menu-admin"
        @open="handleOpen"
        @close="handleClose"
        background-color="#545c64"
        text-color="#fff"
        active-text-color="#ffd04b">
        <el-submenu index="1">
        <template slot="title">
            <i class="el-icon-location"></i>
            <span>用户管理</span>
        </template>
        <el-menu-item index="2">
            <i class="el-icon-menu"></i>
            <span slot="title">用户列表</span>
        </el-menu-item>
        </el-submenu>
    </el-menu>
</el-aside>
  1. 定义导航菜单的点击事件
methods:{
    handleOpen (key, keyPath) {
        console.log(key, keyPath)
    },
    handleClose (key, keyPath) {
        console.log(key, keyPath)
    }
}

完成头部内容以及中间部分路由跳转

  1. 使用element-ui完成头部导航条
<!-- header部分 -->
<el-header>
    <i class="myicon myicon-menu toggle-btn" @click="toggleCollapse"></i>
    <div class="system-title">电商后台管理系统</div>
    <div>
    <span class="welcome">
        您好,xxx
    </span>
    <el-button type="text" @click="logout">退出</el-button>
    </div>
</el-header>
<!-- 中间内容部分 -->
<el-main>
    <router-view></router-view>
</el-main>
  1. data中定义isCollapse属性控制左侧导航的切换
// 1. el-menu上绑定collapse属性
// 2. 定义isCollapse:false
data() {
    return {
        isCollapse:false
    }
}
/// 3. 切换事件
toggleCollapse(){
    this.isCollapse = !this.isCollapse
}
  1. 退出登录按钮绑定事件
logout () {
    // 1. 清除登录状态,即删除保存在localStorage中的token
    localStorage.removeItem('mytoken')
    // 2. 跳转到登录页面
    this.$router.push({name: 'Login'})
}
  1. 定义路由规则显示中间区域

由于每次点击左侧导航都是中间区域变化,而整体页面的左侧和头部不变,所以中间区域应该设置成子路由

// 1. 创建welcome组件并导入
import welcome from '../views/welcome/Welcome.vue'

// 2. 配置子路由规则用于显示welcome组件
{
    path: '/',
    name: 'Home',
    component: home,
    redirect: {
        path: 'welcome'
    },
    children: [{
        name: 'Welcome',
        path: 'welcome',
        component: welcome
    }]
}
// 3. 子路由的组件需要显示到父路由对应的组件中,所以Home.vue中需要放一个router-view
<el-main>
    <router-view></router-view>
</el-main>

集成Vuex保存用户名

  1. 新建store/index.js文件,定义store实例
import Vue from 'vue'

// 1. 导入并绑定Vuex
import Vuex from 'vuex'
Vue.use(Vuex)

// 2. 定义数据存储仓库state
const state = {
  username: ''
}

// 3. 定义用于修改数据的方法
const mutations = {
  setUsername: (state, username) => {
    state.username = username
    localStorage.setItem('username', username)
  }
}

// 4. 异步修改数据
const actions = {}

// 5. 计算属性
const getters = {
  username: (state) => localStorage.getItem('username')
}

// 6. 导出store实例
export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})
  1. main.js中导入并集成store
// 导入
import store from './store/index.js'
// 挂载到vue实例
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})
  1. 登录成功后调用修改数据的方法保存数据
// 2. 使用vuex存储用户名
this.$store.commit('setUsername', res.data.username)
  1. 使用$store.getters.username显示数据
<span class="welcome">
    您好,{{$store.getters.username}}
</span>

用户列表界面路由跳转及布局

  1. 启用element-ui路由跳转功能,Home.vue组件中的el-menu标签上添加:router = "true"

  2. el-menu-item标签的index属性设置需要跳转的路由路径

<el-menu-item index="/user">
    <i class="el-icon-menu"></i>
    <span slot="title">用户列表</span>
</el-menu-item>
  1. 创建User.vue组件,并配置好路由规则
// 导入组件
import user from '../views/user/User.vue'
// 创建路由规则对象
{
    name: 'User',
    path: 'user',
    component: user
}
  1. 使用element-ui组件进行页面布局
<div class="user">
    <el-row>
        <el-col :span="24">
            <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item>用户管理</el-breadcrumb-item>
            <el-breadcrumb-item>用户列表</el-breadcrumb-item>
        </el-breadcrumb>
        </el-col>
    </el-row>
    <el-row>
        <el-col :span="24">
            <!-- 给组件绑定原生事件的话,需要一个.native的修饰符 -->
            <el-input placeholder="请输入内容" class="search-input">
            <el-button slot="append" icon="el-icon-search"></el-button>
            </el-input>
            <el-button type="success" plain>添加用户</el-button>
        </el-col>
    </el-row>
    <el-table
        :data="tableData"
        border
        style="width: 100%">
        <el-table-column
        prop="date"
        label="日期"
        width="180">
        </el-table-column>
        <el-table-column
        prop="name"
        label="姓名"
        width="180">
        </el-table-column>
        <el-table-column
        prop="address"
        label="地址">
        </el-table-column>
    </el-table>
    <div class="page">
        <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="1"
            :page-sizes="[10, 20, 30, 40]"
            :page-size="10"
            layout="total, sizes, prev, pager, next, jumper"
            :total="40">
        </el-pagination>
    </div>
</div>
  1. 跳转样式
/*element.scss中书写面包屑组件样式*/
.el-breadcrumb {
    background-color: #D3DCE6;
    height: 45px;
    font-size: 15px;
    padding-left: 10px;
    line-height: 45px;
    margin-bottom: 15px;
}

/*组件内部style标签中书写以下样式*/
.user {
  .margin-20 {
    margin: 20px 0;
  }
  .search-input {
    width: 300px;
  }
  .page {
    padding: 5px 0;
    background-color: #D3DCE6;
  }
}   

用户列表数据渲染

  1. 发送数据请求
// 1. data中定义存储数据的数组userList
// 2. 在methods中定义数据请求方法
initList () {
    getUserList({params: {query: '', pagenum: 1, pagesize: 3}}).then(res => {
        console.log(res)
        this.userList = res.data.users
    })
}
// 3. 在生命周期函数created中发送请求
created(){
    this.initList()
}
  1. 渲染数据
<el-table
    :data="userList"
    border
    style="width: 100%">
    <el-table-column
        type="index"
        width="50">
    </el-table-column>
    <el-table-column
        prop="username"
        label="姓名"
        width="180">
    </el-table-column>
    <el-table-column
        prop="email"
        label="邮箱"
        width="180">
    </el-table-column>
    <el-table-column
        prop="mobile"
        label="电话">
    </el-table-column>
    <el-table-column label="用户状态">
        <template slot-scope="scope">
            <el-switch v-model="value"></el-switch>
        </template>
    </el-table-column>
    <el-table-column label="操作">
        <template slot-scope="scope">
            <el-button size="mini" type="primary" plain icon="el-icon-edit"></el-button>
            <el-button size="mini" type="danger" plain icon="el-icon-delete"></el-button>
            <el-button size="mini" type="warning" plain icon="el-icon-check"></el-button>
        </template>
    </el-table-column>
</el-table>

完成搜索功能

  1. data中定义query属性保存搜索条件,并将搜索条件加入到数据请求的参数中
// 由于搜索接口和获取用户列表是同一个接口,所以只需要加入搜索条件即可
initList () {
    getUserList({params: {query: this.query, pagenum: 1, pagesize: 3}}).then(res => {
        console.log(res)
        this.userList = res.data.users
    })
}
  1. 给搜索按钮绑定点击事件
<el-button slot="append" icon="el-icon-search" @click="initList"></el-button>
  1. 给搜索框绑定keydown事件
<!-- 组件绑定原生DOM事件需要绑定native事件修饰符 -->
<el-input placeholder="请输入内容" class="search-input" v-model="query" @keydown.native.enter="initList">

完成分页功能

  1. 在data中定义total,pagesize,pagenum三个属性分别存储数据总条数、每页显示多少条、页码

  2. 数据请求方法中给total赋值

// 初始化表格数据
initList () {
    getUserList({params: {query: this.query, pagenum: this.pagenum, pagesize: this.pagesize}}).then(res => {
        console.log(res)
        this.userList = res.data.users
        this.total = res.data.total
    })
}
  1. 切换页码和切换条数事件中重新发送请求
handleSizeChange (val) {
    console.log(`每页 ${val} 条`)
    this.pagesize = val
    this.initList()
},
handleCurrentChange (val) {
    console.log(`当前页: ${val}`)
    this.pagenum = val
    this.initList()
}

完成修改用户状态功能

  1. 使用v-model给switch开关绑定状态值,并且添加切换状态的方法
<el-switch v-model="scope.row.mg_state" @change="changeUserState(scope.row)"></el-switch>
  1. 定义更新用户状态的网络请求
// 更改用户状态
export const changeUserState = params => {
  return axios.put(`users/${params.uid}/state/${params.type}`).then(res => res.data)
}
  1. 定义切换状态的方法发送网络请求更新用户状态
// 改变用户状态
changeUserState (row) {
    console.log(row)
    changeUserState({uid: row.id, type: row.mg_state}).then(res => {
        if (res.meta.status === 200) {
            this.$message({
                type: 'success',
                message: '修改用户状态成功'
            })
        } else {
            this.$message({
                type: 'warning',
                message: res.meta.msg
            })
        }
    })
}

完成添加用户功能

  1. 使用el-dialog组件实现弹窗
<!-- 1. 添加按钮绑定点击事件 -->
<el-button type="success" plain @click="addDialogFormVisible=true">添加用户</el-button>
<!-- 2. el-dialog布局 -->
<!-- 添加用户对话框 -->
<!-- 
  :visible.sync属性控制弹窗显示隐藏
 -->
<el-dialog title="添加用户" :visible.sync="addDialogFormVisible">
    <el-form :model="addForm" label-width="80px" :rules="rules" ref="addUserForm">
        <el-form-item label="用户名" prop="username">
        <el-input v-model="addForm.username" auto-complete="off"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
        <el-input v-model="addForm.password" auto-complete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
        <el-input v-model="addForm.email" auto-complete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="mobile">
        <el-input v-model="addForm.mobile" auto-complete="off"></el-input>
        </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
        <el-button @click="addDialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="addUserSubmit('addUserForm')">确 定</el-button>
    </div>
</el-dialog>
  1. data中定义表单用到的数据和表单验证规则
addDialogFormVisible: false,
addForm: {
    username: '',
    password: '',
    email: '',
    mobile: ''
},
// 添加用户的表单验证
rules: {
    username: [{ 
        required: true, message: '请输入用户名', trigger: 'blur'
    }],
    password: [{ 
        required: true, message: '请输入密码', trigger: 'blur' 
    }],
    email: [{ 
        required: true, message: '请输入邮箱地址', trigger: 'blur' },{ 
        type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur,change'
    }],
    mobile: [{ 
        required: true, message: '电话不能为空' 
    }]
}
  1. 定义添加用户的网络请求
// 添加用户
export const addUser = params => {
  return axios.post('users', params).then(res => res.data)
}
  1. 点击确定按钮发送网络请求
// 添加用户
addUserSubmit (formName) {
    this.$refs[formName].validate(valide => {
        if (valide) {
            // 执行添加用户方法
            addUser(this.addForm).then(res => {
                console.log(res)
                if (res.meta.status === 201) {
                    this.$message({
                        type: 'success',
                        message: '创建用户成功!'
                    })
                }
                this.addDialogFormVisible = false
                this.initList()
            })
        }
    })
}

完成编辑用户功能

  1. 使用el-dialog组件实现弹窗
<!-- 编辑按钮点击事件 -->
<el-button size="mini" type="primary" plain icon="el-icon-edit" @click="showEditDialog(scope.row)"></el-button>

<!-- 编辑用户对话框 -->
<el-dialog title="编辑用户" :visible.sync="editDialogFormVisible">
    <el-form :model="editForm" label-width="80px" :rules="rules" ref="editUserForm">
        <el-form-item label="用户名" prop="username">
        <el-input v-model="editForm.username" auto-complete="off" :disabled="true"></el-input>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
        <el-input v-model="editForm.email" auto-complete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话" prop="mobile">
        <el-input v-model="editForm.mobile" auto-complete="off"></el-input>
        </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
        <el-button @click="editDialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="editUserSubmit('editUserForm')">确 定</el-button>
    </div>
</el-dialog>
  1. 定义编辑表单中的数据
editDialogFormVisible: false,
editForm: {
    username: '',
    email: '',
    mobile: '',
    id: 0
}
  1. 定义获取用户信息的网络请求和编辑用户信息的网络请求
// 根据id获取用户信息
export const getUserById = params => {
  return axios.get(`users/${params}`).then(res => res.data)
}

// 编辑用户信息
export const editUser = params => {
  return axios.put(`users/${params.id}`, params).then(res => res.data)
}
  1. 点击编辑按钮发送获取用户信息的网络请求
// 显示编辑用户对话框
showEditDialog (row) {
    this.editDialogFormVisible = true
    // 获取用户信息
    getUserById(row.id).then(res => {
        if (res.meta.status === 200) {
            this.editForm.username = res.data.username
            this.editForm.email = res.data.email
            this.editForm.mobile = res.data.mobile
            this.editForm.id = res.data.id
        }
    })
},
  1. 点击确定按钮发送编辑用户信息的网络请求
// 编辑用户提交
editUserSubmit (formName) {
    this.$refs[formName].validate(valide => {
        if (valide) {
            editUser(this.editForm).then(res => {
                if (res.meta.status === 200) {
                    this.$message({
                        type: 'success',
                        message: '编辑成功'
                    })
                    this.editDialogFormVisible = false
                    this.initList()
                }
            })
        }
    })
}

完成删除用户功能

  1. 添加删除按钮事件
<el-button size="mini" type="danger" plain icon="el-icon-delete" @click="showDeleteDialog(scope.row)"></el-button>
  1. 定义删除用户的网络请求
// 删除用户信息
export const deleteUser = params => {
  return axios.delete(`users/${params}`).then(res => res.data)
}
  1. 发送删除用户网络请求
// 显示删除对话框
showDeleteDialog (row) {
    this.$confirm('此操作将永久删除该用户, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(() => {
        // 执行删除用户操作
        deleteUser(row.id).then(res => {
            if (res.meta.status === 200) {
                this.$message({
                    type: 'success',
                    message: '删除成功!'
                })
                this.initList()
            }
        })
    }).catch(() => {
        this.$message({
            type: 'info',
            message: '已取消删除'
        })
    })
}

完成分配用户角色功能

  1. 分配角色按钮添加事件,并使用弹窗组件
<el-button size="mini" type="warning" plain icon="el-icon-check" @click="showGrantDialog(scope.row)"></el-button>

<!-- 分配角色对话框 -->
<el-dialog title="分配角色" :visible.sync="grantDialogFormVisible">
    <el-form :model="grantForm" label-width="120px">
        <el-form-item label="当前的用户:" prop="username">
        <el-tag type="info">{{grantForm.username}}</el-tag>
        </el-form-item>
        <el-form-item label="请选择角色:">
        <el-select v-model="roleId" placeholder="请选择角色">
            <el-option
            v-for="(role, index) in roleList"
            :key="index"
            :label="role.roleName"
            :value="role.id">
            </el-option>
        </el-select>
        </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
        <el-button @click="grantDialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="grantUserSubmit()">确 定</el-button>
    </div>
</el-dialog>
  1. 定义分配角色表单需要用到的数据
// 控制分配用户角色对话框显示隐藏
grantDialogFormVisible: false,
// 存储分配角色时获取的用户信息
grantForm: {},
// 角色列表
roleList: [],
// 角色ID
roleId: ''
  1. 定义获取角色列表和分配角色的网络请求
// 获取角色列表
export const getRoleList = params => {
  return axios.get('roles').then(res => res.data)
}
// 分配角色
export const grantUserRole = params => {
  return axios.put(`users/${params.id}/role`, {id: params.id, rid: params.rid}).then(res => res.data)
}
  1. 点击分配角色按钮发送获取角色列表的请求,并使用v-for渲染数据
// 显示分配角色对话框
showGrantDialog (row) {
    this.grantForm = row
    this.grantDialogFormVisible = true
    getRoleList().then(res => {
        console.log(res)
        if (res.meta.status === 200) {
            this.roleList = res.data
        }
    })
}
  1. 点击确定按钮发送分配角色网络请求
// 分配角色
grantUserSubmit () {
// 当没有选择角色时,不发送网络请求
    if (!this.roleId) {
        this.$message({
            type: 'warning',
            message: '角色不能为空,请选择!'
        })
    } else {
        grantUserRole({id: this.grantForm.id, rid: this.roleId}).then(res => {
            if (res.meta.status === 200) {
                this.$message({
                    type: 'success',
                    message: '设置角色成功'
                })
                this.grantDialogFormVisible = false
            } else {
                this.$message({
                    type: 'error',
                    message: res.meta.msg
                })
            }
        })
    }
}

权限管理路由跳转

  1. 书写结构代码,更改index为路由路径
<el-submenu index = "2">
    <template slot="title">
        <i class="el-icon-location"></i>
        <span>权限管理</span>
    </template>
    <el-menu-item index="/roles">
        <i class="el-icon-menu"></i>
        <span slot="title">角色列表</span>
    </el-menu-item>
    <el-menu-item index="/rights">
        <i class="el-icon-menu"></i>
        <span slot="title">权限列表</span>
    </el-menu-item>
</el-submenu>
  1. 新建Roles.vueRights.vue组件,并配置路由规则
import rights from '../views/right/Rights.vue'
import roles from '../views/right/Roles.vue'
{
    name: 'Rights',
    path: 'rights',
    component: rights
}, {
    name: 'Roles',
    path: 'roles',
    component: roles
}

权限列表数据展示

  1. 结构代码
<div class="rights">
    <el-row>
    <el-col :span="24">
        <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item>权限管理</el-breadcrumb-item>
        <el-breadcrumb-item>权限列表</el-breadcrumb-item>
    </el-breadcrumb>
    </el-col>
    </el-row>
    <el-table
    v-loading="isLoading"
    :data="rightList"
    border
    style="width: 100%">
    <el-table-column
        type="index"
        width="50">
    </el-table-column>
    <el-table-column
        prop="authName"
        label="权限名称"
        width="180">
    </el-table-column>
    <el-table-column
        prop="path"
        label="路径">
    </el-table-column>
    <el-table-column label="层级">
        <template slot-scope="scope">
        <span>{{scope.row.level | fmtLevel}}</span>
        </template>
    </el-table-column>
    </el-table>
</div>
  1. 定义网络请求
// 获取权限列表
export const getRightList = params => {
  return axios.get(`rights/${params.type}`).then(res => res.data)
}
  1. created钩子函数中发送网络请求
created () {
    this.isLoading = true
    getRightList({type: 'list'}).then(res => {
    if (res.meta.status === 200) {
        this.rightList = res.data
        this.isLoading = false
    }
    })
}
  1. 定义过滤器过滤层级显示
filters: {
    fmtLevel (level) {
        if (level === '0') {
            return '一级'
        } else if (level === '1') {
            return '二级'
        } else {
            return '三级'
        }
    }
}

角色列表数据展示

  1. 结构代码
<div class="roles">
      <el-row>
        <el-col :span="24">
            <el-breadcrumb separator="/">
                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                <el-breadcrumb-item>权限管理</el-breadcrumb-item>
                <el-breadcrumb-item>角色列表</el-breadcrumb-item>
            </el-breadcrumb>
        </el-col>
      </el-row>
      <el-button type="primary" plain>添加角色</el-button>
      <el-table
        border
        class="mt-15"
        :data="roleList"
        style="width: 100%">
        <el-table-column type="expand">
          <template slot-scope="scope">
            <div>tag</div>
          </template>
        </el-table-column>
        <el-table-column
          label="角色名称"
          prop="roleName"
          width="165px">
        </el-table-column>
        <el-table-column
          label="描述"
          prop="roleDesc"
          width="130px">
        </el-table-column>
        <el-table-column
          label="操作">
          <template slot-scope="scope">
            <el-button size="mini" type="primary" plain icon="el-icon-edit"></el-button>
            <el-button size="mini" type="danger" plain icon="el-icon-delete"></el-button>
            <el-button size="mini" type="warning" plain icon="el-icon-check" title="授权角色"></el-button>
          </template>
        </el-table-column>
      </el-table>
  </div>
  1. 样式
.roles {
  .el-tag {
    margin-right: 5px;
    margin-bottom: 5px;
  }
  .tree-container {
    height: 300px;
    overflow: auto;
  }
}
  1. 发送网络请求渲染界面
data(){
    return {
        roleList: []
    }
},
created () {
    this.initList()
},
methods: {
    initList () {
        getRoleList().then(res => {
            if (res.meta.status === 200) {
                console.log(res)
                this.roleList = res.data
            }
        })
    }
}

角色列表展开数据展示

  1. 使用栅格布局渲染界面
<template slot-scope="scope">
    <el-row v-for="firstChildren in scope.row.children" :key="firstChildren.id">
        <el-col :span="4">
        <el-tag closable>{{firstChildren.authName}}</el-tag>
        <i class="el-icon-arrow-right" v-if="firstChildren.children.length !== 0"></i>
        </el-col>
        <el-col :span="20">
        <el-row v-for="secondChildren in firstChildren.children" :key="secondChildren.id">
            <el-col :span="4">
            <el-tag closable type="success">{{secondChildren.authName}}</el-tag>
            <i class="el-icon-arrow-right" v-if="secondChildren.children.length !== 0"></i>
            </el-col>
            <el-col :span="20">
            <el-tag closable type="warning" v-for="thirdChildren in secondChildren.children" :key="thirdChildren.id">
                {{thirdChildren.authName}}
                </el-tag>
            </el-col>
        </el-row>
        </el-col>
    </el-row>
    <el-row v-if="scope.row.children.length === 0">
        <el-col :span="24">该角色没有分配权限,请前往分配!</el-col>
    </el-row>
</template>

角色权限删除功能

  1. el-tag标签绑定删除事件
<el-tag closable @close="deleteRight(scope.row, firstChildren.id)">{{firstChildren.authName}}</el-tag>
<el-tag closable type="success" @close="deleteRight(scope.row, secondChildren.id)">{{secondChildren.authName}}</el-tag>
<el-tag closable type="warning" @close="deleteRight(scope.row, thirdChildren.id)" v-for="thirdChildren in secondChildren.children" :key="thirdChildren.id">
  1. 定义删除权限数据请求
// 删除角色指定权限
export const deleteRoleRight = params => {
  return axios.delete(`roles/${params.roleId}/rights/${params.rightId}`).then(res => res.data)
}
  1. 点击删除按钮触发删除请求
deleteRight (row, rightId) {
    deleteRoleRight({roleId: row.id, rightId: rightId}).then(res => {
        if (res.meta.status === 200) {
          row.children = res.data
        } else {
          this.$message({
            type: 'error',
            message: res.meta.msg
          })
        }
      })
    }
}

授权角色树数据展示

  1. 书写弹窗组件结构代码
<el-dialog title="授权角色" :visible.sync="dialogFormVisible">
    <div class="tree-container">
        <el-tree
        :data="rightList"
        show-checkbox
        ref="tree"
        node-key="id"
        :default-expand-all="true"
        :default-checked-keys="selectedRights"
        :props="defaultProps">
        </el-tree>
    </div>
    <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary">确 定</el-button>
    </div>
</el-dialog>
  1. 发送数据请求渲染界面
// 1. 定义组件需要用到的数据
dialogFormVisible: false, // 控制弹窗显示隐藏
rightList: [], // 存放权限列表
defaultProps: {
    children: 'children', // // 树形组件展示的数据源
    label: 'authName' // 树形组件显示的标题字段
}

// 2. 发送数据请求
showDialog (row) {
    this.dialogFormVisible = true
    this.currentRole = row
    getRightList({type: 'tree'}).then(res => {
        if (res.meta.status === 200) {
            console.log(res.data)
            this.rightList = res.data
        } else {
            this.$message({
                type: 'error',
                message: res.meta.msg
            })
        }
    })
}

完成授权角色默认选中功能

  1. 利用:default-checked-keys="selectedRights"树形控制树形组件默认选中项

  2. 遍历所有权限,找到已授权的ID存放到selectedRights

// 遍历之前先让数组清空
this.selectedRights.length = 0
// 取出当前点击角色的所有权限, 然后遍历到它的第三个children,取出它里面的所有的项的id,存进selectedRights中
// 1. 遍历第一个children取出一级权限
this.currentRole.children.forEach(first => {
    if (first.children && first.children.length !== 0) {
        // 2. 遍历第二个children取出二级权限
        first.children.forEach(second => {
            if (second.children && second.children.length !== 0) {
                    // 3. 遍历第三个children取出三级权限
                    second.children.forEach(third => {
                    this.selectedRights.push(third.id)
                })
            }
        })
    }
})

提交授权功能

  1. 定义数据请求
// 角色授权
export const grantRoleRight = (roleId, rids) => {
  return axios.post(`roles/${roleId}/rights`, rids).then(res => res.data)
}
  1. 点击确定按钮发送提交授权请求
// 提交授权
submitGrant () {
    // 获取到所有选中的权限ID
    let rids = this.$refs.tree.getCheckedKeys().join(',')
    grantRoleRight(this.currentRole.id, {rids: rids}).then(res => {
        if (res.meta.status === 200) {
            this.$message({
            type: 'success',
            message: res.meta.msg
            })
            this.dialogFormVisible = false
            this.initList()
        }
    })
}

动态获取权限菜单

  1. 定义获取权限菜单的数据请求
// 左侧菜单权限
export const getMenus = () => {
  return axios.get('menus').then(res => res.data)
}
  1. 发送获取权限菜单的请求
created() {
    getMenus().then(res => {
        if (res.meta.status === 200) {
        this.menuData = res.data;
        }
    })
}
  1. 使用v-for循环渲染界面
<el-menu
    :router="true"
    :unique-opened="true"
    :collapse="isCollapse"
    class="el-menu-admin"
    @open="handleOpen"
    @close="handleClose"
    background-color="#545c64"
    text-color="#fff"
    active-text-color="#ffd04b">
    <el-submenu :index="item.path" v-for="item in menuData" :key="item.id">
    <template slot="title">
        <i class="el-icon-location"></i>
        <span>{{item.authName}}</span>
    </template>
    <el-menu-item :index="tag.path" v-for="tag in item.children" :key="tag.id">
        <i class="el-icon-menu"></i>
        <span slot="title">{{tag.authName}}</span>
    </el-menu-item>
    </el-submenu>
</el-menu>

商品分类界面布局

  1. 创建category/Category.vue组件,配置路由规则实现跳转
{
    name: 'Category',
    path: 'categories',
    component: category
}
  1. componets文件夹中放入TreeGrid组件,并在Category.vue组件中进行集成
// 1. 在category.vue中导入组件
import TreeGrid from '@/components/TreeGrid/TreeGrid'

// 2. 注册组件
data() {
    return {
      addDialogFormVisible: false,
      columns: [
        {
          text: "分类名称",
          dataIndex: "cat_name",
          width: ""
        },
        {
          text: "是否有效",
          dataIndex: "cat_deleted",
          width: ""
        },
        {
          text: "排序",
          dataIndex: "cat_level",
          width: ""
        }
      ],
      dataSource: [],
      total: 10
    }
},
components: {
    TreeGrid
}
  1. 使用TreeGrid.vue组件
<div class="category">
    <el-row>
        <el-col :span="24">
            <el-breadcrumb separator="/">
                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                <el-breadcrumb-item>商品管理</el-breadcrumb-item>
                <el-breadcrumb-item>商品分类</el-breadcrumb-item>
            </el-breadcrumb>
        </el-col>
    </el-row>

    <el-row>
        <el-col :span="24">
            <el-button type="success" plain>添加分类</el-button>
        </el-col>
    </el-row>
    <tree-grid
    :treeStructure="true"
    :columns="columns"
    :data-source="dataSource"
    @deleteCate="deleteCategory"
    @editCate="editCategory"
    >
    </tree-grid>

    <div class="page">
        <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="1"
            :page-sizes="[10, 20, 30, 40]"
            :page-size="10"
            layout="total, sizes, prev, pager, next, jumper"
            :total="total">
        </el-pagination>
    </div>
</div>

商品分类数据获取

  1. 定义获取数据的请求
// 获取商品分类信息
export const getCategories = (params) => {
  return axios.get('categories', {params: params}).then(res => res.data)
}
  1. 发送网络请求获取数据
initList() {
    getCategories({
        type: "3",
        pagenum: this.pagenum,
        pagesize: this.pagesize
    }).then(res => {
        console.log(res);
        if (res.meta.status === 200) {
            this.total = res.data.total;
            this.dataSource = res.data.result;
        }
    });
}

添加商品分类级联选择器使用

  1. 添加分类弹窗布局
<!-- 添加分类对话框 -->
<el-dialog title="添加分类" :visible.sync="addDialogFormVisible">
    <el-form :model="addForm" label-width="80px" :rules="rules" ref="addCateForm">
    <el-form-item label="分类名称" prop="cat_name">
        <el-input v-model="addForm.cat_name" auto-complete="off"></el-input>
    </el-form-item>
    <el-form-item label="父级名称">
        <el-cascader
        :options="options"
        v-model="selectedOptions"
        :props="props"
        @change="handleChange">
        </el-cascader>
    </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
    <el-button @click="addDialogFormVisible = false">取 消</el-button>
    <el-button type="primary" @click="addCateSubmit('addCateForm')">确 定</el-button>
    </div>
</el-dialog>
  1. 定义级联选择器需要用到的数据源
addForm: {
    cat_name: "",
    cat_pid: 0,
    cat_level: 0
},
// 级联选择器的数据源
options: [],
// 级联选择器选中后的数据
selectedOptions: [], 
// props表示配置级联选择器展示的数据字段
props: {
    value: "cat_id",
    label: "cat_name"
}
  1. 点击添加分类按钮发送请求获取分类数据
addCategory() {
    this.addDialogFormVisible = true;
    getCategories({ type: "2" }).then(res => {
        console.log(res);
        if (res.meta.status === 200) {
            this.options = res.data;
        }
    });
}

添加商品分类数据提交

addCateSubmit(formName) {
    this.$refs[formName].validate(valide => {
        if (valide) {
            // 添加一级分类
            if (this.selectedOptions.length === 0) {
                this.addForm.cat_pid = 0;
                this.addForm.cat_level = 0;
            } else if (this.selectedOptions.length === 1) {
                // 添加二级分类
                this.addForm.cat_pid = this.selectedOptions[
                    this.selectedOptions.length - 1
                ];
                this.addForm.cat_level = 1;
            } else {
                // 添加三级分类
                this.addForm.cat_pid = this.selectedOptions[
                    this.selectedOptions.length - 1
                ];
                this.addForm.cat_level = 2;
            }
            addCategories(this.addForm).then(res => {
                if (res.meta.status === 201) {
                    this.addDialogFormVisible = false;
                    this.initList();
                    this.$message({
                        type: "success",
                        message: res.meta.msg
                    });
                }
            });
        }
    });
}

添加商品步骤条组件和tabs组件集成

  1. 创建商品列表组件和添加商品组件,并配置路由规则
{
    name: 'Goods',
    path: 'goods',
    component: goods
}, {
    name: 'AddGoods',
    path: 'toadd',
    component: addGoods
}
  1. 商品列表组件代码
<template>
  <div class="goods">
    <el-row>
      <el-col :span="24">
        <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item>商品管理</el-breadcrumb-item>
        <el-breadcrumb-item>商品列表</el-breadcrumb-item>
      </el-breadcrumb>
      </el-col>
    </el-row>
    <el-row>
      <el-col :span="24">
        <el-button type="success" plain @click="addGoods">添加商品</el-button>
      </el-col>
    </el-row>
  </div>
</template>
<script>
export default {
  methods: {
    addGoods () {
      this.$router.push({name: 'AddGoods'})
    }
  }
}
</script>
<style lang="scss" scoped>

</style>
  1. 添加商品组件结构代码
<div class="add-goods">
    <el-row>
        <el-col :span="24">
        <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item>商品管理</el-breadcrumb-item>
        <el-breadcrumb-item>商品列表</el-breadcrumb-item>
        </el-breadcrumb>
        </el-col>
    </el-row>
    <el-steps :active="active" finish-status="success">
        <el-step title="步骤 1"></el-step>
        <el-step title="步骤 2"></el-step>
        <el-step title="步骤 3"></el-step>
        <el-step title="步骤 4"></el-step>
        <el-step title="步骤 5"></el-step>
    </el-steps>
    <el-tabs v-model="activeName" @tab-click="handleClick" tab-position="left" class="mt-20">
        <el-tab-pane label="基本信息" name="first">
        <el-form label-width="80px">
            <el-form-item label="活动名称">
            <el-input></el-input>
            </el-form-item>
        </el-form>
        </el-tab-pane>
        <el-tab-pane label="商品参数" name="second">配置管理</el-tab-pane>
        <el-tab-pane label="商品属性" name="third">角色管理</el-tab-pane>
        <el-tab-pane label="商品图片" name="fourth">图片上传</el-tab-pane>
        <el-tab-pane label="商品内容" name="fifth">定时任务补偿</el-tab-pane>
    </el-tabs>
</div>
  1. 点击tabs切换步骤条
data () {
    return {
      active: 0,
      activeName: 'first'
    }
},
methods: {
    handleClick (tab, event) {
        switch (tab.name) {
            case 'first':
                this.active = 0
                break
            case 'second':
                this.active = 1
                break
            case 'third':
                this.active = 2
                break
            case 'fourth':
                this.active = 3
                break
            case 'fifth':
                this.active = 4
                break
            default:
                this.active = 0
                break
        }
    }
}

图片上传组件使用

  1. 使用el-upload组件上传图片
<el-upload
action="http://www.lovegf.cn:8888/api/private/v1/upload"
:on-preview="handlePreview"
:on-success="handleSuccess"
:headers="setHeader()"
:on-remove="handleRemove"
list-type="picture">
    <el-button size="small" type="primary">点击上传</el-button>
</el-upload>
  1. 设置请求头处理成功回调
handleRemove(file, fileList) {
    console.log(file, fileList);
},
handlePreview(file) {
    console.log(file);
},
// 成功后回调函数
handleSuccess(response, file, fileList) {
    if (response.meta.status === 200) {
        this.$message({
            type: "success",
            message: response.meta.msg
        });
    }
},
// 设置请求头
setHeader() {
    let token = localStorage.getItem("mytoken");
    return { Authorization: token };
}

axios上传图片

<input  name="file" type="file" accept="image/png,image/gif,image/jpeg" @change="update($event)"/>
update(e) {
    let file = e.target.files[0];// 获取文件数据
    let param = new FormData(); // 创建form对象
    param.append("file", file); // 通过append向form对象添加数据
    console.log(param.get("file")); // FormData私有类对象,访问不到,可以通过get判断值是否传进去
    let config = {
    headers: {
        "Content-Type": "multipart/form-data",
    }
    };
    axios
    .post("http://www.lovegf.cn:8888/api/private/v1/upload", param, config)
    .then(response => {
        console.log(response.data);
    });
}

空文件

简介

暂无描述 展开 收起
JavaScript
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/UniverseKing/vue_oa.git
git@gitee.com:UniverseKing/vue_oa.git
UniverseKing
vue_oa
vue_oa
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891