parent
5cf96cb347
commit
4c1ecc771f
@ -0,0 +1,22 @@ |
|||||||
|
.DS_Store |
||||||
|
node_modules |
||||||
|
/dist |
||||||
|
/test |
||||||
|
|
||||||
|
# local env files |
||||||
|
.env.local |
||||||
|
.env.*.local |
||||||
|
|
||||||
|
# Log files |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.idea |
||||||
|
.vscode |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
*.sw? |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
module.exports = { |
||||||
|
title: 'jxc-admin', |
||||||
|
|
||||||
|
description: "jxc-admin后台管理系统文档", |
||||||
|
|
||||||
|
host: 'localhost', |
||||||
|
|
||||||
|
// 输出目录
|
||||||
|
dest: 'dist', |
||||||
|
|
||||||
|
// 主题配置
|
||||||
|
themeConfig: { |
||||||
|
smoothScroll: true, |
||||||
|
|
||||||
|
lastUpdated: '上次更新:', |
||||||
|
|
||||||
|
nav: [ |
||||||
|
{text: '在线预览', link: 'https://toesbieya.cn'}, |
||||||
|
{text: 'GitHub', link: 'https://github.com/toesbieya/jxc-admin'} |
||||||
|
], |
||||||
|
|
||||||
|
sidebar: [ |
||||||
|
'installation', |
||||||
|
{ |
||||||
|
title: '前端', |
||||||
|
collapsable: false, |
||||||
|
children: [ |
||||||
|
'/frontend/', |
||||||
|
'/frontend/layout', |
||||||
|
'/frontend/router', |
||||||
|
'/frontend/permission', |
||||||
|
'/frontend/new', |
||||||
|
'/frontend/deploy', |
||||||
|
{ |
||||||
|
title: '组件', |
||||||
|
collapsable: false, |
||||||
|
children: [ |
||||||
|
'/frontend/components/abstract-table', |
||||||
|
'/frontend/components/region-selector', |
||||||
|
'/frontend/components/search-form', |
||||||
|
'/frontend/components/upload-file', |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: '全局方法', |
||||||
|
collapsable: false, |
||||||
|
children: [ |
||||||
|
'/frontend/global-methods/bottom-tip', |
||||||
|
'/frontend/global-methods/guide', |
||||||
|
'/frontend/global-methods/image-viewer', |
||||||
|
'/frontend/global-methods/puzzle-verify', |
||||||
|
'/frontend/global-methods/signature', |
||||||
|
] |
||||||
|
}, |
||||||
|
'/frontend/utils', |
||||||
|
'/frontend/plugins' |
||||||
|
] |
||||||
|
}, |
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
plugins: { |
||||||
|
'@vuepress/pwa': { |
||||||
|
serviceWorker: true, |
||||||
|
updatePopup: true |
||||||
|
}, |
||||||
|
'@vuepress/back-to-top': true |
||||||
|
} |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 160 KiB |
@ -0,0 +1,9 @@ |
|||||||
|
--- |
||||||
|
home: true |
||||||
|
heroImage: https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png |
||||||
|
heroText: jxc-admin |
||||||
|
tagline: 前后端分离的简易进销存后台管理系统。 |
||||||
|
actionText: 快速上手 → |
||||||
|
actionLink: ./installation/ |
||||||
|
footer: MIT Licensed | Copyright © 2020-present toesbieya |
||||||
|
--- |
||||||
@ -0,0 +1,69 @@ |
|||||||
|
# 介绍 |
||||||
|
|
||||||
|
## 功能 |
||||||
|
|
||||||
|
``` |
||||||
|
- 首屏加载动画 |
||||||
|
|
||||||
|
- 登录 \ 注册 |
||||||
|
- 集成多种canvas背景动画 |
||||||
|
- 仿极验滑动验证 |
||||||
|
|
||||||
|
- 布局功能 |
||||||
|
- 异步路由组件loading骨架屏 |
||||||
|
- 动态侧边栏(支持多级路由嵌套) |
||||||
|
- 动态面包屑 |
||||||
|
- 多页签(支持详情页缓存) |
||||||
|
- 支持移动端 |
||||||
|
- 导航栏、侧边栏可隐藏 |
||||||
|
- 侧边栏折叠时可显示上级 |
||||||
|
|
||||||
|
- Excel导出,支持多级表头、合并行 |
||||||
|
|
||||||
|
- 错误页面 |
||||||
|
- 403 |
||||||
|
- 404 |
||||||
|
|
||||||
|
- 组件 |
||||||
|
- 用户导航 |
||||||
|
- 行政区划选择器,支持树形和选项卡型 |
||||||
|
- 封装的上传组件,整合七牛云,支持多种格式的文件预览 |
||||||
|
- 封装了antd风格的dialog,可拖拽、超出可滚动 |
||||||
|
- 手写签名 |
||||||
|
|
||||||
|
- element-ui增强 |
||||||
|
- message支持分组、进度条功能 |
||||||
|
- select重写,分别使用了popperjs的v1和v2版本 |
||||||
|
``` |
||||||
|
|
||||||
|
## 目录结构 |
||||||
|
``` |
||||||
|
├── element-ui-personal # element-ui增强 |
||||||
|
├── mock # 项目mock,需打开devServer.before的注释 |
||||||
|
├── public # 静态资源文件夹 |
||||||
|
├── src |
||||||
|
│ ├── api # 数据请求集合 |
||||||
|
│ ├── assets |
||||||
|
│ │ │── icons # 项目所有svg icons |
||||||
|
│ │ └── styles # 样式集合 |
||||||
|
│ ├── bizComponents # 公用业务组件 |
||||||
|
│ ├── components # 公用组件 |
||||||
|
│ ├── config |
||||||
|
│ │ │── index.js # 基础配置项 |
||||||
|
│ │ └── request.js # axios封装 |
||||||
|
│ ├── directive # 自定义指令集合 |
||||||
|
│ ├── filters # 自定义过滤器集合 |
||||||
|
│ ├── layout # 布局文件夹,包含了导航栏、侧边栏、多页签、全局页脚、页面 |
||||||
|
│ ├── mixins # 公用混入 |
||||||
|
│ ├── plugin # 一些插件,均使用import()来动态引入 |
||||||
|
│ ├── router |
||||||
|
│ │ │── authority # 需要鉴权的路由集合 |
||||||
|
│ │ │── constant # 不需要鉴权的路由集合 |
||||||
|
│ │ │── index.js |
||||||
|
│ │ └── util.js |
||||||
|
│ ├── store # vuex配置 |
||||||
|
│ ├── utils # 工具类 |
||||||
|
│ ├── views # 路由页面集合 |
||||||
|
│ ├── App.vue |
||||||
|
│ └── main.js |
||||||
|
``` |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
# Abstract Table |
||||||
|
|
||||||
|
<img :src="$withBase('/abstract-table.png')"> |
||||||
|
|
||||||
|
给`<el-table>`设置了一些默认属性,设置了`empty`插槽。 |
||||||
|
```html |
||||||
|
<el-table |
||||||
|
ref="table" |
||||||
|
current-row-key="id" |
||||||
|
row-key="id" |
||||||
|
highlight-current-row |
||||||
|
{...data} |
||||||
|
> |
||||||
|
{children} |
||||||
|
<Empty slot="empty"/> |
||||||
|
</el-table> |
||||||
|
``` |
||||||
|
|
||||||
|
### 引入: |
||||||
|
|
||||||
|
`@/components/AbstractTable`。 |
||||||
|
|
||||||
|
### 使用: |
||||||
|
|
||||||
|
照着官方文档,当成 `<el-table>` 用就行。 |
||||||
|
|
||||||
@ -0,0 +1,46 @@ |
|||||||
|
# 行政区划选择器 |
||||||
|
|
||||||
|
目前分 `树型` 和 `选项卡型` 两种,由于 `树型` 不完善,所以这里只介绍 `选项卡型`,有兴趣的可以查看源码`@/components/RegionSelector/Tree`。 |
||||||
|
|
||||||
|
<img :src="$withBase('/region-selector_tab.png')"> |
||||||
|
|
||||||
|
由于行政区划数据在本项目中是公共数据,所以使用了`vuex`来存放,如果不想用`vuex`但又想拿到别的项目里用,可以自己改造一下。 |
||||||
|
|
||||||
|
### 引入: |
||||||
|
|
||||||
|
`@/components/RegionSelector` |
||||||
|
|
||||||
|
### 使用: |
||||||
|
```html |
||||||
|
<region-selector v-model="value"/> |
||||||
|
``` |
||||||
|
|
||||||
|
行政区划的数据模型可以查看`public/static/json/region-pcas.json`,这是精确到街道的数据,[由此获取](https://github.com/modood/Administrative-divisions-of-China)。 |
||||||
|
|
||||||
|
### RegionSelector Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| :-----------------: | :-------------------------------------: | :--------------: | :-----: | |
||||||
|
| type | 选择器类型,`tab / tree` | `string` | `'tab'` | |
||||||
|
| value / v-model | 绑定值 | `string / array` | - | |
||||||
|
| readonly | 是否只读,等同于`el-select`的`disabled` | `boolean` | - | |
||||||
|
| size | 大小,等同于`el-select`的`size` | `string` | - | |
||||||
|
| getChildrenOnSelect | 在确认选择时是否连同子级一起获取 | `boolean` | - | |
||||||
|
| limit | 是否需要剪枝 | `boolean` | - | |
||||||
|
| limitApi | 获取剪枝数据的api | `function` | - | |
||||||
|
| separation | 分隔号 | `string` | `','` | |
||||||
|
|
||||||
|
### RegionSelector Events: |
||||||
|
|
||||||
|
| 事件名称 | 说明 | 回调参数 | |
||||||
|
| :------: | :------------------------: | :--------------: | |
||||||
|
| clear | 等同于`el-select`的`clear` | - | |
||||||
|
| select | 确定选择时触发 | `(node,idArray)` | |
||||||
|
|
||||||
|
|
||||||
|
::: tip |
||||||
|
`value` 为 `string` 时支持被 `separation` 所连接的汉字或者数字,比如可以是 `北京市,东城区` ,也可以是 `北京市,110101`。 |
||||||
|
为 `array` 时,数组里的每一项除了支持上面的形式外,还支持 `{id:'11',...}、{name:'北京市',...}` 这两种。 |
||||||
|
|
||||||
|
使用 `v-model` 时,不管初始传入的数据是什么样的,最终都会返回 `北京市,东城区` 这样的字符串。 |
||||||
|
::: |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
# Search Form |
||||||
|
|
||||||
|
一个自适应的表单容器,根据父元素的内宽来确定一行渲染多少个表单控件,同时确定是否需要折叠。 |
||||||
|
|
||||||
|
<img :src="$withBase('/search-form_1.png')"> |
||||||
|
<img :src="$withBase('/search-form_2.png')"> |
||||||
|
|
||||||
|
### 引入: |
||||||
|
|
||||||
|
`@/components/SearchForm`,`@/components/SearchFormItem` |
||||||
|
|
||||||
|
### 使用: |
||||||
|
```html |
||||||
|
<search-form> |
||||||
|
<search-form-item label="单 号:"> |
||||||
|
<el-input/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="采购订单:"> |
||||||
|
<el-input/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="创建人:"> |
||||||
|
<el-input/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="创建时间:"> |
||||||
|
<el-date-picker/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="审核人:"> |
||||||
|
<el-input/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="审核时间:"> |
||||||
|
<el-date-picker/> |
||||||
|
</search-form-item> |
||||||
|
<search-form-item label="状 态:"> |
||||||
|
<el-select/> |
||||||
|
</search-form-item> |
||||||
|
</search-form> |
||||||
|
``` |
||||||
|
|
||||||
|
### SearchForm Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| :-----------------: | :----------------------------------: | :------: | :-------: | |
||||||
|
| label-width | 等同于`el-form`的`label-width` | `string` | `'120px'` | |
||||||
|
| xs | 父元素内宽<768px时,每行渲染多少空间 | `number` | `1` | |
||||||
|
| sm | >=768px | `number` | `2` | |
||||||
|
| md | >=998px | `number` | `3` | |
||||||
|
| lg | >=1200px | `number` | `4` | |
||||||
|
|
||||||
|
### SearchForm Slots: |
||||||
|
|
||||||
|
| 名称 | 说明 | |
||||||
|
| :------: | :---------------------------------------------------------------------: | |
||||||
|
| collapse | 折叠按钮,参数为 `{collapse:展开为true否则false,handle:展开折叠的方法}` | |
||||||
|
|
||||||
|
### SearchFormItem Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| :---: | :---------------------------: | :------: | :-------: | |
||||||
|
| label | 等同于`el-form-item`的`label` | `string` | - | |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
# Upload File |
||||||
|
|
||||||
|
集成七牛云直传,需要配合后端使用。支持图片、word、pdf、ppt、rar、excel、txt、zip类型的文件预览(使用kkFileView)。 |
||||||
|
|
||||||
|
<img :src="$withBase('/upload-file.png')"> |
||||||
|
|
||||||
|
### 引入: |
||||||
|
|
||||||
|
`@/components/UploadFile` |
||||||
|
|
||||||
|
### 使用: |
||||||
|
```html |
||||||
|
<upload-file/> |
||||||
|
``` |
||||||
|
|
||||||
|
### UploadFile Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| :------: | :--------------------: | :---------------: | :------: | |
||||||
|
| disabled | 等同于`el-upload` | `boolean` | - | |
||||||
|
| multiple | 等同于`el-upload` | `boolean` | `true` | |
||||||
|
| fileList | 等同于`el-upload` | `array` | `[]` | |
||||||
|
| limit | 等同于`el-upload` | `number` | `10` | |
||||||
|
| maxSize | 单次上传文件的最大大小 | `number / string` | `'10MB'` | |
||||||
|
|
||||||
|
### UploadFile Events: |
||||||
|
|
||||||
|
| 事件名称 | 说明 | 回调参数 | |
||||||
|
| :------: | :--------------------------: | :---------------: | |
||||||
|
| remove | 移出不是本次上传的文件时触发 | `{url}` | |
||||||
|
| success | 上传成功后触发 | `(file,response)` | |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
# 构建和发布 |
||||||
|
|
||||||
|
## 构建 |
||||||
|
|
||||||
|
当项目开发完毕,只需要运行一行命令就可以打包你的应用: |
||||||
|
```shell script |
||||||
|
# 默认环境为 production |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
|
||||||
|
构建打包成功之后,会在根目录生成`dist`文件夹,里面就是构建打包好的静态文件。 |
||||||
|
|
||||||
|
如果需要指定`dist`目录等,则需要通过`vue.config.js`的`outputDir`进行配置。 |
||||||
|
|
||||||
|
## 发布 |
||||||
|
|
||||||
|
如果静态文件不是部署在根目录下,比如想部署在 `/app` 下,那么需要将 `@/config/index.js` 中的 `contextPath` 改为 `/app/`。 |
||||||
|
|
||||||
|
想在 `js`中引入的话,可以直接使用 `process.env.BASE_URL` 拿到`contextPath`。 |
||||||
|
|
||||||
|
如果是在 `html` 中引入的话,是 `<%= BASE_URL %>` 的形式,这是 `ejs` 的语法。 |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
`contextPath` 应始终以 `/` 开头,以 `/` 结束。 |
||||||
|
::: |
||||||
|
|
||||||
|
## 路由模式 |
||||||
|
|
||||||
|
项目提供了动态修改路由模式的功能,只需要修改 `@/config/index.js` 中的 `routerMode` 即可。 |
||||||
|
|
||||||
|
当路由模式为 `'history'` 时,需要服务器提供支持。以 `nginx` 为例,需要在配置中补上: |
||||||
|
```shell script |
||||||
|
try_files $uri $uri/ /index.html; |
||||||
|
``` |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
# 底部警告信息弹框 |
||||||
|
|
||||||
|
### 使用: |
||||||
|
```js |
||||||
|
this.$bottomTip('hello') |
||||||
|
``` |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
# 用户导航 |
||||||
|
|
||||||
|
这是在driver.js的基础上改造来的。 |
||||||
|
|
||||||
|
### 使用: |
||||||
|
|
||||||
|
- 在 `@/mixins/guide/data` 下创建导航步骤,例如 `test.js`。 |
||||||
|
|
||||||
|
- 在需要导航的路由页面里引入 `@/mixins/guide`,并混入 `mixins: [guide.test]` |
||||||
|
|
||||||
|
```js |
||||||
|
//配置导航 |
||||||
|
this.$guide.config(config) |
||||||
|
|
||||||
|
//开始一个导航 |
||||||
|
this.$guide(start,steps,[,beforeExist]) |
||||||
|
|
||||||
|
//退出导航 |
||||||
|
this.$guide.exit() |
||||||
|
``` |
||||||
|
|
||||||
|
### $guide.config(config) |
||||||
|
|
||||||
|
- config,所有可配置项如下: |
||||||
|
```js |
||||||
|
{ |
||||||
|
stageBackground: "#ffffff",//弹框背景颜色 |
||||||
|
doneBtnText: "完成", //完成按钮的文字 |
||||||
|
closeBtnText: "关闭", //关闭按钮的文字 |
||||||
|
nextBtnText: "下一步", //下一步按钮的文字 |
||||||
|
prevBtnText: "上一步", //上一步按钮的文字 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### $guide(start,steps,[,beforeExist]) |
||||||
|
|
||||||
|
- `start` : `number / string`,从第几个步骤开始,`number` 型从0开始,`string` 型可以为 `'first'` 或 `'last'`。 |
||||||
|
|
||||||
|
- `step` : `array`,导航步骤数组,所有可配置项如下: |
||||||
|
```js |
||||||
|
{ |
||||||
|
...config, |
||||||
|
element: string, 用于querySelector, |
||||||
|
content: string, 弹框内容,可以使用html, |
||||||
|
forceShowPrevBtn: boolean, 是否强制显示上一步按钮 |
||||||
|
forceShowNextBtn: boolean, 是否强制显示下一步按钮 |
||||||
|
onPrevious: function, 跳转到上一个导航步骤前触发,返回false或Promise.reject()来阻止跳转 |
||||||
|
onNext: function, 跳转到下一个导航步骤前触发,返回false或Promise.reject()来阻止跳转 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
- `beforeExist` : `function`,退出导航前触发,回调参数是一个 `boolean`,表示当前是否是最后一个步骤,返回 `false` 或 `Promise.reject()` 来阻止退出。 |
||||||
|
|
||||||
|
### $guide.exit() |
||||||
|
|
||||||
|
退出导航,会触发 `beforeExist` 钩子 |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
# 图片预览 |
||||||
|
|
||||||
|
### 使用: |
||||||
|
|
||||||
|
```js |
||||||
|
//开始预览 |
||||||
|
this.$image({index, urlList}) |
||||||
|
|
||||||
|
//退出预览 |
||||||
|
this.$image.close() |
||||||
|
``` |
||||||
|
|
||||||
|
### $image({index, urlList}) |
||||||
|
|
||||||
|
- `index` : `number`,当前图片在图片数组中的下标。 |
||||||
|
|
||||||
|
- `urlList` : `array`,需要预览的图片的url数组 |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
# 拼图滑块验证 |
||||||
|
|
||||||
|
<img :src="$withBase('/puzzle-verify.png')"> |
||||||
|
|
||||||
|
### 使用: |
||||||
|
|
||||||
|
```js |
||||||
|
this.$puzzleVerify().then(...).catch(...) |
||||||
|
``` |
||||||
|
|
||||||
|
返回一个 `promise`,如果验证失败会 `reject`,否则 `resolve` |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
# 手写签名 |
||||||
|
|
||||||
|
### 使用: |
||||||
|
|
||||||
|
```js |
||||||
|
//开始签名 |
||||||
|
this.$signature({image, lineWidth, lineColor, onConfirm) |
||||||
|
|
||||||
|
//退出签名 |
||||||
|
this.$signature.close() |
||||||
|
``` |
||||||
|
|
||||||
|
### $signature({image, lineWidth, lineColor, onConfirm) |
||||||
|
|
||||||
|
- `image` : `string`,需要回显的签名图片的src。 |
||||||
|
|
||||||
|
- `lineWidth` : `number`,线的宽度,默认 `6` |
||||||
|
|
||||||
|
- `lineColor` : `string`,线的颜色,默认 `'#000000'` |
||||||
|
|
||||||
|
- `onConfirm` : `function`,签名完毕后触发,回调参数为签名图片的base64 |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
# 布局 |
||||||
|
|
||||||
|
此布局是在vue-element-admin的基础上改造而来。 |
||||||
|
|
||||||
|
实际上这个布局算是二级路由,一级路由是App.vue,这就是为什么经常会看到路由会这么写的原因: |
||||||
|
```json |
||||||
|
{ |
||||||
|
"path": "/", |
||||||
|
"component": Layout, |
||||||
|
"children": [ |
||||||
|
{ |
||||||
|
"path": "index", |
||||||
|
"name": "index", |
||||||
|
"component": Page, |
||||||
|
"meta": {"title": "首页"} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
`Layout` 填充到 `App.vue` 里的 `<router-view>`,而真正的路由页面 `@/views/index` 则填充到 `Layout` 里的 `<router-view>`。 |
||||||
|
|
||||||
|
项目中没有用到 `Layout` 的页面只有 `@/router/constant/modules/app.js` 里的 `login`、`403`、`404` 这三个。 |
||||||
|
|
||||||
|
另外可以发现 `App.vue` 使用了 `<keep-alive>`,这是为了从 `403`、`404` 返回时能保存之前打开的标签页。 |
||||||
|
|
||||||
|
## 多页签 |
||||||
|
|
||||||
|
增加了平滑滚动功能,增加了快捷键功能:`ctrl + → 下一个页签`、`ctrl + ← 上一个页签` |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
多页签在 启用 / 停用 之间切换时,之前缓存的页签实例会清空,也就是说再次打开时会是新的页面 |
||||||
|
::: |
||||||
|
|
||||||
|
## 侧边栏 |
||||||
|
|
||||||
|
重复点击菜单可刷新路由,折叠时可以选择是否在折叠菜单上显示父级。 |
||||||
|
|
||||||
|
侧边栏的滚动分两种情况,一种是整体的滚动,这是用 `<el-scrollbar>` 做的,不过缺点就是移动端体验不好。 |
||||||
|
另一种是弹出菜单的滚动,这是用 `css` 做的,不用 `<el-scrollbar>` 是因为没办法避免bug,具体原因可以去看element-ui的 `submenu #handleMouseleave`。 |
||||||
|
|
||||||
|
## 全局页脚 |
||||||
|
|
||||||
|
只有使用了 `Layout` 组件的路由页面才会有这个页脚。 |
||||||
|
这里的实现比较特别,由于路由页面被 `<el-scrollbar>` 包裹,需要将页脚的dom插入到 `<el-scrollbar>` 的dom中,所以使用了这种方法: |
||||||
|
```javascript |
||||||
|
const FooterConstructor = Vue.extend(Footer) |
||||||
|
const footerInstance = new FooterConstructor().$mount() |
||||||
|
this.$refs.scrollbar.$refs.wrap.appendChild(footerInstance.$el) |
||||||
|
``` |
||||||
|
至于为什么不用函数式组件,是因为我还不知道怎么样拿到一个函数式组件的dom |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# 新增页面 |
||||||
|
|
||||||
|
1. 把数据请求api创建出来,建议在 `@/api`下根据模块区分。 |
||||||
|
|
||||||
|
2. 创建路由页面,建议在 `@/views` 下根据模块区分。 |
||||||
|
|
||||||
|
3. 如果页面过大过复杂,建议按功能拆分,放在当前页面的文件夹里。 |
||||||
|
|
||||||
|
4. 独有的样式同样放在当前页面的文件夹里,能公用的提取到 `@/assets/styles` 里。 |
||||||
|
|
||||||
|
5. 创建路由,根据是否需要鉴权,在 `@/router/authority/modules` 或 `@/router/constant/modules` 下创建,这两个文件夹下的js会被自动导出。 |
||||||
|
|
||||||
|
6. 如果需要创建vuex模块,请在 `@/store/modules` 下创建,同样的,该文件夹下的js会被自动导出。 |
||||||
|
|
||||||
|
7. 如果页面之间存在公共组件,建议抽取出来,根据业务耦合度拆分至 `@/components` 或 `@/bizComponents` |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
# 权限 |
||||||
|
|
||||||
|
项目使用基于资源url的形式来实现权限控制,说白点就是用户的所有操作都可以用一个url来标识。 |
||||||
|
|
||||||
|
鉴权方法为 `@/util/auth.js #auth`。 |
||||||
|
|
||||||
|
鉴权流程: |
||||||
|
1. 如果用户的`admin` 字段为1,那么直接放行。 |
||||||
|
|
||||||
|
2. 如果访问的 `url` 不在总权限树内,说明该操作无需权限,放行。 |
||||||
|
|
||||||
|
3. 如果访问的 `url` 在用户的权限树内,说明有权限,放行。 |
||||||
|
|
||||||
|
4. 否则拦截,跳转至403页面。 |
||||||
|
|
||||||
|
当然前端的权限控制只是过滤一小部分,真正的工作还是由后端完成。 |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
# 插件 |
||||||
|
|
||||||
|
## canvas动画 |
||||||
|
|
||||||
|
所有动画位于 `@/plugin/canvasAnimation`下,使用 `new Animation(canvas)`的方式来创建一个动画。 |
||||||
|
想停止的话调用 `Animation.stop()` 即可。 |
||||||
|
|
||||||
|
## 图片压缩 |
||||||
|
|
||||||
|
支持 `png`、`jpg` 两种格式的图片,使用方法参考 `@/views/example/developingTest/components/ImageCompressTest.vue`。 |
||||||
|
|
||||||
|
::: tip |
||||||
|
由于是以 `worker` 的形式调用,所以需要准备 `cjpeg.min.js` 和 `pngquant.min.js`,这两个文件都放在项目的 `public`下。 |
||||||
|
::: |
||||||
|
|
||||||
|
## 其他 |
||||||
|
|
||||||
|
此外项目中还有 `live2d` 和 `webgl` 两个插件,有兴趣的可以自行查看。 |
||||||
@ -0,0 +1,158 @@ |
|||||||
|
# 路由 |
||||||
|
|
||||||
|
## 路由配置项 |
||||||
|
|
||||||
|
路由配置项不光参与路由的生成,也参与侧边栏、搜索结果的生成。 |
||||||
|
|
||||||
|
### Route Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| ---------- | ---------------------------------------------------- | ------------------- | ----- | |
||||||
|
| hidden | 是否在侧边栏中显示 | `boolean` | - | |
||||||
|
| alwaysShow | 是否总是把只有一个子级的菜单以嵌套模式在侧边栏中展示 | `boolean` | - | |
||||||
|
| sort | 侧边栏的排序值,值越小越靠前 | `number` | 10000 | |
||||||
|
|
||||||
|
### Route.meta Attributes: |
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认 | |
||||||
|
| ------------ | ------------------------------------------------ | --------- | ----------------------- | |
||||||
|
| title | 路由在侧边栏、面包屑、页签、搜索结果中的显示名称 | `string` | - | |
||||||
|
| icon | 图标名,支持svg-icon、el-icon | `string` | - | |
||||||
|
| affix | 是否在多页签中固定显示 | `boolean` | - | |
||||||
|
| noCache | true时缓存页面 | `boolean` | - | |
||||||
|
| activeMenu | 侧边栏当前激活菜单的index | `string` | - | |
||||||
|
| noAuth | true时路由不需要鉴权 | `boolean` | router/constant下为true | |
||||||
|
| iframe | 需要打开的iframe的地址 | `string` | - | |
||||||
|
| isDetailPage | 是否是详情页 | `boolean` | - | |
||||||
|
|
||||||
|
::: tip 注意 |
||||||
|
路由meta上的affix、noAuth、noCache会被子路由继承,优先使用子路由的值 |
||||||
|
::: |
||||||
|
|
||||||
|
## 页面缓存 |
||||||
|
|
||||||
|
先总结一下路由页面被缓存的条件: |
||||||
|
- 启用了多页签 |
||||||
|
- `(route.name || route.meta.isDetailPage) && route.meta.noCache && !route.meta.iframe` |
||||||
|
|
||||||
|
通常情况路由想做缓存都是通过 `<keep-alive>`,但是如果是像 `/edit/1`、`/edit/2` 这样的共用路由页面的详情页的话, |
||||||
|
`<keep-alive>` 就满足不了需求,因为它是通过组件的 `name` 来区分组件的。 |
||||||
|
所以项目中仿照 `<keep-alive>` 写了 `<keep-router-view-alive>`,可以根据当前路由的路径来区分组件。 |
||||||
|
|
||||||
|
除此之外还有个问题,在共用了同一个组件的路由之间跳转的话,组件会被复用,假如有以下路由: |
||||||
|
```js |
||||||
|
const component={template:`<input v-model="data">`, data: ()=> ({data:1})} |
||||||
|
|
||||||
|
const routes = [ |
||||||
|
{ path: '/foo', component }, |
||||||
|
{ path: '/bar', component } |
||||||
|
] |
||||||
|
``` |
||||||
|
先访问 `/foo`,然后将输入框的值改为100,再访问 `/bar` ,会发现输入框的值还是100。 |
||||||
|
所以在 `router.beforeEach` 中做了判断,如果跳转前后的路由的 `meta.isDetailPage` 均为 `true` 的话,那么借助 `/redirect` 跳转一次,从而避免组件复用的问题 |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
如果不使用meta.isDetailPage的话,需要缓存的页面的 `name` 必须和路由的 `name` 一致 |
||||||
|
::: |
||||||
|
|
||||||
|
## 路由白名单 |
||||||
|
|
||||||
|
在 `@/router/index.js` 中有这么一句: |
||||||
|
```js |
||||||
|
const whiteList = transformWhiteList(['/login', '/register', '/404', '/403']) |
||||||
|
``` |
||||||
|
这个 `whiteList` 就是白名单,里面的值会被转为正则表达式,详细请查看[path-to-regexp文档](https://github.com/pillarjs/path-to-regexp)。 |
||||||
|
被`whiteList` 匹配的路由不需要验证,直接放行。 |
||||||
|
|
||||||
|
## 首屏加载效果 |
||||||
|
|
||||||
|
想体验的可以把 `network` 设为 `slow 3g` 就可以在在线实例看到效果。 |
||||||
|
|
||||||
|
原理很简单,在 `index.html` 里有这么一段: |
||||||
|
```html |
||||||
|
<div id="app"> |
||||||
|
<div id="loader-wrapper"> |
||||||
|
<img> |
||||||
|
<p>资源加载中......</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
``` |
||||||
|
在 `vuejs` 初始化完成后,`app` 里的内容会被替换,从而实现加载效果。 |
||||||
|
|
||||||
|
## 骨架屏 |
||||||
|
|
||||||
|
通常情况下,路由组件都是以 `import(...)` 的形式异步引入, |
||||||
|
如果想让路由在加载异步组件时显示,可以使用以下方式: |
||||||
|
```js |
||||||
|
function lazyLoadView(component) { |
||||||
|
const AsyncHandler = () => ({component, loading: Skeleton}) |
||||||
|
return Promise.resolve({ |
||||||
|
functional: true, |
||||||
|
render(h, {data, children}) { |
||||||
|
return h(AsyncHandler, data, children) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
//将路由配置中的component项改为以下形式 |
||||||
|
component: () => lazyLoadView(import(...)) |
||||||
|
``` |
||||||
|
|
||||||
|
关于 `AsyncHandler` 的更多选项,可以查看[官方文档](https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%A4%84%E7%90%86%E5%8A%A0%E8%BD%BD%E7%8A%B6%E6%80%81) |
||||||
|
|
||||||
|
## 过渡动画 |
||||||
|
|
||||||
|
正常 `<router-view>` 使用过渡动画的方法应该是这样的: |
||||||
|
```html |
||||||
|
<transition> |
||||||
|
<keep-alive> |
||||||
|
<router-view> |
||||||
|
</keep-alive> |
||||||
|
</transition> |
||||||
|
``` |
||||||
|
|
||||||
|
但是由于项目中使用了自定义的 `<keep-router-view-alive>`,所以就变成这样的: |
||||||
|
```html |
||||||
|
<keep-router-view-alive :include="cachedViews"> |
||||||
|
<transition :name="transitionName" mode="out-in"> |
||||||
|
<router-view> |
||||||
|
</transition> |
||||||
|
</keep-router-view-alive> |
||||||
|
``` |
||||||
|
|
||||||
|
具体原因是 `<transition>` 中的代码硬编码了 `<keep-alive>`,导致如果按照往常写法的话,过渡动画不会生效。 |
||||||
|
|
||||||
|
项目中是根据页签的前后位置来决定路由动画的,比如从前面的页签跳转到后面的,那么动画是从左滑出,反之是从右滑出。 |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
只有在启用了多页签的时候才会有上面的效果,否则默认是 `el-fade-in-linear` 动画。 |
||||||
|
::: |
||||||
|
|
||||||
|
## iframe |
||||||
|
|
||||||
|
只需要按如下配置即可打开一个iframe: |
||||||
|
```json |
||||||
|
{ |
||||||
|
"path":"/baidu", |
||||||
|
"meta":{ |
||||||
|
"iframe":"https://www.baidu.com" |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
当向上面的路由跳转时,会隐藏路由页面,展示 `<iframe>` 标签,反之则隐藏。 |
||||||
|
另外,相同地址的iframe不会重复打开,想要刷新需使用 `/redirect` 跳转。 |
||||||
|
|
||||||
|
## 外链 |
||||||
|
|
||||||
|
也可以在路由中配置一个外链,只要在 path 中填写了合法的 url 路径,当你点击该路由菜单时就会新开这个页面。 |
||||||
|
|
||||||
|
例如: |
||||||
|
```json |
||||||
|
{ |
||||||
|
"path": "https://www.baidu.com", |
||||||
|
"meta": { |
||||||
|
"title": "百度" |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
# 常用工具类 |
||||||
|
|
||||||
|
## 日期格式化 |
||||||
|
```js |
||||||
|
import {timeFormat} from '@/utils' |
||||||
|
|
||||||
|
// 以 yyyy-MM-dd HH:mm:ss 格式显示的当前时间 |
||||||
|
timeFormat() |
||||||
|
|
||||||
|
// 06-29 |
||||||
|
timeFormat('MM-dd',new Date('2020/06/29')) |
||||||
|
``` |
||||||
|
|
||||||
|
## 数学运算 |
||||||
|
|
||||||
|
基于 `decimal.js` 封装了加减乘除运算方法。 |
||||||
|
|
||||||
|
```js |
||||||
|
import {plus,sub,mul,div} from '@/utils/math' |
||||||
|
|
||||||
|
//1.05 * 10.8 / 0.6 /0.1 + 0.1 + 0.5 - 0.2 - 0.3 |
||||||
|
sub(plus(div(mul(1.05,10.8),0.6,0.1),0.1,0.5),0.2,0.3) |
||||||
|
``` |
||||||
|
|
||||||
|
## 列表转树 |
||||||
|
|
||||||
|
```js |
||||||
|
import {createTree} from '@/utils/tree' |
||||||
|
|
||||||
|
const arr1 = [{id: 1, pid: 0, name: 'a'}, {id: 2, pid: 1, name: 'b'}, {id: 3, pid:2, name: 'c'}] |
||||||
|
const tree1 = createTree(arr1) |
||||||
|
|
||||||
|
const arr2 = [{xid: 1, ypid: 0, name: 'a'}, {xid: 2, ypid: 1, name: 'b'}, {xid: 3, ypid: 2, name:' c'}] |
||||||
|
const tree2 = createTree(arr2, 0, 'xid', 'ypid') |
||||||
|
``` |
||||||
|
|
||||||
|
## 纯前端导出excel |
||||||
|
|
||||||
|
依赖exceljs(实际上项目中还有xlsx-style的实现版本,见 `@/utils/excel/xlsx`,不过存在问题,所以弃用了)。 |
||||||
|
```js |
||||||
|
import {json2workbook, exportExcelByJs} from '@/utils/excel' |
||||||
|
|
||||||
|
const column = [ |
||||||
|
{header: ['合并表头演示', '序号'], prop: 'no', merge: true}, |
||||||
|
{header: ['合并表头演示', '名称'], prop: 'name', merge: true}, |
||||||
|
{header: '日期', prop: 'date'} |
||||||
|
] |
||||||
|
|
||||||
|
const arr = [ |
||||||
|
{no: 1,name: '老王',date: '20190201'} |
||||||
|
] |
||||||
|
|
||||||
|
const workbook = json2workbook(arr, column) |
||||||
|
|
||||||
|
exportExcelByJs(workbook, '测试导出.xlsx') |
||||||
|
``` |
||||||
|
|
||||||
|
### json2workbook(data, columns, merge) : workbook |
||||||
|
|
||||||
|
- `data` : `array`,json数组。 |
||||||
|
|
||||||
|
- `columns` : `array`,列配置项数组,列配置详细如下: |
||||||
|
```js |
||||||
|
{ |
||||||
|
header : string | array,表头名称,类型为 array 时,将从左往右地解析成一颗树,用于表头的合并 |
||||||
|
prop : string,表头对应的 json 字段名称,如果为空,则该字段不会写入excel表格 |
||||||
|
merge : boolean,是否需要合并,true的话会从上至下合并值相同的相邻单元格 |
||||||
|
width : number,单元格宽度,默认 20 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
- `merge` : `object`,合并配置项,详细如下: |
||||||
|
```js |
||||||
|
{ |
||||||
|
primaryKey : string,每行json数据的唯一标识字段名称 |
||||||
|
orderKey : string,每行json数据的序号字段名称 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### exportExcelByJs(workbook, filename) |
||||||
|
|
||||||
|
- `workbook`,json2workbook的返回结果 |
||||||
|
|
||||||
|
- `filename` : `string`,导出的文件名称,需要带上.xlsx后缀 |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
# 安装 |
||||||
|
|
||||||
|
## 环境准备 |
||||||
|
|
||||||
|
- jdk1.8 |
||||||
|
- mysql8 |
||||||
|
- redis3.2 |
||||||
|
- node |
||||||
|
- npm |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
后端开发时ide需要安装lombok插件 |
||||||
|
::: |
||||||
|
|
||||||
|
## 基础信息配置 |
||||||
|
|
||||||
|
- 导入位于`java`目录下的sql脚本(初始管理员账号密码:admin/123456) |
||||||
|
|
||||||
|
- 修改`java/src/main/resources/application.yml`中的`datasource`、`redis`、`socket`、`file`、`qiniu`等配置项 |
||||||
|
|
||||||
|
- 根据注释修改`vue/src/config/index.js`中的变量,通常只需要修改`socketUrl`、`attachmentPrefix`、`filePreviewPrefix`即可 |
||||||
|
|
||||||
|
::: warning 注意 |
||||||
|
如果后端部署到服务器上,yml中的socket.hostname需要是服务器的局域网ip,否则socket服务端创建不了 |
||||||
|
::: |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"name": "docs", |
||||||
|
"version": "1.0.0", |
||||||
|
"scripts": { |
||||||
|
"dev": "vuepress dev .", |
||||||
|
"build": "vuepress build ." |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"vuepress": "^1.5.2" |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue