【Go + Nuxt.js 搭建一个 BBS 系统】11. 使用Element-UI搭建后台管理系统

交流 Go语言分享 收藏
0 / 255

点击下面链接购买后可获取本课程完整源码,同时提供高大上的在线IDE开发环境,边看教程边动手。

作者简介

大猫猫,互联网公司老码农、不折腾不舒服斯基,多年千万日活服务端研发和架构经验。关注公众号查看更多技术干货:

码农俱乐部

实验介绍

实验内容

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+)。你可以使用 nvmnvm-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

服务启动成功后,打开浏览器即可预览,如下图:

后台页面框架搭建

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

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添加到路由中,同时使用irismiddleware功能实现后台登录拦截,修改后的完整代码如下:

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 前端项目目录中执行下面命令来来添加axiosjs-cookie依赖

vue add axios
npm install --save js-cookie
``` 

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

```vue
"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 语言编写的接口服务和后台页面服务即可预览效果。

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

一. 未登录状态页面

二. 登录页面

三. 登录成功页面

总结

跟着本章节的讲解,我们完成了后台管理功能的框架搭建,接下来我们就可以基于该框架逐个模块的去完善后台管理功能了。下面我们看下本章节实例完整代码目录结构:

.
├── admin
│   ├── README.md
│   ├── babel.config.js
│   ├── package-lock.json
│   ├── package.json
│   ├── postcss.config.js
│   ├── public
│   │   ├── favicon.ico
│   │   └── index.html
│   └── src
│       ├── App.vue
│       ├── assets
│       │   └── logo.png
│       ├── components
│       │   └── HelloWorld.vue
│       ├── element-variables.scss
│       ├── main.js
│       ├── plugins
│       │   ├── axios.js
│       │   └── element.js
│       ├── router
│       │   └── index.js
│       ├── store
│       │   └── index.js
│       └── views
│           ├── About.vue
│           ├── Home.vue
│           └── Login.vue
└── server
    ├── admin_user_controller.go
    ├── comment_controller.go
    ├── comment_model.go
    ├── comment_service.go
    ├── go.mod
    ├── go.sum
    ├── main.go
    ├── topic_controller.go
    ├── topic_model.go
    ├── topic_service.go
    ├── user_controller.go
    ├── user_model.go
    └── user_service.go

文章转载请注明出处,原文链接:https://mlog.club/topic/653