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