使用 Element-UI 搭建后台管理系统

滔哥 2021年02月27日 282次浏览

element-ui是由饿了么开源的一套基于 Vue.js 的组件库。本实验我们就使用element-ui将我们论坛的后台管理框架搭建起来。

知识点

  • 使用 vue-cli 创建基于 element-ui 的项目
  • 使用 element-ui 搭建管理后台页面框架
  • 实现后台登录功能

安装 vue-cli

Element-ui官方为vue-cli准备了相应的插件,你可以使用它快速地搭建一个基于Element-ui的项目。所以我们首先需要安装vue-cli。

开始之前请先确认我们的环境,Nuxt.js 需要 Node.js 8.9 或更高版本 (推荐 8.11.0+)。你可以使用 nvm 或 nvm-windows 在同一台电脑中管理多个 Node 版本。
环境确认无误后,执行以下命令来安装vue-cli

npm install -g @vue/cli

安装之后,你就可以在命令行中访问 vue 命令。如果安装成功,输入 vue 后应该会显示一份所有可用命令的帮助信息。

你还可以用这个命令来检查其版本是否正确:

vue --version

创建 Element-UI 项目

假设我们基于element-ui的后台项目名称叫做admin,那么我们可以使用以下命令来创建项目。

vue create admin

执行命令后,会要求选择一个预设配置,这里我们选择Manually select features,进行手动配置,手动配置中会要求我们做一些选择,我们的选择如下:
接下来等该命令执行完成之后,执行下面命令添加element-ui依赖:

cd admin
vue add element

执行该命令的时候有一些选项,我们按照提示信息选择即可,如下图:
添加完成element-ui依赖之后,我们接着来添加vue-router,执行以下命令即可添加:

vue add router
``
然后执行以下命令即可启动服务:

npm run serve
``
服务启动成功后,打开浏览器即可预览

后台页面框架搭建

管理后台的页面我们采用顶部菜单导航、下面正文内容的形式,如下图:
uid770606-20191022-1571732387262

element-ui为我们提供了丰富的组件库,接下来我们就利用el-container进行整个页面的布局、利用el-menu作为顶部导航菜单来完成后台框架的搭建。

打开文件admin/src/App.vue文件,修改代码,修改后完整代码如下:

<template>
  <div id="app">
    <el-container style="height:100%;">
      <el-header>
        <el-menu
          class="main-menu"
          mode="horizontal"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b"
          default-active="/"
          :router="true"
        >
          <el-menu-item index="/">首页</el-menu-item>
          <el-submenu index>
            <template slot="title">内容管理</template>
            <el-menu-item index="/topics">话题管理</el-menu-item>
            <el-menu-item index="/comments">跟帖管理</el-menu-item>
          </el-submenu>
          <el-menu-item index="/users">用户管理</el-menu-item>
          <el-menu-item index="/about">关于</el-menu-item>
        </el-menu>
      </el-header>
      <el-main class="main-container">
        <router-view />
      </el-main>
    </el-container>
  </div>
</template>

<script>
  export default {
    methods: {
      handleOpen(key, keyPath) {
        console.log(key, keyPath);
      },
      handleClose(key, keyPath) {
        console.log(key, keyPath);
      },
    },
  };
</script>

<style lang="scss" scoped></style>

<style lang="scss">
  html,
  body {
    height: 100%;
    margin: 0px;
  }

  #app {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    height: 100%;

    .el-header {
      text-align: center;
      line-height: 60px;
      padding: 0px !important;
    }

    .main-menu {
      width: 100%;
    }

    .main-container {
      overflow: auto;
    }
  }
</style>

整个管理后台页面布局完成之后,接下来就可以逐个功能模块去完善功能了。

用户登录功能

