Skip to content

steveoyung/microservice-ops-ui

This branch is 4 commits ahead of lixiawss/microservice-ops-ui:master.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Jun 5, 2018
37a17b8 · Jun 5, 2018

History

82 Commits
Nov 27, 2017
Nov 27, 2017
Jan 5, 2018
Feb 14, 2018
Jun 5, 2018
Jan 2, 2018
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Feb 14, 2018
Feb 14, 2018
Nov 27, 2017
Nov 27, 2017
Nov 27, 2017
Dec 12, 2017
Nov 27, 2017
Nov 27, 2017
Mar 27, 2018
Nov 27, 2017

Repository files navigation

准备工作

  • node.js环境(npm包管理器)
  • vue-cli 脚手架构建工具
  • cnpm npm的淘宝镜像

开发运行

   
    # 安装依赖
    npm install
    //or # 建议不要用cnpm  安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
    npm install --registry=https://registry.npm.taobao.org

    # 本地开发 开启服务
    npm run dev

浏览器访问 http://localhost:9527/index

发布

开发环境 npm run dev
    
    # 发布测试环境 带webpack ananalyzer
    npm run build:sit-preview

    # 构建生成环境
    npm run build:prod

部署nginx配置参考

  location / {
        # 指向我们打包后上传的前端文件
        root /opt/nginx/dist;
        index index.html;
    }
    location /jwt/ {
        # 转发请求到后端服务网关
        proxy_pass http://127.0.0.1:8765/jwt/;
    }
    location /api/ {
        proxy_pass http://127.0.0.1:8765/api/;
     }

目录结构

├── build                      // 构建相关  
├── config                     // 配置相关
├── src                        // 源代码
│   ├── api                    // 所有请求---------------------所有的js-data方法都放到api中 
│   ├── assets                 // 主题 字体等静态资源
│   ├── components             // 全局公用组件
│   ├── directive              // 全局指令
│   ├── filtres                // 全局filter
│   ├── mock                   // mock数据
│   ├── router                 // 路由
│   ├── store                  // 全局store管理-----------------Vuex 的仓库 管理所有组件状态
│   ├── styles                 // 全局样式
│   ├── utils                  // 全局公用方法
│   ├── view                   // view        ------------------ 页面 每个view 对应一个api 文件
│   ├── App.vue                // 入口页面
│   └── main.js                // 入口 加载组件 初始化等
├── static                     // 第三方不打包资源
│   ├── jquery
│   └── Tinymce                // 富文本
├── .babelrc                   // babel-loader 配置
├── eslintrc.js                // eslint 配置项
├── .gitignore                 // git 忽略项
├── favicon.ico                // favicon图标
├── index.html                 // html模板
└── package.json               // package.json

状态管理

后台只有user和app配置相关状态使用vuex存在全局,其它数据都由每个业务页面自己管理。

1.api 和 views

根据业务模块来划分views,并且将views 和 api 两个模块一一对应,方便维护

2.components

放置的都是全局公用的一些组件,如上传组件,富文本等等。一些页面级的组件建议还是放在各自views文件下

3.store

使用vuex来存储公共的data
业务之间的耦合度是高的时候使用
vuex来统一管理的,如登录token,用户信息(角色,权限),或者是一些全局个人偏好设置等

4.webpack

vue-cli为基础模板构建

5.jQuery

会经常用到一些第三方插件,但有些插件是不得不依赖jquery的,如市面上好的富文本基都是依赖jquery的,所以干脆就直接引入到项目中省事

6.alias

在build下面
webpack.base.cof.js 指定 绝对路径

7.eslint

eslint+vscode来写vue 代码语法格式自动检测

vscode 插件地址 varHarrie/varharrie.github.io#10

8.封装axios

我们经常遇到一些线上的bug,但测试环境很难模拟。其实可以通过简单的配置就可以在本地调试线上环境。

ajax 发送和接受数据的时候拦截 进行一些操作 如401,404 等等

9.多环境

npm run dev 
npm run build:prod

