# 介绍

基于 Vue.js 2.x 和 ant-design-vue 组件库且风格统一的用于快速构建中后台系统的前端脚手架。在线效果预览 (opens new window)

# 兼容性

Chrome Firefox Safari Edge IE11

# 功能列表

模块 功能介绍 进度
动态路由 后端返回菜单结构前端渲染,支持一键切换到静态路由
静态路由 前端写死的静态路由,支持一键切换到动态路由
菜单 支持展开收起、黑白色主题切换、支持左边和顶部两种布局,应对业务的多种风格需要
布局 内置了不同的布局,新增布局可应对大部分场景
权限控制 支持控制菜单显隐、按钮显隐,前端对数据结构有一定要求,可约定小改,权限控制如鱼得水
通用页面 内置了 403、404、500 页面,让系统更人性化
错误捕获 可一键开启和关闭错误日志,错误排查和定位可视化管理
请求 基于 axios,支持文件上传、下载、自动检测 header、自定识别 mockUrl 与 url、支持多域名单域名,覆盖了绝大部分作为一个请求的使用场景
工具库 内置了变量类型判断、uuid 生成、常用手机号、身份证、密码校验等其他常用函数,全局可调用,方便快捷
自定义扩展 包含对 Vue.prototype 的扩展、自定义指令、组件库等,全局添加指令和功能更加统一
打包 支持私有云打包(内网环境)和非私有云打包,一键开启和关闭,无需关注繁杂的 webpack 配置
mock 服务 内置了 mocker-api 和 mockjs2,应对不同的使用场景,且做了兼容,只需要一种 mock 写法即可,不再因为迟迟没提供接口而等待
eslint 内置了比较严格和完整的 eslint 规则,保证了代码规范,协同开发更高效
store 内置了 store,可直接获取到用户信息、全局配置等,拿来即用
用户中心 内置了登录、注册、找回密码页面,简单的对接后端接口即可完成账号体系的构建
多环境 内置了预览环境、演示环境、测试环境、正式环境打包配置,可满足多环境打包部署要求
模板库 提供了一套模板库来极速创建页面,开发效率显著提升
单元测试 集成完整的测试用例,让脚手架更健壮 feature

# 项目结构

.
├── README.md              # 搞事之前请先读我
├── babel.config.js
├── config                 # 项目配置
│   ├── config.js          # 提供给 vue.config.js 使用的自定义配置,抽离了相对独立部分
│   ├── project.config.js  # 提供给 Node.js 部署包的配置项
│   └── theme.plugin.js    # 主题配置,自定义主题配置的时候会用到
├── jsconfig.json
├── mock                   # mock 数据服务
│   ├── index.js
│   ├── modules            # 所有的 mock 数据定义在这里
│   └── utils.js           # 内置处理 mock 数据的工具函数
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
├── server                # Node.js 部署包中间件
│   └── index.js
├── src                   # 源码目录
│   ├── api               # 请求
│   ├── app.less          # 公共样式
│   ├── app.vue           # 根组件
│   ├── assets            # 静态资源
│   ├── components        # 组件
│   ├── extends           # 扩展,包含对 Vue.prototype 的扩展、自定义指令、组件库等
│   ├── layouts           # 布局
│   ├── main.js           # 入口文件
│   ├── router            # 路由配置
│   ├── store             # store 配置
│   ├── utils             # 工具库
│   └── views             # 页面
├── tests                 # 单元测试
│   └── unit
├── .env                  # 通用的环境变量配置
├── .env.development      # 开发环境变量配置
├── .env.production       # 正式环境变量配置
├── vue.config.js         # vue-cli 提供的配置文件
└── yarn.lock

# 常用命令

推荐使用 yarn 来管理依赖。

# 安装
yarn install

# 启动开发服务
yarn run dev

# 生产环境打包
yarn run build

# 测试环境打包
yarn run build:test

# 项目配置

# 环境变量

脚手架约定了一份环境变量配置,用来实现对各个功能的切换和支持,各个变量的作用如下:

# BASE_URL