后台管理系统我们采用的是前后端分离的方案,后台用户登录功能和我们之前章节讲解的使用Nuxt.js开发的 bbs 前台页面登录处理方式相同。他们的登录原理为:

  1. 调用登录接口,验证用户名密码,验证成功后接口返回授权令牌(userToken);
  2. 前端网页收到授权令牌(userToken)后,将他们存储到 cookie 中;
  3. 前端网页在每次请求后台接口的时候检查 cookie 中是有有userToken,如果有就带上;
  4. 服务端在收到网页中的接口请求时,检查请求中是否有合法的userToken,否则就返回错误要求网页进行登录;
    接下来我们逐步来实现后台的登录功能:

第一步:基于之前 Go 语言的服务端代码进行修改,新建文件server/admin_user_controller.go,内容如下:

package main

import (
    "github.com/kataras/iris/context"
    "github.com/mlogclub/simple"
)

type AdminUserController struct {
    Ctx context.Context
}

// 登录
func (this *AdminUserController) PostLogin() *simple.JsonResult {
    var (
        username = this.Ctx.PostValue("username")
        password = this.Ctx.PostValue("password")
    )
    user, token := UserService.Login(username, password)
    if user == nil {
        return simple.JsonErrorMsg("用户名密码错误")
    }
    // 登录成功返回用户信息和授权令牌
    return simple.NewRspBuilder(user).Put("token", token).JsonResult()
}

// 获取当前登录用户
func (this *AdminUserController) GetCurrent() *simple.JsonResult {
    user := UserService.GetCurrent(this.Ctx)
    if user == nil {
        return simple.JsonError(simple.ErrorNotLogin)
    }
    return simple.JsonData(user)
}

同时修改server/main.go文件,将刚刚定义的AdminUserController添加到路由中,同时使用iris的middleware功能实现后台登录拦截,修改后的完整代码如下:

package main

