0.0.1 • Published 3 years ago

@loushangvue/layout v0.0.1

Weekly downloads
45
License
MIT
Repository
-
Last release
3 years ago

插件基于vue-element-admin项目封装,推荐基于vue-element-admin开发的项目使用本插件,项目目录

├── build                      # 构建相关
├── mock                       # 项目mock 模拟数据
├── plop-templates             # 基本模板
├── public                     # 静态资源
│   │── favicon.ico            # favicon图标
│   └── index.html             # html模板
├── src                        # 源代码
│   ├── api                    # 所有请求
│   ├── assets                 # 主题 字体等静态资源
│   ├── components             # 全局公用组件
│   ├── directive              # 全局指令
│   ├── filters                # 全局 filter
│   ├── icons                  # 项目所有 svg icons
│   ├── lang                   # 国际化 language
│   ├── layout                 # ********************插件基于layout封装**************************#
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局样式
│   ├── utils                  # 全局公用方法
│   ├── views                  # views 所有页面
│   ├── App.vue                # 入口页面
│   ├── main.js                # 入口文件 加载组件 初始化等
│   └── permission.js          # 权限管理
├── tests                      # 测试
├── .env.xxx                   # 环境变量配置
├── .eslintrc.js               # eslint 配置项
├── .babelrc                   # babel-loader 配置
├── .travis.yml                # 自动化CI配置
├── vue.config.js              # vue-cli 配置
├── postcss.config.js          # postcss 配置
└── package.json               # package.json

注意:删除项目中的layout文件夹,取消styles/sidebar.scss样式文件引入,防止样式冲突,其他如提示信息根据具体样式,自行更改

快速开始

npm install @vuelement/layout --save

项目配置

main

// main.js文件

// 引入插件样式
import '@vuelement/layout/src/styles/index.scss'
// 可以通过init来初始化项目,init封装了keycloak
import { init } from '@vuelement/layout'

init(Vue, {
  el: '#app',
  router,
  store,
  i18n,
  render: h => h(App)
}).catch(err => console.error(err))

// 如果要引入项目中的面包屑Breadcrumb组件,可以通过bindComponent方法来使用
import { bindComponent } from '@vuelement/layout'
import Breadcrumb from '@/components/Breadcrumb'

// 1.通过init方法来使用
init(Vue, {
  el: '#app',
  router,
  store,
  i18n,
  render: h => h(App)
}).then(ret => bindComponent(Vue, Breadcrumb))
.catch(err => console.error(err))
// 2.在vue初始化后,调用 bindComponent(Vue, Breadcrumb)

router

// router/index.js
import Layout from '@vuelement/layout/src/layout/Layout.vue'
[{
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  },
  {
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: { title: 'Example', icon: 'dashboard' },
    children: [
      {
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: { title: 'Table' }
      },
      {
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: { title: 'Tree' }
      }
    ]
}]

注意:1. 图标icon仅支持svg格式,并且要在icons/icon文件夹中一一对应;

store

// store/index.js
import getters from './getters'
import layoutStore from '@vuelement/layout/src/store' // 引入插件的store模块
import { bindRoutes } from '@vuelement/layout'

Vue.use(Vuex)
const { headerApp, headerUser, headerGetters } = { ...layoutStore } // 引入插件的store模块
bindRoutes("authRoutes") // 引入权限过滤之后的路由数组,authRoutes为getters中对应的名称,用户自行更改

// 1.第一种写法
const store = new Vuex.Store({
  modules: {
    headerApp,
    headerUser,
    ...   // 添加合并模块
  },
  getters: Object.assign(getters, headerGetters) // 添加合并模块
})

// 第二种写法
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})
// 添加合并模块
Object.assign(modules, { headerApp, headerUser})

const store = new Vuex.Store({
  modules,
  getters: Object.assign(getters, headerGetters) // 添加合并模块
})
// getters.js
const getters = {
  ...,
  authRoutes: state => state.xxxx.authRoutes, // 用户自行更改
  ...
}
export default getters

配置文件

// public/config/config.json
{ 
  // sidebar所显示的系统名称
  "systemName": "事件总线",
  "baseUrl": "http://yzh.inspur.com/",
    
  // 当前环境
  "environment": "prod",
  // 测试开发环境
  "dev": {
        "clientId": "yunzhou", // keycloak clientId
        "realm": "picp", // keycloak realm
        "consoleUrl": "http://service-test.yzh.inspur.com/apistore/apis",  // 获取链接列表的地址
   },
   // 生产环境
   "prod": {
        "clientId": "yunzhou",
        "realm": "picp",
        "consoleUrl": "http://service.yzh.inspur.com/apistore/apis", // 获取链接列表的地址
    },
   // xxx环境
   "xxx": {
         ...: ...
    }
  
    // 其他配置
    ...,
    "layout": {
      "sidebar": false,
      "route": ""
    }
}

加载配置文件

// main.js
import {init, loadConfig } from '@vuelement/layout'

// 1.可以通过init方法来加载配置,默认配置文件放在public/config/config.json,也可以指定路径path
init(Vue, options, path) // Vue, options用来初始化Vue

页面缓存问题

<!-- layout/components/AppMain.vue 文件-->