路由地址前缀,vue-cli 提供的环境变量,一般不需要改,效果同 vue.config.js 中的 publicPath,若有对 publicPath 的修改需求都强烈建议来修改 BASE_URL 变量。

# VUE_APP_ROUTER_BASE_URL

路由地址前缀,用于 Vue Router 的 base 配置,同时也用于 location.href 等强刷页面跳转时的路径拼接。

# VUE_APP_API_PREFIX

请求接口的 url 前缀,跟随后端开发提供的来改,若不需要前缀,可设置为「/」。

# VUE_APP_LS_PREFIX

脚手架内置了 vue-ls 这个库来操作 storage,此变量是 namespace 字段配置。

# VUE_APP_PRIVATE

是否属于私有云部署,就是是否部署到内网,如果项目本身使用到的组件很少,也可以设置为 true,以此来减少没有必要的 cdn 资源加载,如果使用了自定义主题功能,一定要设置成 true,否则自定义主题打包后不生效。

# VUE_APP_USE_MOCKER

内置 mocker-api(可以看到网络请求)和 mockjs2(看不到网络请求)两个 mock 服务,设置为 true 标识使用 mocker-api,设置成 false 使用 mockjs2

# VUE_APP_BUILD_ENV

打包环境,部署到不同生产环境的区分。默认是 production,即生产环境,已在 package.json 中内置了预览环境、测试环境、演示环境和生产环境的配置,可根据业务自行添加更多的环境配置。

# VUE_APP_THEME_COLOR

主题色,默认是跟随 ant-design-vue 的主题色 #1890ff,自定义主题色时,主色值只需要修改这一个地方即可,然后在 config/config.s 下的 modifyVars 字段中配置自定义的主题色变量。跟自定义主题色相关的改动,一般也只需要改动这两个地方即可。

# VUE_APP_BUILD_REPORT

是否开启打包分析,当发现打包后的 dist 资源文件很大时,设置为 true,每次打包完毕后会自动打开本次打包的可视化分析页面。

提示

除了上面的环境变量,脚手架基于 vue-cli 提供的 vue.config.js 配置能力,封装了对打包优化、主题配置和接口代理配置功能。

# 打包优化

VUE_APP_PRIVATE 设置为 false,打包时就会自动忽略常用的第三方库而使用 cdn 加载,减少了打包后的资源大小,提高了打包速度。

VUE_APP_PRIVATE 设置为 true,会将所有资源都打包到 dist,同时对第三方库进行了简单的分割以减小单个文件的大小。

# 主题配置

config/theme.config.js 中封装了自定义主题色功能的支持,配合 VUE_APP_THEME_COLOR 变量使用。

# 接口代理配置

config/theme.config.jsproxy 字段中配置接口请求地址。

{
  // 系统默认的前缀配置
  [apiPrefix]: {
    target: 'http://127.0.0.1:7001',
    ws: false,
    changeOrigin: true
  },
  // 用户中心接口前缀配置
  '^/account': {
    target: 'http://account.example.com',
    ws: false,
    changeOrigin: true,
    pathRewrite: {
      '^/account': ''
    }
  }
}

# 定制

项目启动后,在右边会始终有一个「定制」按钮,展开后的完整配置项如下:

在这里可以设置菜单风格、主题色、导航模式快速预览效果,确定好最终的配置后,可在 src/store/modules/app.js 中同步修改,以下是支持的完整配置项:

{
  /**
   * logo 的图片地址,默认是 @/assets/logo.svg
   */
  logo,
  /**
   * logo 旁边的 slogan
   */
  slogan: '中后台前端脚手架',
  /**
   * 菜单主题,默认为深色主题
   * dark:深色主题
   * light:亮色主题
   */
  menuTheme: Vue.ls.get(types.MENU_THEME, 'light'),
  /**
   * 菜单模式
   * vertical:垂直,菜单是弹出形式
   * inline:内嵌菜单
   * horizontal:水平,顶部导航菜单
   */
  menuMode: 'inline',
  /**
   * 布局模式
   * side:左右布局,导航菜单在左边
   * top:上下布局,导航菜单在顶部
   */
  layout: Vue.ls.get(types.LAYOUT, 'side'),
  /**
   * 默认的主题色,跟随 ant-design-vue 库
   */
  themeColor: Vue.ls.get(types.THEME_COLOR, process.env.VUE_APP_THEME_COLOR),
  /**
   * 路由模式
   * static:静态。如果是静态模式,路由的增删改在 src/router/route-static.js 中完成
   * dynamic:动态。如果是动态模式,路由的增删改在 src/router/route-dynamic.js 中完成,每次
   * 在 routeComponents 中新增一个路由页面之后,再去更新后端的路由菜单
   */
  routeMode: 'dynamic',
  /**
   * 菜单的展开收起状态
   */
  menuCollapsed: Vue.ls.get(types.MENU_COLLAPSED, false),
  /**
   * 导航模式
   * breadcrumb:面包屑导航
   * tab:标签导航
   */
  navMode: Vue.ls.get(types.NAV_MODE, 'breadcrumb'),
  /**
   * 是否需要捕获错误日志,具体实现代码请移步 src/core/extends.js 里面查看
   */
  catchError: Vue.ls.get(types.CATCH_ERROR, false)
}

提示

正式环境打包上线后,这个「定制」入口会自动隐藏

# 请求

完整的请求配置项如下:

字段名 注解
url 请求地址,如果定义了 url 就优先使用,否则使用 mockUrl,默认不以斜杠开头,如果以斜杠开头就不会拼接 VUE_APP_API_PREFIX 变量
mockUrl mock 服务的请求地址,定义了就使用
method 请求方式,支持 get、post、put、delete 等
data 如果是 post,data 方式传递的参数是 json 类型,可以和下面的 params 字段一起使用
params 传递的参数是字符串类型的,拼接在 url 后面的,类似 /a/b?id=1&type=2 这种,如果是 get 请求,只能通过 params 传递参数
headerType 请求头类型,默认是 json,对应的 Content-Typeapplication/json,设置成 form 的时候,Content-Typeapplication/x-www-form-urlencoded; charset=UTF-8,设置成 uploadContent-Typemultipart/form-data,设置成 downloadheaders.resTypeblob,会自动下载文件,配合 fileName 字段自定义下载文件的名称,设置成 blob,返回文件流信息
fileName 配合 headerType 等于 download 时使用,用来自定义下载文件的名称
closeAutoTips 请求失败之后默认会抛出后端给的错误,closeAutoTips 设置为 true 可以关闭掉这个行为

请求 code 配置 (opens new window)

使用示例:

// 发起 post 请求,并传递 data 和 params 参数,然后关闭自动提示
export function queryTableList (data) {
  return request({
    // 定义了 url 就会优先使用,否则使用 mockUrl,url 不以 / 开头时,会自动拼接 VUE_APP_API_PREFIX 前缀
    url: 'table/list',
    mockUrl: '/mock/table/list',
    method: 'post',
    params,
    data,
    // 设置请求类型
    headerType: 'json',
    // 关闭自动提示
    closeAutoTips: true
  })
}
// 发起 get 请求
export function queryGetDemo (params) {
  return request({
    // 接口以 / 开头时,会自动忽略 VUE_APP_API_PREFIX 配置,这种使用场景一般会配合设置一下代理
    url: '/account/table/list',
    method: 'get',
    params
  })
}
// post 请求下载文件
export function queryDownloadDemo (data) {
  return request({
    url: 'file/download',
    method: 'post',
    // 设置为 download 进行文件下载,内部封装了文件下载动作,调用后会自动下载文件,无需再做其他处理
    headerType: 'download',
    // 指定下载的文件名称
    fileName: '前端入门到放弃.pdf',
    // 参数传递
    data
  })
}
// 获取文件流自定义处理
export function queryContentBlob (data) {
  return request({
    url: 'file/download',
    method: 'post',
    data,
    headerType: 'blob'
  })
    .then(blob => {
      // blob 文件流信息,可自定义处理
    })
}

# 使用 mock 服务

在后端还没提供接口前,前端可自定义页面需要的假数据。在 mock/modules 新增一个 select.js 并定义一个 mock 数据:

const selectData = [
  { value: '1', label: '正常' },
  { value: '2', label: '禁用' }
]
module.exports = {
  'POST /select/list': selectData
}

然后在 mock/index.js 中引入:

const select = require('./modules/select')
const data = {
  ...addPrefix(select)
}

然后在 src/api/select.js 中定义调用接口的方法:

// 获取下拉列表
export function querySelectData (params) {
  return request({
    mockUrl: '/mock/select/list',
    method: 'get',
    params
  })
}

最后在需要使用该请求的页面导入使用即可。

在 template 中使用:

<ak-select :loadData="querySelectData" />

在 JS 中使用:

querySelectData()
  .then(res => {
    this.selectData = res.data || []
  })

# 路由和菜单

路由分为静态和动态两种,静态就是前端写死路由配置,路由的控制完全由前端负责。而动态路由则是由后端接口返回一个菜单列表,前端根据约定规则解析并生成最终的路由。两者的切换只需要在 src/store/modules/app.js 中修改 routeMode 的值即可。

{
  /**
   * 路由模式
   * static:静态。如果是静态模式,路由的增删改在 src/router/route-static.js 中完成
   * dynamic:动态。如果是动态模式,路由的增删改在 src/router/route-dynamic.js 中完成,每次在 routeComponents 中新增一个路由页面之后,再去更新后端的路由菜单
   */
  routeMode: 'dynamic'
}

路由支持的配置项如下:

字段名 注解
path 访问路径,不带斜杠,相对路径,最终会组装成绝对路径
component 对应的组件,静态路由这个值是一个 import 导入的一个页面对象,动态路由是 routeComponents 里面定义的 key
title 当前页面的标题,在面包屑导航和菜单导航里面显示
icon 菜单前面的 Icon 图标,这个顶级菜单才会设置,目前支持的 Icon 是 antd 的 Icon 组件内的,如果想要自定义图标,需要引入自定义 Icon 组件
meta 路由的元信息,如果是静态路由,这里面包含了 titleiconhiddentargethrefhideChildren 这 6 个字段
hidden 设置当前路由是否在菜单导航中显示
hideChildren 设置当前路由的子路由是否在菜单导航中显示
children 子路由

一份完整的静态路由看起来像这样:

[{
  path: '/',
  // component 始终指向一个引用的 vue 页面文件
  component: LayoutBase,
  redirect: '/dashboard/workplace',
  children: [{
    path: 'dashboard',
    component: LayoutBlock,
    title: '首页',
    icon: 'dashboard',
    children: [{
      path: 'workplace',
      component: () => import('@/views/dashboard/workspace.vue'),
      title: '工作台'
    }]
  }, {
    path: 'list',
    component: () => import('@/views/table/list.vue'),
    title: '列表',
    icon: 'list'
  }]
}, {
  path: '/user',
  component: LayoutUser,
  title: '用户中心',
  icon: 'user',
  children: []
}]

一份相对完整的动态路由看起来像这样:

[{
  id: 1,
  parentId: 0,
  sort: 1,
  path: '/',
  // component 始终指向一个字符串,这个字符串在 src/router/route-dynamic.js 文件的 routeComponents 中定义
  component: 'LayoutBase',
  redirect: '/dashboard/workplace',
  children: [{
    id: 2,
    parentId: 1,
    sort: 1,
    path: 'dashboard',
    component: 'LayoutBlock',
    title: '首页',
    icon: 'dashboard',
    children: [{
      id: 3,
      parentId: 2,
      sort: 1,
      path: 'workplace',
      component: 'workspace',
      title: '工作台',
      icon: null
    }]
  }, {
    id: 4,
    parentId: 1,
    sort: 1,
    path: 'list',
    component: 'tableList',
    title: '列表',
    icon: 'list'
  }]
}, {
  id: 5,
  parentId: 0,
  sort: 1,
  path: '/user',
  component: 'LayoutUser',
  title: '用户中心',
  icon: 'user',
  children: []
}]