package.json中放的就是打包的信息
  "scripts": {
		"dev": "node build/dev-server.js",
		"build:prod": "cross-env NODE_ENV=production node build/build.js",
		"build:sit": "cross-env NODE_ENV=sit node build/build.js",
		"build:sit-preview": "cross-env NODE_ENV=sit npm_config_preview=true  npm_config_report=true node build/build.js",
		"lint": "eslint --ext .js,.vue src"
	  },

	  代码里自行判断,想干就干啥
	  var env = process.env.NODE_ENV === 'production' ? config.build.prodEnv : config.build.sitEnv
	  
	  新版的vue-cli也内置了webpack-bundle-analyzer 一个模块分析的东西,相当的好用。使用方法也很简单,和之前一样封装一个npm script 就可以。

	  //package.json
		"build:sit-preview": "NODE_ENV=sit npm_config_preview=true  npm_config_report=true node build/build.js"

		//之后通过process.env.npm_config_report来判断是否来启用webpack-bundle-analyzer 
		var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
		webpackConfig.plugins.push(new BundleAnalyzerPlugin())
		
		如webpack.prod.conf.js
		
		webpack-bundle-analyzer这个插件还是很有用的,对后期的代码优化什么的,最重要的是它够装逼~

10.前后端交互

webpack-dev-server的proxy来解决,开发环境用nginx反代理一下就好了
config index.js  dev 代理配置  如果是生产环境的话 直接用Nginx代理

后端mock api 生成好文档, 前端 对接接口的。 文档生成器swagger

前端 用 mock  server 或者使用mockjs+rap也是很方便的。 不久前出的easy-mock也相当的不错,还能结合swagger。

11.iconfont

element-ui 默认的icon不是很多 http://iconfont.cn 阿里图标神奇

12.router-view

computed: { key() { return this.$route.name !== undefined? this.$route.name + +new Date(): this.$route + +new Date() } }

创建和编辑的页面使用的是同一个component,默认情况下当这两个页面切换时并不会触发vue的created或者mounted钩子

watch $route的变化来做处理,但其实说真的还是蛮麻烦的。 简单的做法是router-view上加上一个唯一的key,来保证路由切换时都会重新渲染触发钩子了

13. 权限

不同的权限对应着不同的路由,同时侧边栏也需根据不同的权限,异步生成。这

设计思路
登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),
	  前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
权限验证:通过token获取用户的role,动态根据用户的role算出其相应有权限的路由,通过router.addRoutes动态挂载路由

数据和操作都是通过vuex全局管理控制的

a.登录篇

click事件触发登录操作

	this.$store.dispatch('LoginByEmail', this.loginForm).then(() => {
	  this.$router.push({ path: '/' }); //登录成功之后重定向到首页
	}).catch(err => {
	  this.$message.error(err); //登录失败提示错误
	});
	
	action

LoginByEmail({ commit }, userInfo) {
  const email = userInfo.email.trim();
  return new Promise((resolve, reject) => {
	loginByEmail(email, userInfo.password).then(response => {
	  const data = response.data;
	  Cookies.set('Token', response.data.token); //登录成功后将token存储在cookie之中
	  commit('SET_TOKEN', data.token);
	  commit('SET_EMAIL', email);
	  resolve();
	}).catch(error => {
	  reject(error);
	});
  });
}

登录成功后,服务端会返回一个token(该token的是一个能唯一标示用户身份的一个key),之后我们将token存储在本地cookie之中,这样下次打开页面或者刷新页面的时候能记住用户的登录住在那台,不用再去登录页面重新登录了

获取用户信息 用户登录成功之后,我们会在全局钩子router.beforeEach中拦截路由,判断是否已获得token,在获得token之后我们就要去获取用户的基本信息了

//router.beforeEach if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 store.dispatch('GetInfo').then(res => { // 拉取user_info const roles = res.data.role; next();//resolve 钩子 })

b.权限篇

权限控制的主体思路
前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登录之后,通过token获取用户的role,动态根据用户的role算出其相应有权限的路由,再通过router.addRoutes动态挂载路由,但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是绝对安全的,后端的权限验证是逃不掉的
不同权限的用户显示不同的侧边栏和能进入不同的页面(也做了少许按钮级别的权限控制),
后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求不管是get还是post都会让前端在请求header里面携带用户的token,后端会根据该token来验证用户是否有权限执行该操作。

router.addRoutes 动态挂载路由表

1.创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。
2.当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
3.调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。
4.使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。


// router.js
import Vue from 'vue';
import Router from 'vue-router';