<section class="app-main" :style="{'top': hasNavbar ? '56px' : '0'}">
    <transition name="fade-transform" mode="out-in">
      <keep-alive>
        <!-- 需要缓存的视图组件 -->
        <router-view v-if="$route.meta.noCache === false"></router-view>
      </keep-alive>
    </transition>
    <transition name="fade-transform" mode="out-in">
      <!-- 不需要缓存的视图组件 -->
      <router-view v-if="$route.meta.noCache === true || $route.meta.noCache === undefined"></router-view>
    </transition>
</section>

子路由的侧边栏渲染问题

通过在子路由元数据中添加activeMenu字段来控制当前路由未在侧边栏显示时,侧边栏选中的路由地址,'activepath'代表侧边栏选中的路由地址

meta: {
  activeMenu: 'activepath'
}

token

token存放由Session Storage转为cookie, key值改为前缀+inspur_token,操作token的方法可以通过插件引入

import { CookieService } from '@vuelement/layout'

token相关更新

token的相关操作不在通过引入单个函数来进行,改为提供一个CookieService的实例对象CookieService来保存和操作token的相关信息(当前token仍然存在cookie中)

export class CookieService {
    static TokenKey = 'inspur_token'
    static RefreshToken = 'refresh_token'
    static AuthKey = 'auth'
    static IsLoginValid = 'isLoginValid'
    static OldTokenKey = 'token'
    prefix = ''
    domain = ''
    auth = null

    constructor() {
        this.domain = CookieService.getDomain()
    }

    static getDomain () {
        const hostname = window.location.hostname
        if (validDomainName(hostname)) {
            return '.' + hostname.split('.').slice(-2).join('.')
        } else {
            return hostname
        }
    }

    // 设置前缀
    setPrefix (prefix) {
        this.prefix = !!prefix ? prefix + '_' : ''
    }

    // 获取前缀
    getPrefix () {
        return this.prefix
    }

    // 获取token
    getToken () {
        return Cookies.get(this.prefix + CookieService.TokenKey)
    }

    // 设置token
    setToken (token) {
        sessionStorage.setItem(CookieService.OldTokenKey, token)
        Cookies.set(this.prefix + CookieService.TokenKey, token, { domain: this.domain })
    }

    // 移除token
    removeToken () {
        sessionStorage.removeItem(CookieService.OldTokenKey)
        Cookies.remove(this.prefix + CookieService.TokenKey, { domain: this.domain })
    }

    // 设置refresh_token
    setRefreshToken (refreshToken) {
        Cookies.set(this.prefix + CookieService.RefreshToken, refreshToken, { domain: this.domain })
    }

    // 获取refresh_token
    getRefreshToken () {
        return Cookies.get(this.prefix + CookieService.RefreshToken)
    }

    // 移除refresh_token
    removeRefreshToken () {
        Cookies.remove(this.prefix + CookieService.RefreshToken, { domain: this.domain })
    }

    // 设置IsLoginValid
    setIsLoginValid (isLoginValid) {
        Cookies.set(this.prefix + CookieService.IsLoginValid, isLoginValid, { domain: this.domain })
    }

    // 获取IsLoginValid
    getIsLoginValid () {
        return Cookies.get(this.prefix + CookieService.IsLoginValid)
    }

    // 移除IsLoginValid
    removeIsLoginValid () {
        Cookies.remove(this.prefix + CookieService.IsLoginValid, { domain: this.domain })
    }

    // 设置auth
    setAuth(tokenInfo) {
        this.auth = {
            inspur_token: tokenInfo.token,
            idToken: tokenInfo.idToken,
            refreshToken: tokenInfo.refreshToken
        }
    }

    // 获取auth
    getAuth() {
        return this.auth
    }

    // 清除cookie中的信息
    clear () {
        this.removeToken()
        this.removeRefreshToken()
        this.removeIsLoginValid()
    }
}

CookieService使用

ConfigService、CookieService均可通过import { ConfigService, CookieService } from '@vuelement/layout'引入

import { ConfigService, CookieService } from '@vuelement/layout'

const name = ConfigService.getSystemName()
console.log(name)

const token = cookieService.getToken()
config.headers['Authorization'] = 'Bearer ' + token

也可以通过import { ServiceFactory } from '@vuelement/layout'引入工厂类来创建服务

import { ServiceFactory } from '@vuelement/layout'

// 服务创建使用单例模式,在各处创建的服务实例为同一个
const CookieService = ServiceFactory.getConfigService()
const cookieService = ServiceFactory.getCookieService()

解决在其他应用退出后,vue项目没跳转到登陆页面的问题

在request.js中,引入CookieService,在请求配置时调用CookieService中的checkToken方法,并将原有设置header的代码注释掉。

例:

import { CookieService } from '@vuelement/layout'

service.interceptors.request.use(
  config => {
    // 1、检查token是否存在,如果存在,方法内部会添加header;如果不存在,方法内部的处理是跳转到登录页
    CookieService.checkToken(config)
    
    // 2、原有设置token的方法可注释掉
    //if (store.getters.layoutToken) {
      //config.headers['Authorization'] = 'Bearer ' + store.getters.layoutToken
    //}

    return config
  }
)

不需要登录或者登录失效仍然能访问的接口需要在request中添加"auth: false"属性