import (
    "github.com/jinzhu/gorm"
    "github.com/kataras/iris"
    "github.com/kataras/iris/context"
    "github.com/kataras/iris/mvc"
    "github.com/mlogclub/simple"

    "github.com/iris-contrib/middleware/cors"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

var db *gorm.DB

func main() {
    initDB()

    app := iris.New()

    // 跨域配置
    app.Use(cors.New(cors.Options{
        AllowedOrigins:   []string{"*"}, // allows everything, use that to change the hosts.
        AllowCredentials: true,
        MaxAge:           600,
        AllowedMethods:   []string{iris.MethodGet, iris.MethodPost, iris.MethodOptions, iris.MethodHead, iris.MethodDelete, iris.MethodPut},
        AllowedHeaders:   []string{"*"},
    }))
    app.AllowMethods(iris.MethodOptions)

    mvc.Configure(app.Party("/api/user"), func(mvcApp *mvc.Application) {
        mvcApp.Handle(new(UserController))
    })

    mvc.Configure(app.Party("/api/topic"), func(mvcApp *mvc.Application) {
        mvcApp.Handle(new(TopicController))
    })

    mvc.Configure(app.Party("/api/comment"), func(mvcApp *mvc.Application) {
        mvcApp.Handle(new(CommentController))
    })

    mvc.Configure(app.Party("/api/admin"), func(mvcApp *mvc.Application) {
        // 使用middleware实现登录校验
        mvcApp.Router.Use(func(context context.Context) {
            if context.Path() == "/api/admin/user/login" { // 登录接口不做校验
                context.Next()
                return
            }
            user := UserService.GetCurrent(context)
            if user == nil { // 如果用户没登录,那么返回错误码1,要求登录
                _, _ = context.JSON(simple.JsonError(simple.ErrorNotLogin))
                context.StopExecution()
                return
            }
            context.Next()
        })
        mvcApp.Party("/user").Handle(new(AdminUserController))
    })

    _ = app.Run(iris.Addr(":8081"), iris.WithoutServerError(iris.ErrServerClosed))
}

// 初始化数据库链接
func initDB() {
    var err error
    db, err = gorm.Open("mysql", "root@tcp(localhost:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local")
    if err != nil {
        panic(err)
    }
    err = db.AutoMigrate(&User{}, &UserToken{}, &Topic{}, &Comment{}).Error
    if err != nil {
        panic(err)
    }
    db.LogMode(true)
}

第二步:接下来我们修改前端页面,实现登录功能。

登录功能需要安装以下两个依赖:

axios 用于接口请求
js-cookie Cookie 操作工具,用于读取和写入userToken
在我们 admin 前端项目目录中执行下面命令来来添加axios和js-cookie依赖

vue add axios
npm install --save js-cookie

axios插件添加完成之后会自动生成文件:admin/src/plugins/axios.js,我们修改修改一下该文件,让axios在每次请求的时候都在 header 中带上userToken,修改之后的完整内容如下:

"use strict"; import Vue from 'vue'; import axios from "axios"; import qs from
'qs' import cookies from 'js-cookie' // Full config:
https://github.com/axios/axios#request-config // axios.defaults.baseURL =
process.env.baseURL || process.env.apiUrl || ''; //
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; //
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded'; let config = { // baseURL:
process.env.baseURL || process.env.apiUrl || "" // timeout: 60 * 1000, //
Timeout // withCredentials: true, // Check cross-site Access-Control //
接口host,请根据实际情况配置 baseURL: 'http://localhost:8081' }; const _axios =
axios.create(config); _axios.interceptors.request.use( function (config) { const
userToken = cookies.get('userToken'); if (userToken) { //
如果cookie中有userToken,那么放到请求header中
config.headers.common['X-User-Token'] = userToken } config.transformRequest = [
function (data) { if (process.client && data instanceof FormData) { //
如果是FormData就不转换 return data } data = qs.stringify(data) return data } ]
return config; }, function (error) { // Do something with request error return
Promise.reject(error); } ); // Add a response interceptor
_axios.interceptors.response.use( function (response) { if (response.status !==
200) { return Promise.reject(response); } if (response.data.success) { return
Promise.resolve(response.data.data); } else { return
Promise.reject(response.data); } }, function (error) { // Do something with
response error return Promise.reject(error); } ); _axios.onRequest
Plugin.install = function (Vue) { Vue.axios = _axios; window.axios = _axios;
Object.defineProperties(Vue.prototype, { axios: { get() { return _axios; } },
$axios: { get() { return _axios; } }, }); }; Vue.use(Plugin) export default
Plugin;

第三步:实现登录页面

新增文件admin/src/views/Login.vue,在该页面中实现登录操作,登录成功之后将服务端返回的userToken存储到cookie中,完整代码如下:

<template>
  <div>
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>登录</span>
      </div>
      <div class="text item">
        <el-form>
          <el-form-item label="用户名">
            <el-input v-model="username"></el-input>
          </el-form-item>
          <el-form-item label="密码">
            <el-input type="password" v-model="password"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="doLogin">登录</el-button>
          </el-form-item>
        </el-form>
      </div>
    </el-card>
  </div>
</template>

<script>
import cookies from 'js-cookie';

export default {
  data() {
    return {
      username: '',
      password: '',
    };
  },
  methods: {
    async doLogin() {
      try {
        const ret = await this.axios.post('/api/admin/user/login', {
          username: this.username,
          password: this.password,
        });
        cookies.set('userToken', ret.token);
        this.$message('登录成功');
        this.$router.push('/'); // 跳转到首页
      } catch (error) {
        this.$message.error(error.message || error);
      }
    },
  },
};
</script>

<style lang="scss" scoped></style>

第四步:配置登录页面路由

然后修改admin/src/router/index.js文件,将Login.vue页面注册到vue-router路由中,代码片段如下:

...

const routes = [
  ...
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/Login.vue')
  },
  ...
]

...

经过以上几个步骤我们就完成了后台的登录功能,接下来我们分别启动 Go 语言编写的接口服务和后台页面服务即可预览效果。

注意:登录用户名和密码请去前端页面自行注册,或者手动去数据库添加数据初始化。