实际业务中,可能还有 hiddentargetcreatedAt 等字段,可视情况自行添加。

# 权限

脚手架支持按钮级别的权限控制,只需要在当前登录的用户信息(src/store/modules/user.js)中返回对应的权限点即可。例如当前用户拥有列表页面的编辑、添加和删除权限,那对应的权限点配置如下:

{
  permissions: ['listAdd', 'listDelete', 'listEdit']
}

在列表页面控制操作按钮的显示可以这么使用:

 <a-button v-action.listAdd>添加</a-button>
 <a-button v-action.listDelete>删除</a-button>

也可以使用 $auth 方法来控制:

if (this.$auth('listEdit')) {
  // 进行编辑操作
}

# 样式

脚手架内置了样式库。样式库使用文档

脚手架也将 ant-design-vue 的主题变量添加到了全局,你可以在任意的 less 中使用其中的变量,就像这样:

.ant-card {
  color: @main-color !important;
}
.C-FAIL {
  color: @error-color;
}

完整的 less 变量 (opens new window)

同时,脚手架为了方便扩展也自定义了一部分 less 变量,可以在 src/components/index.less 中找到。

# 使用模板库

脚手架开发了一套配套的模板库,可以方便的创建各种组件和页面,极大提高开发效率。模板库使用文档

# 发布

脚手架内置了生产环境、测试环境、演示环境和预览环境,命令都是配置好的,可直接执行对应的命令进行打包:

# 生产环境打包
yarn run build

# 测试环境打包
yarn run build:test

# 演示环境打包
yarn run build:demo

# 预览环境打包
yarn run build:preview

提示

预览环境是脚手架自带的环境,会将「定制」功能打包出来。脚手架视觉效果预览 (opens new window)

# 部署

# 部署到后端服务器

直接将打包的 dist 静态资源复制到对应后端的静态资源目录即可。例如 Java 做后端的话可以放到 src/main/resources/static 下,或者放到 src/main/webapp 下。

# 部署到 nginx 服务器

直接将打包的 dist 静态资源复制到 nginx 指定的静态资源目录即可。

# 使用 wy app 部署

wy 命令是 wy-cli 提供的命令。wy-cli 使用文档

需要先执行 yarn run build 打包出来 dist 静态资源包,然后执行 wy tar 打包成一个 Node.js 部署包。

然后将部署包上传到后端服务器,解压后在根目录执行 wy app 命令一键启动即可。

wy app 启动使用的配置文件参见 config/project.config.js

# GitHub

脚手架 GitHub 地址 (opens new window)

# 常见问题

# 如何添加临时的动态路由?

1、先找到 src/api/user.js 里面的 getMenus 方法

2、使用 .then 修改返回值

// 获取菜单树
export function getMenus (data) {
  return request({
    method: 'post',
    url: '',
    mockUrl: '/mock/menus',
    data
  })
    // 临时添加的部分
    .then(res => {
      const data = res.data || []
      // 添加的路由一定都是挂在 / 下面的
      const route = data.find(item => item.path === '/') || {}
      route.children = route.children || []
      // 添加子菜单
      route.children.push({
        path: 'form',
        component: 'form',
        title: '表单',
        icon: 'form'
      })
      // 最后一定要将 res 返回去
      return res
    })
}

# 业务通用的常量数据定义在哪里?

建议定义在 src/utils/const.js 里面,例如:

export const STATUS = [{
  id: 1, name: '状态一'
}, {
  id: 1, name: '状态二'
}, {
  id: 1, name: '状态三'
}]

提示

js 里面通过 this.$const 访问,template 里面通过 $const 访问

# 上传下载文件为什么报接口 404 的错误?

上传和下载默认使用 src/api/index.js 里面定义的接口,需要改成自己的业务接口,例如:

export default {
  // 上传
  upload: `${API_PREFIX}/upload.json`,
  // 下载
  download: `${API_PREFIX}/download.json`
}
最后更新时间: 2022-09-15 21:37:51