import Login from '../views/login/';
const dashboard = resolve => require(['../views/dashboard/index'], resolve);
//使用了vue-routerd的[Lazy Loading Routes
](https://router.vuejs.org/en/advanced/lazy-loading.html)

//所有权限通用路由表 
//如首页和登录页和一些不用权限的公用页面
export const constantRouterMap = [
  { path: '/login', component: Login, hidden: true //hidden为自定义属性,侧边栏那章会纤细解释},
  {
	path: '/',
	component: Layout,
	redirect: '/dashboard',
	name: '首页',
	children: [{ path: 'dashboard', component: dashboard }]
  },
]

//实例化vue的时候只挂载constantRouter
export default new Router({
  routes: constantRouterMap
});

//异步挂载的路由
//动态需要根据权限加载的路由表 
export const asyncRouterMap = [
  {
	path: '/permission',
	component: Layout,
	redirect: '/permission/index',
	name: '权限测试',
	meta: { role: ['admin','super_editor'] }, //页面需要的权限
	children: [
	{ 
		path: 'index',
		component: Permission,
		name: '权限测试页',
		meta: { role: ['admin','super_editor'] }  //页面需要的权限
	}]
  },
  { path: '*', redirect: '/404', hidden: true }
];

14 vue 实例

ivew 一国人个人写的框架,美观度和交互性都不错,有种介于Element和Ant之间的感觉,之前和element团队小小的撕了一下,有兴趣的自己去围观吧,框架还是很不做的,一个人能做出这样,也是很不容易的。作者公开信件
vue-admin 也是一个不错的选择,代码写的和不错,官方也出了一个admin的架子,也很值得借鉴
vue-material 一个material design vue框架库
vuetify 又是一个material design vue框架库
Keen-UI 又又是一个material design vue框架库
CoreUI-Free-Bootstrap-Admin-Template 和以前的Bootstrap一样,搭好了一个完整的架子,大家可以进行二次拓展,它有vue,react,angular多个版本

基于Element的动态换肤

a.左侧栏 导航栏的问题, 侧边栏是根据 router.js 配置的路由并且根据权限动态生成的, 如果路由可能会有多层嵌套,很多人反馈自己的侧边栏会有三级,甚至还有五级的。所以重构了一下侧边栏,使用了递归组件,这样不管你多少级,都能愉快的显示了

		  <router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
			<el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
			  <icon-svg v-if='item.icon' :icon-class="item.icon"></icon-svg><span>{{item.children[0].name}}</span>
			</el-menu-item>
		  </router-link>

		  <el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
			<template slot="title">
			  <icon-svg v-if='item.icon' :icon-class="item.icon"></icon-svg><span>{{item.name}}</span>
			</template>
			<template v-for="child in item.children" v-if='!child.hidden'>

			  <sidebar-item class='nest-menu' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item>

			  <router-link v-else :to="item.path+'/'+child.path">
				<el-menu-item :index="item.path+'/'+child.path">
				  <icon-svg v-if='child.icon' :icon-class="child.icon"></icon-svg><span>{{child.name}}</span>
				</el-menu-item>
			  </router-link>

			</template>

		  </el-submenu>

		</template>
	  </div>
	</template>

	<script>
	export default {
	  name: 'SidebarItem',
	  props: {
		routes: {
		  type: Array
		}
	  }
	}
	</script>

侧边栏高亮问题: :default-active="$route.path" 将default-active一直指向当前路由就可以了,就是这么简单。

监听侧边栏每个link 的 click事件,每次点击都给router push 一个不一样的query 来确保会重新刷新view。 点击侧边栏 刷新当前路由 clickLink(path) { this.$router.push({ path, query: { t: +new Date() //保证每次点击路由的query项都是不一样的,确保会重新刷新view } }) }

router-view 加上一个特定唯一的 key,如 <router-view :key="$route.path"></router-view>

Table

Table 拖拽排序
import Sortable from 'sortablejs'
let el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
let sortable = Sortable.create(el)
在table mounted之后申明Sortable.create(el) table的每行tr就可以随意拖拽了,麻烦的目前我们的排序都是基于dom的,我们的数据层list并没有随之改变。所以我们就要手动的来管理我们的列表。

this.sortable = Sortable.create(el, {
  onEnd: evt => { //监听end事件 手动维护列表
	const tempIndex = this.newList.splice(evt.oldIndex, 1)[0];
	this.newList.splice(evt.newIndex, 0, tempIndex);
  }
});

Table 内联编辑
<el-table-column min-width="300px" label="标题">
  <template scope="scope">
	<el-input v-show="scope.row.edit" size="small" v-model="scope.row.title"></el-input>
	<span v-show="!scope.row.edit">{{ scope.row.title }}</span>
  </template>
</el-table-column>
<el-table-column align="center" label="编辑" width="120">
  <template scope="scope">
	<el-button v-show='!scope.row.edit' type="primary" @click='scope.row.edit=true' size="small" icon="edit">编辑</el-button>
	<el-button v-show='scope.row.edit' type="success" @click='scope.row.edit=false' size="small" icon="check">完成</el-button>
  </template>
</el-table-column>

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • CSS 44.0%
  • Vue 42.4%
  • JavaScript 13.5%
  • HTML 0.1%