Repository: vueComponent/ant-design-vue-pro
Branch: master
Commit: 616df9e063a4
Files: 255
Total size: 566.6 KB
Directory structure:
gitextract_h9on6hx8/
├── .browserslistrc
├── .editorconfig
├── .eslintrc.js
├── .eslintrc.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── feature_request.md
│ │ └── need-help-issue.md
│ ├── pull_request_template.md
│ └── workflows/
│ └── cloudflare.yml
├── .gitignore
├── .husky/
│ ├── .gitignore
│ ├── commit-msg
│ └── pre-commit
├── .lintstagedrc.json
├── .prettierrc
├── .stylelintrc.js
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── README.zh-CN.md
├── babel.config.js
├── commitlint.config.js
├── config/
│ ├── plugin.config.js
│ └── themePluginConfig.js
├── deploy/
│ ├── caddy.conf
│ └── nginx.conf
├── docs/
│ ├── add-page-loading-animate.md
│ └── webpack-bundle-analyzer.md
├── jest.config.js
├── jsconfig.json
├── package.json
├── postcss.config.js
├── public/
│ └── index.html
├── src/
│ ├── App.vue
│ ├── api/
│ │ ├── login.js
│ │ └── manage.js
│ ├── components/
│ │ ├── ArticleListContent/
│ │ │ ├── ArticleListContent.vue
│ │ │ └── index.js
│ │ ├── AvatarList/
│ │ │ ├── Item.jsx
│ │ │ ├── List.jsx
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ └── index.md
│ │ ├── Charts/
│ │ │ ├── Bar.vue
│ │ │ ├── ChartCard.vue
│ │ │ ├── Liquid.vue
│ │ │ ├── MiniArea.vue
│ │ │ ├── MiniBar.vue
│ │ │ ├── MiniProgress.vue
│ │ │ ├── MiniSmoothArea.vue
│ │ │ ├── Radar.vue
│ │ │ ├── RankList.vue
│ │ │ ├── TagCloud.vue
│ │ │ ├── TransferBar.vue
│ │ │ ├── Trend.vue
│ │ │ ├── chart.less
│ │ │ └── smooth.area.less
│ │ ├── Dialog.js
│ │ ├── Editor/
│ │ │ ├── QuillEditor.vue
│ │ │ └── WangEditor.vue
│ │ ├── Ellipsis/
│ │ │ ├── Ellipsis.vue
│ │ │ ├── index.js
│ │ │ └── index.md
│ │ ├── FooterToolbar/
│ │ │ ├── FooterToolBar.vue
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ └── index.md
│ │ ├── GlobalFooter/
│ │ │ └── index.vue
│ │ ├── GlobalHeader/
│ │ │ ├── AvatarDropdown.vue
│ │ │ └── RightContent.vue
│ │ ├── IconSelector/
│ │ │ ├── IconSelector.vue
│ │ │ ├── README.md
│ │ │ ├── icons.js
│ │ │ └── index.js
│ │ ├── MultiTab/
│ │ │ ├── MultiTab.vue
│ │ │ ├── events.js
│ │ │ ├── index.js
│ │ │ └── index.less
│ │ ├── NProgress/
│ │ │ └── nprogress.less
│ │ ├── NoticeIcon/
│ │ │ ├── NoticeIcon.vue
│ │ │ └── index.js
│ │ ├── NumberInfo/
│ │ │ ├── NumberInfo.vue
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ └── index.md
│ │ ├── Other/
│ │ │ └── CarbonAds.vue
│ │ ├── PageLoading/
│ │ │ └── index.jsx
│ │ ├── Search/
│ │ │ ├── GlobalSearch.jsx
│ │ │ └── index.less
│ │ ├── SelectLang/
│ │ │ ├── index.jsx
│ │ │ └── index.less
│ │ ├── SettingDrawer/
│ │ │ ├── SettingDrawer.vue
│ │ │ ├── SettingItem.vue
│ │ │ ├── index.js
│ │ │ ├── settingConfig.js
│ │ │ └── themeColor.js
│ │ ├── StandardFormRow/
│ │ │ ├── StandardFormRow.vue
│ │ │ └── index.js
│ │ ├── Table/
│ │ │ ├── README.md
│ │ │ └── index.js
│ │ ├── TagSelect/
│ │ │ ├── TagSelectOption.jsx
│ │ │ └── index.jsx
│ │ ├── TextArea/
│ │ │ ├── index.jsx
│ │ │ └── style.less
│ │ ├── Tree/
│ │ │ └── Tree.jsx
│ │ ├── Trend/
│ │ │ ├── Trend.vue
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ └── index.md
│ │ ├── _util/
│ │ │ └── util.js
│ │ ├── index.js
│ │ ├── index.less
│ │ └── tools/
│ │ └── TwoStepCaptcha.vue
│ ├── config/
│ │ ├── defaultSettings.js
│ │ └── router.config.js
│ ├── core/
│ │ ├── bootstrap.js
│ │ ├── directives/
│ │ │ └── action.js
│ │ ├── icons.js
│ │ ├── lazy_use.js
│ │ ├── permission/
│ │ │ └── permission.js
│ │ └── use.js
│ ├── global.less
│ ├── layouts/
│ │ ├── BasicLayout.less
│ │ ├── BasicLayout.vue
│ │ ├── BlankLayout.vue
│ │ ├── PageView.vue
│ │ ├── RouteView.vue
│ │ ├── UserLayout.vue
│ │ └── index.js
│ ├── locales/
│ │ ├── index.js
│ │ └── lang/
│ │ ├── en-US/
│ │ │ ├── account/
│ │ │ │ └── settings.js
│ │ │ ├── account.js
│ │ │ ├── dashboard/
│ │ │ │ └── analysis.js
│ │ │ ├── dashboard.js
│ │ │ ├── form/
│ │ │ │ └── basicForm.js
│ │ │ ├── form.js
│ │ │ ├── global.js
│ │ │ ├── menu.js
│ │ │ ├── result/
│ │ │ │ ├── fail.js
│ │ │ │ └── success.js
│ │ │ ├── result.js
│ │ │ ├── setting.js
│ │ │ └── user.js
│ │ ├── en-US.js
│ │ ├── zh-CN/
│ │ │ ├── account/
│ │ │ │ └── settings.js
│ │ │ ├── account.js
│ │ │ ├── dashboard/
│ │ │ │ └── analysis.js
│ │ │ ├── dashboard.js
│ │ │ ├── form/
│ │ │ │ └── basicForm.js
│ │ │ ├── form.js
│ │ │ ├── global.js
│ │ │ ├── menu.js
│ │ │ ├── result/
│ │ │ │ ├── fail.js
│ │ │ │ └── success.js
│ │ │ ├── result.js
│ │ │ ├── setting.js
│ │ │ └── user.js
│ │ └── zh-CN.js
│ ├── main.js
│ ├── mock/
│ │ ├── index.js
│ │ ├── services/
│ │ │ ├── article.js
│ │ │ ├── auth.js
│ │ │ ├── manage.js
│ │ │ ├── other.js
│ │ │ ├── tagCloud.js
│ │ │ └── user.js
│ │ └── util.js
│ ├── permission.js
│ ├── router/
│ │ ├── README.md
│ │ ├── generator-routers.js
│ │ └── index.js
│ ├── store/
│ │ ├── app-mixin.js
│ │ ├── device-mixin.js
│ │ ├── getters.js
│ │ ├── i18n-mixin.js
│ │ ├── index.js
│ │ ├── modules/
│ │ │ ├── app.js
│ │ │ ├── async-router.js
│ │ │ ├── static-router.js
│ │ │ └── user.js
│ │ └── mutation-types.js
│ ├── utils/
│ │ ├── axios.js
│ │ ├── domUtil.js
│ │ ├── filter.js
│ │ ├── request.js
│ │ ├── routeConvert.js
│ │ ├── screenLog.js
│ │ ├── util.js
│ │ └── utils.less
│ └── views/
│ ├── 404.vue
│ ├── account/
│ │ ├── center/
│ │ │ ├── index.vue
│ │ │ └── page/
│ │ │ ├── App.vue
│ │ │ ├── Article.vue
│ │ │ ├── Project.vue
│ │ │ └── index.js
│ │ └── settings/
│ │ ├── AvatarModal.vue
│ │ ├── BasicSetting.vue
│ │ ├── Binding.vue
│ │ ├── Custom.vue
│ │ ├── Index.vue
│ │ ├── Notification.vue
│ │ └── Security.vue
│ ├── dashboard/
│ │ ├── Analysis.vue
│ │ ├── Monitor.vue
│ │ ├── TestWork.vue
│ │ ├── Workplace.less
│ │ └── Workplace.vue
│ ├── exception/
│ │ ├── 403.vue
│ │ ├── 404.vue
│ │ └── 500.vue
│ ├── form/
│ │ ├── advancedForm/
│ │ │ ├── AdvancedForm.vue
│ │ │ ├── RepositoryForm.vue
│ │ │ └── TaskForm.vue
│ │ ├── basicForm/
│ │ │ └── index.vue
│ │ └── stepForm/
│ │ ├── Step1.vue
│ │ ├── Step2.vue
│ │ ├── Step3.vue
│ │ └── StepForm.vue
│ ├── list/
│ │ ├── BasicList.vue
│ │ ├── CardList.vue
│ │ ├── QueryList.vue
│ │ ├── TableList.vue
│ │ ├── components/
│ │ │ └── Info.vue
│ │ ├── modules/
│ │ │ ├── CreateForm.vue
│ │ │ ├── StepByStepModal.vue
│ │ │ └── TaskForm.vue
│ │ ├── search/
│ │ │ ├── Applications.vue
│ │ │ ├── Article.vue
│ │ │ ├── Projects.vue
│ │ │ ├── SearchLayout.vue
│ │ │ └── components/
│ │ │ ├── CardInfo.vue
│ │ │ └── IconText.vue
│ │ └── table/
│ │ ├── Edit.vue
│ │ └── List.vue
│ ├── other/
│ │ ├── BigForm.vue
│ │ ├── IconSelectorView.vue
│ │ ├── PermissionList.vue
│ │ ├── RoleList.vue
│ │ ├── TableInnerEditList.vue
│ │ ├── TreeList.vue
│ │ ├── UserList.vue
│ │ └── modules/
│ │ ├── OrgModal.vue
│ │ └── RoleModal.vue
│ ├── profile/
│ │ ├── advanced/
│ │ │ └── Advanced.vue
│ │ └── basic/
│ │ └── index.vue
│ ├── result/
│ │ ├── Error.vue
│ │ └── Success.vue
│ └── user/
│ ├── Login.vue
│ ├── Register.vue
│ └── RegisterResult.vue
├── tests/
│ └── unit/
│ └── .eslintrc.js
├── vue.config.js
└── wrangler.jsonc
================================================
FILE CONTENTS
================================================
================================================
FILE: .browserslistrc
================================================
> 1%
last 2 versions
not ie <= 10
================================================
FILE: .editorconfig
================================================
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=false
indent_style=space
indent_size=2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style=space
indent_size=2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style=space
indent_size=2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2
[*.svg]
indent_style=space
indent_size=2
[*.js.map]
indent_style=space
indent_size=2
[*.less]
indent_style=space
indent_size=2
[*.vue]
indent_style=space
indent_size=2
[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/strongly-recommended',
'@vue/standard'
],
rules: {
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'vue/max-attributes-per-line': [
2,
{
'singleline': 5,
'multiline': {
'max': 1,
'allowFirstLine': false
}
}
],
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-tabs': 0,
'quotes': [
2,
'single',
{
'avoidEscape': true,
'allowTemplateLiterals': true
}
],
'semi': [
2,
'never',
{
'beforeStatementContinuationChars': 'never'
}
],
'no-delete-var': 2,
'prefer-const': [
2,
{
'ignoreReadBeforeAssign': false
}
],
'template-curly-spacing': 'off',
'indent': 'off'
},
parserOptions: {
parser: 'babel-eslint'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}
================================================
FILE: .eslintrc.json
================================================
{
"rules": {
"space-before-function-paren": 0
}
}
================================================
FILE: .gitattributes
================================================
public/* linguist-vendored
# Automatically normalize line endings (to LF) for all text-based files.
* text=auto eol=lf
# Declare files that will always have CRLF line endings on checkout.
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve(Bug 反馈)
title: ''
labels: ''
assignees: ''
---
**Describe the bug (描述 Bug)**
A clear and concise description of what the bug is.
**To Reproduce (重现步骤)**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior(你期待的是什么?)**
A clear and concise description of what you expected to happen.
**Screenshots(截图)**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context(附加信息)**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/ISSUE_TEMPLATE/need-help-issue.md
================================================
---
name: Need help issue
about: Question for use(问题求助)
title: ''
labels: question
assignees: ''
---
**Question (问题描述)**
How to use component `s-table` paging
**Describe the solution you'd like (你期待的是什么?)**
A clear and concise description of what you want to happen.
**Additional context(附加信息)**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/pull_request_template.md
================================================
First of all, thank you for your contribution! 😄
Pull request will be merged after one of collaborators approve.
Please makes sure that these form are filled before submitting your pull request, thank you!
### 这个变动的性质是
- [ ] 新特性提交
- [ ] 日常 bug 修复
- [ ] 文档改进
- [ ] 组件样式改进
- [ ] 重构
- [ ] 代码风格优化
- [ ] 分支合并
- [ ] 其他改动(是关于什么的改动?)
### 需求背景
> 1. 描述相关需求的来源。
> 2. 要解决的问题。
> 3. 相关的 issue 讨论链接。
### 实现方案和 API(非新功能可选)
> 1. 基本的解决思路和其他可选方案。
> 2. 列出最终的 API 实现和用法。
> 3. 涉及UI/交互变动需要有截图或 GIF。
### 对用户的影响和可能的风险(非新功能可选)
> 1. 这个改动对用户端是否有影响?影响的方面有哪些?
> 2. 是否有可能隐含的 break change 和其他风险?
### Changelog 描述(非新功能可选)
> 1. 英文描述
> 2. 中文描述(可选)
### 请求合并前的自查清单
- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] Changelog 已提供或无须提供
### 后续计划(非新功能可选)
> 如果这个提交后面还有相关的其他提交和跟进信息,可以写在这里。
================================================
FILE: .github/workflows/cloudflare.yml
================================================
name: Build and Deploy to Cloudflare
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
name: Build Application
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install --force
- name: Build application
run: npm run build:preview
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-files
path: dist/
retention-days: 1
deploy:
runs-on: ubuntu-latest
name: Deploy to Cloudflare
needs: build
permissions:
contents: read
deployments: write
steps:
- name: Checkout (for config files)
uses: actions/checkout@v5
with:
sparse-checkout: |
wrangler.jsonc
sparse-checkout-cone-mode: false
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-files
path: dist/
- name: Deploy (Workers + Static Assets)
uses: cloudflare/wrangler-action@v3.14.1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangler.jsonc
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/dist
# 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*
package-lock.json
================================================
FILE: .husky/.gitignore
================================================
_
================================================
FILE: .husky/commit-msg
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint
================================================
FILE: .lintstagedrc.json
================================================
{
"*.js": "eslint --fix",
"*.{css,less}": "stylelint --fix"
}
================================================
FILE: .prettierrc
================================================
{
"printWidth": 120,
"semi": false,
"singleQuote": true,
"prettier.spaceBeforeFunctionParen": true
}
================================================
FILE: .stylelintrc.js
================================================
module.exports = {
processors: [],
plugins: ['stylelint-order'],
extends: [
'stylelint-config-standard',
'stylelint-config-css-modules'
],
rules: {
'selector-class-pattern': null,
'string-quotes': 'single', // 单引号
'at-rule-empty-line-before': null,
'at-rule-no-unknown': null,
'at-rule-name-case': 'lower', // 指定@规则名的大小写
'length-zero-no-unit': true, // 禁止零长度的单位(可自动修复)
'shorthand-property-no-redundant-values': true, // 简写属性
'number-leading-zero': 'never', // 小数不带0
'declaration-block-no-duplicate-properties': null, // 禁止声明快重复属性
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器。
'selector-max-id': 3, // 限制一个选择器中 ID 选择器的数量
'max-nesting-depth': 4,
'indentation': [2, { // 指定缩进 warning 提醒
'severity': 'warning'
}],
'order/properties-order': [ // 规则顺序
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-color',
'border',
'border-radius',
'content',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'transform'
]
}
}
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- 10.15.0
cache: yarn
script:
- yarn
- yarn run lint --no-fix && yarn run build
================================================
FILE: Dockerfile
================================================
FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
ADD deploy/nginx.conf /etc/nginx/conf.d/default.conf
COPY dist/ /usr/share/nginx/html/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Anan Yang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
English | [简体中文](./README.zh-CN.md)
Ant Design Vue Pro for Vue 2
An out-of-box UI solution for enterprise applications as a Vue boilerplate. based on
Ant Design of Vue
If you are looking for the Vue 3 version, please visit [Ant Design Vue Pro for Vue 3](https://store.antdv.com/items/admin-pro)
[](https://github.com/vueComponent/ant-design-vue-pro/blob/master/LICENSE)
[](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[](https://travis-ci.org/vueComponent/ant-design-vue-pro)
- Preview: https://preview.pro.antdv.com
- Home Page: https://pro.antdv.com
- Documentation: https://pro.antdv.com/docs/getting-started
- ChangeLog: https://pro.antdv.com/docs/changelog
- FAQ: https://pro.antdv.com/docs/faq
- Vue3 ProLayout: https://github.com/vueComponent/pro-layout
## Overview

### Env and dependencies
- node
- yarn
- webpack
- eslint
- @vue/cli
- [ant-design-vue@1.x](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - Picture edit
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - AntV G2
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - Antv/G2 of Vue
> Note: [Yarn](https://yarnpkg.com/) package management is recommended, the exact same version loaded with the demo site of this project (yarn.lock) . but you can also use npm
### Project setup
- Clone repo
```bash
git clone https://github.com/vueComponent/ant-design-vue-pro.git
cd ant-design-vue-pro
```
- Install dependencies
```
yarn install
```
- Compiles and hot-reloads for development
```
yarn run serve
```
- Compiles and minifies for production
```
yarn run build
```
- Lints and fixes files
```
yarn run lint
```
### Other
- **IMPORTANT : Issue feedback !! when opening Issue read [Issue / PR Contributing](https://github.com/vueComponent/ant-design-vue-pro/issues/90)**
- [Vue-cli3](https://cli.vuejs.org/guide/) used by the project.
- Disable Eslint (not recommended): remove `eslintConfig` field in `package.json` and `vue.config.js` field `lintOnSave: false`
- Load on Demand `/src/main.js` L14, in `import './core/lazy_use'`, `import './core/use''`. more [load-on-demand.md](./docs/load-on-demand.md)
- Customize Theme: [Custom Theme Config (@kokoroli)](https://github.com/kokoroli/antd-awesome/blob/master/docs/Ant_Design_%E6%A0%B7%E5%BC%8F%E8%A6%86%E7%9B%96.md)
- I18n: [locales (@musnow)](./src/locales/index.js)
- Production env `mock` is disabled. use `src/mock/index.js`
- pls use `release` version
## Browsers support
Modern browsers and IE10.
| [ ](http://godban.github.io/browsers-support-badges/)IE / Edge | [ ](http://godban.github.io/browsers-support-badges/)Firefox | [ ](http://godban.github.io/browsers-support-badges/)Chrome | [ ](http://godban.github.io/browsers-support-badges/)Safari | [ ](http://godban.github.io/browsers-support-badges/)Opera |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IE10, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## Contributors
This project exists thanks to all the people who contribute.
================================================
FILE: README.zh-CN.md
================================================
[English](./README.md) | 简体中文
Ant Design Vue Pro for Vue 2
An out-of-box UI solution for enterprise applications as a Vue boilerplate. based on
Ant Design of Vue
如果需要 Vue 3 版本,请访问 [Ant Design Vue Pro for Vue 3](https://store.antdv.com/items/admin-pro)
[](https://github.com/vueComponent/ant-design-vue-pro/blob/master/LICENSE)
[](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[](https://travis-ci.org/vueComponent/ant-design-vue-pro)
- 预览: https://preview.pro.antdv.com
- 首页: https://pro.antdv.com
- 文档: https://pro.antdv.com/docs/getting-started
- 更新日志: https://pro.antdv.com/docs/changelog
- 常见问题: https://pro.antdv.com/docs/faq
- Vue3 ProLayout: https://github.com/vueComponent/pro-layout
## Overview
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 [Ant Design Pro](https://pro.ant.design/)

## 环境和依赖
- node
- yarn
- webpack
- eslint
- @vue/cli
- [ant-design-vue@1.x](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
> 请注意,我们强烈建议本项目使用 [Yarn](https://yarnpkg.com/) 包管理工具,这样可以与本项目演示站所加载完全相同的依赖版本 (yarn.lock) 。由于我们没有对依赖进行强制的版本控制,采用非 yarn 包管理进行引入时,可能由于 Pro 所依赖的库已经升级版本而引入了新版本所导致的问题。作者可能会由于时间问题无法及时排查而导致您采用本项目作为基项目而出现问题。
## 项目下载和运行
- 拉取项目代码
```bash
git clone https://github.com/vueComponent/ant-design-vue-pro.git
cd ant-design-vue-pro
```
- 安装依赖
```
yarn install
```
- 开发模式运行
```
yarn run serve
```
- 编译项目
```
yarn run build
```
- Lints and fixes files
```
yarn run lint
```
## 其他说明
- **关于 Issue 反馈 (重要!重要!重要!) 请在开 _Issue_ 前,先阅读该内容:[Issue / PR 编写建议](https://github.com/vueComponent/ant-design-vue-pro/issues/90)**
- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请确保你所使用的 vue-cli 是新版,并且已经学习 cli 官方文档使用教程
- 关闭 Eslint (不推荐) 移除 `package.json` 中 `eslintConfig` 整个节点代码, `vue.config.js` 下的 `lintOnSave` 值改为 `false`
- 组件按需加载 `/src/main.js` L14 相关代码 `import './core/lazy_use'` / `import './core/use'`
- [修改 Ant Design 配色 (@kokoroli)](https://github.com/kokoroli/antd-awesome/blob/master/docs/Ant_Design_%E6%A0%B7%E5%BC%8F%E8%A6%86%E7%9B%96.md)
- I18n: [多语言支持 (@musnow)](./src/locales/index.js)
- 生产环境默认不加载 `mock`,更多详情请看 `src/mock/index.js`
- **用于生产环境,请使用 `release` 版本代码,使用 master 代码出现的任何问题需要你自行解决**
## 浏览器兼容
Modern browsers and IE10.
| [ ](http://godban.github.io/browsers-support-badges/)IE / Edge | [ ](http://godban.github.io/browsers-support-badges/)Firefox | [ ](http://godban.github.io/browsers-support-badges/)Chrome | [ ](http://godban.github.io/browsers-support-badges/)Safari | [ ](http://godban.github.io/browsers-support-badges/)Opera |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IE10, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## Contributors
This project exists thanks to all the people who contribute.
================================================
FILE: babel.config.js
================================================
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
const IS_PREVIEW = process.env.VUE_APP_PREVIEW === 'true'
const plugins = []
if (IS_PROD && !IS_PREVIEW) {
// 去除日志的插件,
plugins.push('transform-remove-console')
}
// lazy load ant-design-vue
// if your use import on Demand, Use this code
plugins.push(['import', {
'libraryName': 'ant-design-vue',
'libraryDirectory': 'es',
'style': true // `style: true` 会加载 less 文件
}])
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': 3
}
]
],
plugins
}
================================================
FILE: commitlint.config.js
================================================
/**
* feat:新增功能
* fix:bug 修复
* docs:文档更新
* style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
* refactor:重构代码(既没有新增功能,也没有修复 bug)
* perf:性能, 体验优化
* test:新增测试用例或是更新现有测试
* build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
* ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
* chore:不属于以上类型的其他类型,比如构建流程, 依赖管理
* revert:回滚某个更早之前的提交
*/
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'revert']
],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never']
}
}
================================================
FILE: config/plugin.config.js
================================================
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default
const getAntdSerials = (color) => {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#1890ff'), // 主色系列
// 改变样式选择器,解决样式覆盖问题
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
// fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
case '.ant-steps-item-process .ant-steps-item-icon':
return ':not(.ant-steps-item-custom)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)
module.exports = createThemeColorReplacerPlugin
================================================
FILE: config/themePluginConfig.js
================================================
export default {
theme: [
{
key: 'dark',
fileName: 'dark.css',
theme: 'dark'
},
{
key: '#F5222D',
fileName: '#F5222D.css',
modifyVars: {
'@primary-color': '#F5222D'
}
},
{
key: '#FA541C',
fileName: '#FA541C.css',
modifyVars: {
'@primary-color': '#FA541C'
}
},
{
key: '#FAAD14',
fileName: '#FAAD14.css',
modifyVars: {
'@primary-color': '#FAAD14'
}
},
{
key: '#13C2C2',
fileName: '#13C2C2.css',
modifyVars: {
'@primary-color': '#13C2C2'
}
},
{
key: '#52C41A',
fileName: '#52C41A.css',
modifyVars: {
'@primary-color': '#52C41A'
}
},
{
key: '#2F54EB',
fileName: '#2F54EB.css',
modifyVars: {
'@primary-color': '#2F54EB'
}
},
{
key: '#722ED1',
fileName: '#722ED1.css',
modifyVars: {
'@primary-color': '#722ED1'
}
},
{
key: '#F5222D',
theme: 'dark',
fileName: 'dark-#F5222D.css',
modifyVars: {
'@primary-color': '#F5222D'
}
},
{
key: '#FA541C',
theme: 'dark',
fileName: 'dark-#FA541C.css',
modifyVars: {
'@primary-color': '#FA541C'
}
},
{
key: '#FAAD14',
theme: 'dark',
fileName: 'dark-#FAAD14.css',
modifyVars: {
'@primary-color': '#FAAD14'
}
},
{
key: '#13C2C2',
theme: 'dark',
fileName: 'dark-#13C2C2.css',
modifyVars: {
'@primary-color': '#13C2C2'
}
},
{
key: '#52C41A',
theme: 'dark',
fileName: 'dark-#52C41A.css',
modifyVars: {
'@primary-color': '#52C41A'
}
},
{
key: '#2F54EB',
theme: 'dark',
fileName: 'dark-#2F54EB.css',
modifyVars: {
'@primary-color': '#2F54EB'
}
},
{
key: '#722ED1',
theme: 'dark',
fileName: 'dark-#722ED1.css',
modifyVars: {
'@primary-color': '#722ED1'
}
}
]
}
================================================
FILE: deploy/caddy.conf
================================================
0.0.0.0:80 {
gzip
root /usr/share/nginx/html
rewrite {
r .*
to {path} /
}
}
================================================
FILE: deploy/nginx.conf
================================================
server {
listen 80;
server_name _;
# gzip config
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
location / {
try_files $uri $uri/ /index.html;
}
# location /api {
# proxy_pass https://preview.pro.antdv.com/api;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Real-IP $remote_addr;
# }
}
================================================
FILE: docs/add-page-loading-animate.md
================================================
为首屏增加 加载动画
====
## 需求
> 为了缓解用户第一次访问时,加载 JS 过大所导致用户等待白屏时间过长导致的用户体验不好,进行的一个优化动效。
## 实现方案
1. 将 动画加载 dom 元素放在 #app 内,Vue 生命周期开始时,会自动清掉 #app 下的所有元素。
2. 将 动画加载 dom 元素放在 body 下,Vue 生命周期开始时 App.vue (created, mounted) 调用 `@/utils/utll` 下的 removeLoadingAnimate(#id, timeout) 则会移除加载动画
最后一步:
将样式插入到 `public/index.html` 文件的 `` 最好写成内联 ``
----
目前提供有两个样式,均在 `public/loading` 文件夹内。且 pro 已经默认使用了一套 loading 动画方案,可以直接参考 `public/index.html`
## 写在最后
目前 pro 有页面 overflow 显示出浏览器滚动条时,页面会抖动一下的问题。
欢迎各位提供能解决的方案和实现 demo。如果在条件允许的情况下,建议请直接使用 pro 进行改造,也欢迎直接 PR 到 pro 的仓库
================================================
FILE: docs/webpack-bundle-analyzer.md
================================================
先增加依赖
```bash
// npm
$ npm install --save-dev webpack-bundle-analyzer
// or yarn
$ yarn add webpack-bundle-analyzer -D
```
配置文件 `vue.config.js` 增加 `configureWebpack.plugins` 参数
```
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
function resolve (dir) {
return path.join(__dirname, dir)
}
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// 依赖大小分析工具
new BundleAnalyzerPlugin(),
]
},
...
}
```
启动 `cli` 的 `build` 命令进行项目编译,编译完成时,会自动运行一个 http://localhost:8888 的地址,完整显示了支持库依赖
================================================
FILE: jest.config.js
================================================
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/'
}
================================================
FILE: jsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"],
"include": ["src/**/*"]
}
================================================
FILE: package.json
================================================
{
"name": "vue-antd-pro",
"version": "3.0.4",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --no-module",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"build:preview": "vue-cli-service build --no-module --mode preview",
"lint:nofix": "vue-cli-service lint --no-fix",
"lint:js": "eslint src/**/*.js --fix",
"lint:css": "stylelint src/**/*.*ss --fix --custom-syntax postcss-less",
"prepare": "husky install"
},
"dependencies": {
"@ant-design-vue/pro-layout": "^1.0.12",
"@antv/data-set": "^0.10.2",
"ant-design-vue": "^1.7.8",
"axios": "^0.26.1",
"babel-loader": "8",
"core-js": "^3.21.1",
"enquire.js": "^2.1.6",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.3.0",
"mockjs2": "1.0.8",
"moment": "^2.29.2",
"nprogress": "^0.2.0",
"store": "^2.0.12",
"viser-vue": "^2.4.8",
"vue": "^2.6.14",
"vue-clipboard2": "^0.2.1",
"vue-cropper": "0.4.9",
"vue-i18n": "^8.27.1",
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.5.3",
"vue-svg-component-runtime": "^1.0.1",
"vue-template-compiler": "^2.6.14",
"vuex": "^3.6.2",
"wangeditor": "^3.1.1"
},
"devDependencies": {
"@ant-design/colors": "^3.2.2",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@vue/babel-helper-vue-jsx-merge-props": "^1.2.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
"@vue/cli-plugin-unit-jest": "~5.0.8",
"@vue/cli-plugin-vuex": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.3.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.3",
"babel-plugin-transform-remove-console": "^6.9.4",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.0.0",
"eslint-plugin-html": "^5.0.5",
"eslint-plugin-vue": "^5.2.3",
"file-loader": "^6.2.0",
"git-revision-webpack-plugin": "^3.0.6",
"husky": "^6.0.0",
"less": "^3.13.1",
"less-loader": "^5.0.0",
"lint-staged": "^12.5.0",
"postcss": "^8.3.5",
"postcss-less": "^6.0.0",
"regenerator-runtime": "^0.13.9",
"stylelint": "^14.8.5",
"stylelint-config-css-modules": "^4.1.0",
"stylelint-config-recess-order": "^3.0.0",
"stylelint-config-recommended": "^7.0.0",
"stylelint-config-standard": "^25.0.0",
"stylelint-order": "^5.0.0",
"vue-svg-icon-loader": "^2.1.1",
"vue-svg-loader": "0.16.0",
"webpack": "^5.74.0",
"webpack-theme-color-replacer": "^1.3.26"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"gitHooks": {
"pre-commit": "lint-staged"
}
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: {
autoprefixer: {}
}
}
================================================
FILE: public/index.html
================================================
Ant Design Pro
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<% } %>
We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<% } %>
================================================
FILE: src/App.vue
================================================
================================================
FILE: src/api/login.js
================================================
import request from '@/utils/request'
const userApi = {
Login: '/auth/login',
Logout: '/auth/logout',
ForgePassword: '/auth/forge-password',
Register: '/auth/register',
twoStepCode: '/auth/2step-code',
SendSms: '/account/sms',
SendSmsErr: '/account/sms_err',
// get my info
UserInfo: '/user/info',
UserMenu: '/user/nav'
}
/**
* login func
* parameter: {
* username: '',
* password: '',
* remember_me: true,
* captcha: '12345'
* }
* @param parameter
* @returns {*}
*/
export function login (parameter) {
return request({
url: userApi.Login,
method: 'post',
data: parameter
})
}
export function getSmsCaptcha (parameter) {
return request({
url: userApi.SendSms,
method: 'post',
data: parameter
})
}
export function getInfo () {
return request({
url: userApi.UserInfo,
method: 'get',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
export function getCurrentUserNav () {
return request({
url: userApi.UserMenu,
method: 'get'
})
}
export function logout () {
return request({
url: userApi.Logout,
method: 'post',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
/**
* get user 2step code open?
* @param parameter {*}
*/
export function get2step (parameter) {
return request({
url: userApi.twoStepCode,
method: 'post',
data: parameter
})
}
================================================
FILE: src/api/manage.js
================================================
import request from '@/utils/request'
const api = {
user: '/user',
role: '/role',
service: '/service',
permission: '/permission',
permissionNoPager: '/permission/no-pager',
orgTree: '/org/tree'
}
export default api
export function getUserList (parameter) {
return request({
url: api.user,
method: 'get',
params: parameter
})
}
export function getRoleList (parameter) {
return request({
url: api.role,
method: 'get',
params: parameter
})
}
export function getServiceList (parameter) {
return request({
url: api.service,
method: 'get',
params: parameter
})
}
export function getPermissions (parameter) {
return request({
url: api.permissionNoPager,
method: 'get',
params: parameter
})
}
export function getOrgTree (parameter) {
return request({
url: api.orgTree,
method: 'get',
params: parameter
})
}
// id == 0 add post
// id != 0 update put
export function saveService (parameter) {
return request({
url: api.service,
method: parameter.id === 0 ? 'post' : 'put',
data: parameter
})
}
export function saveSub (sub) {
return request({
url: '/sub',
method: sub.id === 0 ? 'post' : 'put',
data: sub
})
}
================================================
FILE: src/components/ArticleListContent/ArticleListContent.vue
================================================
================================================
FILE: src/components/ArticleListContent/index.js
================================================
import ArticleListContent from './ArticleListContent'
export default ArticleListContent
================================================
FILE: src/components/AvatarList/Item.jsx
================================================
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import { Tooltip, Avatar } from 'ant-design-vue'
import { getSlotOptions } from 'ant-design-vue/lib/_util/props-util'
import { warning } from 'ant-design-vue/lib/vc-util/warning'
export const AvatarListItemProps = {
tips: PropTypes.string,
src: PropTypes.string.def('')
}
const Item = {
__ANT_AVATAR_CHILDREN: true,
name: 'AvatarListItem',
props: AvatarListItemProps,
created () {
warning(getSlotOptions(this.$parent).__ANT_AVATAR_LIST, 'AvatarListItem must be a subcomponent of AvatarList')
},
render () {
const size = this.$parent.size === 'mini' ? 'small' : this.$parent.size
const AvatarDom =
return (this.tips && {AvatarDom} ) ||
}
}
export default Item
================================================
FILE: src/components/AvatarList/List.jsx
================================================
import './index.less'
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import Avatar from 'ant-design-vue/es/avatar'
import Item from './Item.jsx'
import { filterEmpty } from '@/components/_util/util'
/**
* size: `number`、 `large`、`small`、`default` 默认值: default
* maxLength: number
* excessItemsStyle: CSSProperties
*/
const AvatarListProps = {
prefixCls: PropTypes.string.def('ant-pro-avatar-list'),
size: {
validator: val => {
return typeof val === 'number' || ['small', 'large', 'default'].includes(val)
},
default: 'default'
},
maxLength: PropTypes.number.def(0),
excessItemsStyle: PropTypes.object.def({
color: '#f56a00',
backgroundColor: '#fde3cf'
})
}
const AvatarList = {
__ANT_AVATAR_LIST: true,
Item,
name: 'AvatarList',
props: AvatarListProps,
render (h) {
const { prefixCls, size } = this.$props
const className = {
[`${prefixCls}`]: true,
[`${size}`]: true
}
const items = filterEmpty(this.$slots.default)
const itemsDom = items && items.length ? : null
return (
{itemsDom}
)
},
methods: {
getItems (items) {
const className = {
[`${this.prefixCls}-item`]: true,
[`${this.size}`]: true
}
const totalSize = items.length
if (this.maxLength > 0) {
items = items.slice(0, this.maxLength)
items.push(({`+${totalSize - this.maxLength}`} ))
}
return items.map((item) => (
{item}
))
}
}
}
AvatarList.install = function (Vue) {
Vue.component(AvatarList.name, AvatarList)
Vue.component(AvatarList.Item.name, AvatarList.Item)
}
export default AvatarList
================================================
FILE: src/components/AvatarList/index.js
================================================
import AvatarList from './List'
import Item from './Item'
export {
AvatarList,
Item as AvatarListItem
}
export default AvatarList
================================================
FILE: src/components/AvatarList/index.less
================================================
@import '../index';
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
.@{avatar-list-prefix-cls} {
display: inline-block;
ul {
display: inline-block;
padding: 0;
margin: 0 0 0 8px;
font-size: 0;
list-style: none;
}
}
.@{avatar-list-item-prefix-cls} {
display: inline-block;
width: @avatar-size-base;
height: @avatar-size-base;
margin-left: -8px;
font-size: @font-size-base;
:global {
.ant-avatar {
cursor: pointer;
border: 1px solid #fff;
}
}
&.large {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
&.small {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
&.mini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
}
================================================
FILE: src/components/AvatarList/index.md
================================================
# AvatarList 用户头像列表
一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
引用方式:
```javascript
import AvatarList from '@/components/AvatarList'
const AvatarListItem = AvatarList.Item
export default {
components: {
AvatarList,
AvatarListItem
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
```
或
```html
```
## API
### AvatarList
| 参数 | 说明 | 类型 | 默认值 |
| ---------------- | -------- | ---------------------------------- | --------- |
| size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` |
| maxLength | 要显示的最大项目 | number | - |
| excessItemsStyle | 多余的项目风格 | CSSProperties | - |
### AvatarList.Item
| 参数 | 说明 | 类型 | 默认值 |
| ---- | ------ | --------- | --- |
| tips | 头像展示文案 | string | - |
| src | 头像图片连接 | string | - |
================================================
FILE: src/components/Charts/Bar.vue
================================================
{{ title }}
================================================
FILE: src/components/Charts/ChartCard.vue
================================================
================================================
FILE: src/components/Charts/Liquid.vue
================================================
================================================
FILE: src/components/Charts/MiniArea.vue
================================================
================================================
FILE: src/components/Charts/MiniBar.vue
================================================
================================================
FILE: src/components/Charts/MiniProgress.vue
================================================
================================================
FILE: src/components/Charts/MiniSmoothArea.vue
================================================
================================================
FILE: src/components/Charts/Radar.vue
================================================
================================================
FILE: src/components/Charts/RankList.vue
================================================
{{ title }}
{{ index + 1 }}
{{ item.name }}
{{ item.total }}
================================================
FILE: src/components/Charts/TagCloud.vue
================================================
================================================
FILE: src/components/Charts/TransferBar.vue
================================================
{{ title }}
================================================
FILE: src/components/Charts/Trend.vue
================================================
================================================
FILE: src/components/Charts/chart.less
================================================
.antv-chart-mini {
position: relative;
width: 100%;
.chart-wrapper {
position: absolute;
bottom: -28px;
width: 100%;
/* margin: 0 -5px;
overflow: hidden; */
}
}
================================================
FILE: src/components/Charts/smooth.area.less
================================================
@import '../index';
@smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area";
.@{smoothArea-prefix-cls} {
position: relative;
width: 100%;
.chart-wrapper {
position: absolute;
bottom: -28px;
width: 100%;
}
}
================================================
FILE: src/components/Dialog.js
================================================
import Modal from 'ant-design-vue/es/modal'
export default (Vue) => {
function dialog (component, componentProps, modalProps) {
const _vm = this
modalProps = modalProps || {}
if (!_vm || !_vm._isVue) {
return
}
let dialogDiv = document.querySelector('body>div[type=dialog]')
if (!dialogDiv) {
dialogDiv = document.createElement('div')
dialogDiv.setAttribute('type', 'dialog')
document.body.appendChild(dialogDiv)
}
const handle = function (checkFunction, afterHandel) {
if (checkFunction instanceof Function) {
const res = checkFunction()
if (res instanceof Promise) {
res.then(c => {
c && afterHandel()
})
} else {
res && afterHandel()
}
} else {
// checkFunction && afterHandel()
checkFunction || afterHandel()
}
}
const dialogInstance = new Vue({
data () {
return {
visible: true
}
},
router: _vm.$router,
store: _vm.$store,
mounted () {
this.$on('close', (v) => {
this.handleClose()
})
},
methods: {
handleClose () {
handle(this.$refs._component.onCancel, () => {
this.visible = false
this.$refs._component.$emit('close')
this.$refs._component.$emit('cancel')
dialogInstance.$destroy()
})
},
handleOk () {
handle(this.$refs._component.onOK || this.$refs._component.onOk, () => {
this.visible = false
this.$refs._component.$emit('close')
this.$refs._component.$emit('ok')
dialogInstance.$destroy()
})
}
},
render: function (h) {
const that = this
const modalModel = modalProps && modalProps.model
if (modalModel) {
delete modalProps.model
}
const ModalProps = Object.assign({}, modalModel && { model: modalModel } || {}, {
attrs: Object.assign({}, {
...(modalProps.attrs || modalProps)
}, {
visible: this.visible
}),
on: Object.assign({}, {
...(modalProps.on || modalProps)
}, {
ok: () => {
that.handleOk()
},
cancel: () => {
that.handleClose()
}
})
})
const componentModel = componentProps && componentProps.model
if (componentModel) {
delete componentProps.model
}
const ComponentProps = Object.assign({}, componentModel && { model: componentModel } || {}, {
ref: '_component',
attrs: Object.assign({}, {
...((componentProps && componentProps.attrs) || componentProps)
}),
on: Object.assign({}, {
...((componentProps && componentProps.on) || componentProps)
})
})
return h(Modal, ModalProps, [h(component, ComponentProps)])
}
}).$mount(dialogDiv)
}
Object.defineProperty(Vue.prototype, '$dialog', {
get: () => {
return function () {
dialog.apply(this, arguments)
}
}
})
}
================================================
FILE: src/components/Editor/QuillEditor.vue
================================================
================================================
FILE: src/components/Editor/WangEditor.vue
================================================
================================================
FILE: src/components/Ellipsis/Ellipsis.vue
================================================
================================================
FILE: src/components/Ellipsis/index.js
================================================
import Ellipsis from './Ellipsis'
export default Ellipsis
================================================
FILE: src/components/Ellipsis/index.md
================================================
# Ellipsis 文本自动省略号
文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
引用方式:
```javascript
import Ellipsis from '@/components/Ellipsis'
export default {
components: {
Ellipsis
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
There were injuries alleged in three cases in 2015, and a
fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
```
## API
参数 | 说明 | 类型 | 默认值
----|------|-----|------
tooltip | 移动到文本展示完整内容的提示 | boolean | -
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -
================================================
FILE: src/components/FooterToolbar/FooterToolBar.vue
================================================
================================================
FILE: src/components/FooterToolbar/index.js
================================================
import FooterToolBar from './FooterToolBar'
import './index.less'
export default FooterToolBar
================================================
FILE: src/components/FooterToolbar/index.less
================================================
@import '../index';
@footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar";
.@{footer-toolbar-prefix-cls} {
position: fixed;
right: 0;
bottom: 0;
z-index: 9;
width: 100%;
height: 56px;
padding: 0 24px;
line-height: 56px;
background: #fff;
box-shadow: 0 -1px 2px rgb(0 0 0 / 3%);
border-top: 1px solid #e8e8e8;
&::after {
display: block;
clear: both;
content: '';
}
}
================================================
FILE: src/components/FooterToolbar/index.md
================================================
# FooterToolbar 底部工具栏
固定在底部的工具栏。
## 何时使用
固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。
引用方式:
```javascript
import FooterToolBar from '@/components/FooterToolbar'
export default {
components: {
FooterToolBar
}
}
```
## 代码演示
```html
提交
```
或
```html
提交
```
## API
参数 | 说明 | 类型 | 默认值
----|------|-----|------
children (slot) | 工具栏内容,向右对齐 | - | -
extra | 额外信息,向左对齐 | String, Object | -
================================================
FILE: src/components/GlobalFooter/index.vue
================================================
================================================
FILE: src/components/GlobalHeader/AvatarDropdown.vue
================================================
{{ currentUser.name }}
================================================
FILE: src/components/GlobalHeader/RightContent.vue
================================================
================================================
FILE: src/components/IconSelector/IconSelector.vue
================================================
================================================
FILE: src/components/IconSelector/README.md
================================================
IconSelector
====
> 图标选择组件,常用于为某一个数据设定一个图标时使用
> eg: 设定菜单列表时,为每个菜单设定一个图标
该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装
### 使用方式
```vue
```
### 事件
| 名称 | 说明 | 类型 | 默认值 |
| ------ | -------------------------- | ------ | ------ |
| change | 当改变了 `icon` 选中项触发 | String | - |
================================================
FILE: src/components/IconSelector/icons.js
================================================
/**
* 增加新的图标时,请遵循以下数据结构
* Adding new icon please follow the data structure below
*/
export default [
{
key: 'directional',
title: '方向性图标',
icons: ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrows-alt', 'down', 'up', 'left', 'right', 'caret-up', 'caret-down', 'caret-left', 'caret-right', 'up-circle', 'down-circle', 'left-circle', 'right-circle', 'double-right', 'double-left', 'vertical-left', 'vertical-right', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap', 'swap-left', 'swap-right', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'play-circle', 'up-square', 'down-square', 'left-square', 'right-square', 'login', 'logout', 'menu-fold', 'menu-unfold', 'border-bottom', 'border-horizontal', 'border-inner', 'border-left', 'border-right', 'border-top', 'border-verticle', 'pic-center', 'pic-left', 'pic-right', 'radius-bottomleft', 'radius-bottomright', 'radius-upleft', 'fullscreen', 'fullscreen-exit']
},
{
key: 'suggested',
title: '提示建议性图标',
icons: ['question', 'question-circle', 'plus', 'plus-circle', 'pause', 'pause-circle', 'minus', 'minus-circle', 'plus-square', 'minus-square', 'info', 'info-circle', 'exclamation', 'exclamation-circle', 'close', 'close-circle', 'close-square', 'check', 'check-circle', 'check-square', 'clock-circle', 'warning', 'issues-close', 'stop']
},
{
key: 'editor',
title: '编辑类图标',
icons: ['edit', 'form', 'copy', 'scissor', 'delete', 'snippets', 'diff', 'highlight', 'align-center', 'align-left', 'align-right', 'bg-colors', 'bold', 'italic', 'underline', 'strikethrough', 'redo', 'undo', 'zoom-in', 'zoom-out', 'font-colors', 'font-size', 'line-height', 'colum-height', 'dash', 'small-dash', 'sort-ascending', 'sort-descending', 'drag', 'ordered-list', 'radius-setting']
},
{
key: 'data',
title: '数据类图标',
icons: ['area-chart', 'pie-chart', 'bar-chart', 'dot-chart', 'line-chart', 'radar-chart', 'heat-map', 'fall', 'rise', 'stock', 'box-plot', 'fund', 'sliders']
},
{
key: 'brand_logo',
title: '网站通用图标',
icons: ['lock', 'unlock', 'bars', 'book', 'calendar', 'cloud', 'cloud-download', 'code', 'copy', 'credit-card', 'delete', 'desktop', 'download', 'ellipsis', 'file', 'file-text', 'file-unknown', 'file-pdf', 'file-word', 'file-excel', 'file-jpg', 'file-ppt', 'file-markdown', 'file-add', 'folder', 'folder-open', 'folder-add', 'hdd', 'frown', 'meh', 'smile', 'inbox', 'laptop', 'appstore', 'link', 'mail', 'mobile', 'notification', 'paper-clip', 'picture', 'poweroff', 'reload', 'search', 'setting', 'share-alt', 'shopping-cart', 'tablet', 'tag', 'tags', 'to-top', 'upload', 'user', 'video-camera', 'home', 'loading', 'loading-3-quarters', 'cloud-upload', 'star', 'heart', 'environment', 'eye', 'camera', 'save', 'team', 'solution', 'phone', 'filter', 'exception', 'export', 'customer-service', 'qrcode', 'scan', 'like', 'dislike', 'message', 'pay-circle', 'calculator', 'pushpin', 'bulb', 'select', 'switcher', 'rocket', 'bell', 'disconnect', 'database', 'compass', 'barcode', 'hourglass', 'key', 'flag', 'layout', 'printer', 'sound', 'usb', 'skin', 'tool', 'sync', 'wifi', 'car', 'schedule', 'user-add', 'user-delete', 'usergroup-add', 'usergroup-delete', 'man', 'woman', 'shop', 'gift', 'idcard', 'medicine-box', 'red-envelope', 'coffee', 'copyright', 'trademark', 'safety', 'wallet', 'bank', 'trophy', 'contacts', 'global', 'shake', 'api', 'fork', 'dashboard', 'table', 'profile', 'alert', 'audit', 'branches', 'build', 'border', 'crown', 'experiment', 'fire', 'money-collect', 'property-safety', 'read', 'reconciliation', 'rest', 'security-scan', 'insurance', 'interation', 'safety-certificate', 'project', 'thunderbolt', 'block', 'cluster', 'deployment-unit', 'dollar', 'euro', 'pound', 'file-done', 'file-exclamation', 'file-protect', 'file-search', 'file-sync', 'gateway', 'gold', 'robot', 'shopping']
},
{
key: 'application',
title: '品牌和标识',
icons: ['android', 'apple', 'windows', 'ie', 'chrome', 'github', 'aliwangwang', 'dingding', 'weibo-square', 'weibo-circle', 'taobao-circle', 'html5', 'weibo', 'twitter', 'wechat', 'youtube', 'alipay-circle', 'taobao', 'skype', 'qq', 'medium-workmark', 'gitlab', 'medium', 'linkedin', 'google-plus', 'dropbox', 'facebook', 'codepen', 'code-sandbox', 'amazon', 'google', 'codepen-circle', 'alipay', 'ant-design', 'aliyun', 'zhihu', 'slack', 'slack-square', 'behance', 'behance-square', 'dribbble', 'dribbble-square', 'instagram', 'yuque', 'alibaba', 'yahoo']
}
]
================================================
FILE: src/components/IconSelector/index.js
================================================
import IconSelector from './IconSelector'
export default IconSelector
================================================
FILE: src/components/MultiTab/MultiTab.vue
================================================
================================================
FILE: src/components/MultiTab/events.js
================================================
import Vue from 'vue'
export default new Vue()
================================================
FILE: src/components/MultiTab/index.js
================================================
import events from './events'
import MultiTab from './MultiTab'
import './index.less'
const api = {
/**
* open new tab on route fullPath
* @param config
*/
open: function (config) {
events.$emit('open', config)
},
rename: function (key, name) {
events.$emit('rename', { key: key, name: name })
},
/**
* close current page
*/
closeCurrentPage: function () {
this.close()
},
/**
* close route fullPath tab
* @param config
*/
close: function (config) {
events.$emit('close', config)
}
}
MultiTab.install = function (Vue) {
if (Vue.prototype.$multiTab) {
return
}
api.instance = events
Vue.prototype.$multiTab = api
Vue.component('multi-tab', MultiTab)
}
export default MultiTab
================================================
FILE: src/components/MultiTab/index.less
================================================
@import '../index';
@multi-tab-prefix-cls: ~"@{ant-pro-prefix}-multi-tab";
@multi-tab-wrapper-prefix-cls: ~"@{ant-pro-prefix}-multi-tab-wrapper";
/*
.topmenu .@{multi-tab-prefix-cls} {
max-width: 1200px;
margin: -23px auto 24px auto;
}
*/
.@{multi-tab-prefix-cls} {
margin: -23px -24px 24px;
background: #fff;
}
.topmenu .@{multi-tab-wrapper-prefix-cls} {
max-width: 1200px;
margin: 0 auto;
}
.topmenu.content-width-Fluid .@{multi-tab-wrapper-prefix-cls} {
max-width: 100%;
margin: 0 auto;
}
================================================
FILE: src/components/NProgress/nprogress.less
================================================
@import url('../index.less');
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
position: fixed;
top: 0;
left: 0;
z-index: 1031;
width: 100%;
height: 2px;
background: @primary-color;
}
/* Fancy blur effect */
#nprogress .peg {
position: absolute;
right: 0;
display: block;
width: 100px;
height: 100%;
opacity: 1;
transform: rotate(3deg) translate(0, -4px);
transform: rotate(3deg) translate(0, -4px);
transform: rotate(3deg) translate(0, -4px);
box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color;
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
position: fixed;
top: 15px;
right: 15px;
z-index: 1031;
display: block;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: @primary-color;
border-left-color: @primary-color;
border-radius: 50%;
animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
position: relative;
overflow: hidden;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
================================================
FILE: src/components/NoticeIcon/NoticeIcon.vue
================================================
123
123
================================================
FILE: src/components/NoticeIcon/index.js
================================================
import NoticeIcon from './NoticeIcon'
export default NoticeIcon
================================================
FILE: src/components/NumberInfo/NumberInfo.vue
================================================
{{ typeof subTitle === 'string' ? subTitle : subTitle() }}
{{ total }}
{{ subTotal }}
================================================
FILE: src/components/NumberInfo/index.js
================================================
import NumberInfo from './NumberInfo'
export default NumberInfo
================================================
FILE: src/components/NumberInfo/index.less
================================================
@import '../index';
@numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info";
.@{numberInfo-prefix-cls} {
.ant-pro-number-info-subtitle {
height: 22px;
overflow: hidden;
font-size: @font-size-base;
line-height: 22px;
color: @text-color-secondary;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
.number-info-value {
margin-top: 4px;
overflow: hidden;
font-size: 0;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
& > span {
display: inline-block;
height: 32px;
margin-right: 32px;
font-size: 24px;
line-height: 32px;
color: @heading-color;
}
.sub-total {
margin-right: 0;
font-size: @font-size-lg;
color: @text-color-secondary;
vertical-align: top;
i {
margin-left: 4px;
font-size: 12px;
transform: scale(.82);
}
.anticon-caret-up {
color: @red-6;
}
.anticon-caret-down {
color: @green-6;
}
}
}
}
================================================
FILE: src/components/NumberInfo/index.md
================================================
# NumberInfo 数据文本
常用在数据卡片中,用于突出展示某个业务数据。
引用方式:
```javascript
import NumberInfo from '@/components/NumberInfo'
export default {
components: {
NumberInfo
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
```
## API
参数 | 说明 | 类型 | 默认值
----|------|-----|------
title | 标题 | ReactNode\|string | -
subTitle | 子标题 | ReactNode\|string | -
total | 总量 | ReactNode\|string | -
subTotal | 子总量 | ReactNode\|string | -
status | 增加状态 | 'up \| down' | -
theme | 状态样式 | string | 'light'
gap | 设置数字和描述之间的间距(像素)| number | 8
================================================
FILE: src/components/Other/CarbonAds.vue
================================================
================================================
FILE: src/components/PageLoading/index.jsx
================================================
import { Spin } from 'ant-design-vue'
export const PageLoading = {
name: 'PageLoading',
props: {
tip: {
type: String,
default: 'Loading..'
},
size: {
type: String,
default: 'large'
}
},
render () {
const style = {
textAlign: 'center',
background: 'rgba(0,0,0,0.6)',
position: 'fixed',
top: 0,
bottom: 0,
left: 0,
right: 0,
zIndex: 1100
}
const spinStyle = {
position: 'absolute',
left: '50%',
top: '40%',
transform: 'translate(-50%, -50%)'
}
return (
)
}
}
const version = '0.0.1'
const loading = {}
loading.newInstance = (Vue, options) => {
let loadingElement = document.querySelector('body>div[type=loading]')
if (!loadingElement) {
loadingElement = document.createElement('div')
loadingElement.setAttribute('type', 'loading')
loadingElement.setAttribute('class', 'ant-loading-wrapper')
document.body.appendChild(loadingElement)
}
const cdProps = Object.assign({ visible: false, size: 'large', tip: 'Loading...' }, options)
const instance = new Vue({
data () {
return {
...cdProps
}
},
render () {
const { tip } = this
const props = {}
this.tip && (props.tip = tip)
if (this.visible) {
return
}
return null
}
}).$mount(loadingElement)
function update (config) {
const { visible, size, tip } = { ...cdProps, ...config }
instance.$set(instance, 'visible', visible)
if (tip) {
instance.$set(instance, 'tip', tip)
}
if (size) {
instance.$set(instance, 'size', size)
}
}
return {
instance,
update
}
}
const api = {
show: function (options) {
this.instance.update({ ...options, visible: true })
},
hide: function () {
this.instance.update({ visible: false })
}
}
const install = function (Vue, options) {
if (Vue.prototype.$loading) {
return
}
api.instance = loading.newInstance(Vue, options)
Vue.prototype.$loading = api
}
export default {
version,
install
}
================================================
FILE: src/components/Search/GlobalSearch.jsx
================================================
import { Select } from 'ant-design-vue'
import './index.less'
const GlobalSearch = {
name: 'GlobalSearch',
data () {
return {
visible: false
}
},
mounted () {
const keyboardHandle = (e) => {
e.preventDefault()
e.stopPropagation()
const { ctrlKey, shiftKey, altKey, keyCode } = e
console.log('keyCode:', e.keyCode, e)
// key is `K` and hold ctrl
if (keyCode === 75 && ctrlKey && !shiftKey && !altKey) {
this.visible = !this.visible
}
}
document.addEventListener('keydown', keyboardHandle)
},
render () {
const { visible } = this
const handleSearch = (e) => {
this.$emit('search', e)
}
const handleChange = (e) => {
this.$emit('change', e)
}
if (!visible) {
return null
}
return (
)
}
}
GlobalSearch.install = function (Vue) {
Vue.component(GlobalSearch.name, GlobalSearch)
}
export default GlobalSearch
================================================
FILE: src/components/Search/index.less
================================================
@import '~ant-design-vue/es/style/themes/default';
.global-search-wrapper {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: @zindex-modal-mask;
background: @modal-mask-bg;
.global-search-box {
position: absolute;
top: 20%;
left: 50%;
width: 450px;
transform: translate(-50%, -50%);
.global-search-tips {
font-size: @font-size-lg;
color: @white;
text-align: right;
}
}
}
================================================
FILE: src/components/SelectLang/index.jsx
================================================
import './index.less'
import { Icon, Menu, Dropdown } from 'ant-design-vue'
import { i18nRender } from '@/locales'
import i18nMixin from '@/store/i18n-mixin'
const locales = ['zh-CN', 'en-US']
const languageLabels = {
'zh-CN': '简体中文',
'en-US': 'English'
}
// eslint-disable-next-line
const languageIcons = {
'zh-CN': '🇨🇳',
'en-US': '🇺🇸'
}
const SelectLang = {
props: {
prefixCls: {
type: String,
default: 'ant-pro-drop-down'
}
},
name: 'SelectLang',
mixins: [i18nMixin],
render () {
const { prefixCls } = this
const changeLang = ({ key }) => {
this.setLang(key)
}
const langMenu = (
)
return (
)
}
}
export default SelectLang
================================================
FILE: src/components/SelectLang/index.less
================================================
@import '~ant-design-vue/es/style/themes/default';
@header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu';
@header-drop-down-prefix-cls: ~'@{ant-prefix}-pro-drop-down';
.@{header-menu-prefix-cls} {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 160px;
}
}
.@{header-drop-down-prefix-cls} {
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
> i {
font-size: 16px !important;
transform: none !important;
svg {
position: relative;
top: -1px;
}
}
}
================================================
FILE: src/components/SettingDrawer/SettingDrawer.vue
================================================
导航模式
该设定仅 [顶部栏导航] 时有效
固定
流式
内容区域宽度
固定 Header
固定 Header 时可配置
下滑时隐藏 Header
固定侧边菜单
================================================
FILE: src/components/SettingDrawer/SettingItem.vue
================================================
================================================
FILE: src/components/SettingDrawer/index.js
================================================
import SettingDrawer from './SettingDrawer'
export default SettingDrawer
================================================
FILE: src/components/SettingDrawer/settingConfig.js
================================================
import message from 'ant-design-vue/es/message'
// import defaultSettings from '../defaultSettings';
import themeColor from './themeColor.js'
// let lessNodesAppended
const colorList = [
{
key: '薄暮', color: '#F5222D'
},
{
key: '火山', color: '#FA541C'
},
{
key: '日暮', color: '#FAAD14'
},
{
key: '明青', color: '#13C2C2'
},
{
key: '极光绿', color: '#52C41A'
},
{
key: '拂晓蓝(默认)', color: '#1890FF'
},
{
key: '极客蓝', color: '#2F54EB'
},
{
key: '酱紫', color: '#722ED1'
}
]
const updateTheme = newPrimaryColor => {
const hideMessage = message.loading('正在切换主题!', 0)
themeColor.changeColor(newPrimaryColor).finally(() => {
setTimeout(() => {
hideMessage()
}, 10)
})
}
const updateColorWeak = colorWeak => {
// document.body.className = colorWeak ? 'colorWeak' : '';
const app = document.body.querySelector('#app')
colorWeak ? app.classList.add('colorWeak') : app.classList.remove('colorWeak')
}
export { updateTheme, colorList, updateColorWeak }
================================================
FILE: src/components/SettingDrawer/themeColor.js
================================================
import client from 'webpack-theme-color-replacer/client'
import generate from '@ant-design/colors/lib/generate'
export default {
getAntdSerials (color) {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return client.varyColor.lighten(color, i / 10)
})
// colorPalette变换得到颜色值
const colorPalettes = generate(color)
const rgb = client.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
},
changeColor (newColor) {
var options = {
newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
changeUrl (cssUrl) {
return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path
}
}
return client.changer.changeColor(options, Promise)
}
}
================================================
FILE: src/components/StandardFormRow/StandardFormRow.vue
================================================
================================================
FILE: src/components/StandardFormRow/index.js
================================================
import StandardFormRow from './StandardFormRow'
export default StandardFormRow
================================================
FILE: src/components/Table/README.md
================================================
Table 重封装组件说明
====
封装说明
----
> 基础的使用方式与 API 与 [官方版(Table)](https://vuecomponent.github.io/ant-design-vue/components/table-cn/) 本一致,在其基础上,封装了加载数据的方法。
>
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 Table 组件传递绑定 `:data="Promise"` 对象即可
该 `table` 由 [@Saraka](https://github.com/saraka-tsukai) 完成封装
例子1
----
(基础使用)
```vue
```
例子2
----
(简单的表格,最后一列是各种操作)
```vue
编辑
更多
1st menu item
2nd menu item
3rd menu item
```
内置方法
----
通过 `this.$refs.table` 调用
`this.$refs.table.refresh(true)` 刷新列表 (用户新增/修改数据后,重载列表数据)
> 注意:要调用 `refresh(bool)` 需要给表格组件设定 `ref` 值
>
> `refresh()` 方法可以传一个 `bool` 值,当有传值 或值为 `true` 时,则刷新时会强制刷新到第一页(常用户页面 搜索 按钮进行搜索时,结果从第一页开始分页)
内置属性
----
> 除去 `a-table` 自带属性外,还而外提供了一些额外属性属性
| 属性 | 说明 | 类型 | 默认值 |
| -------------- | ----------------------------------------------- | ----------------- | ------ |
| alert | 设置是否显示表格信息栏 | [object, boolean] | null |
| showPagination | 显示分页选择器,可传 'auto' \| boolean | [string, boolean] | 'auto' |
| data | 加载数据方法 必须为 `Promise` 对象 **必须绑定** | Promise | - |
`alert` 属性对象:
```javascript
alert: {
show: Boolean,
clear: [Function, Boolean]
}
```
注意事项
----
> 你可能需要为了与后端提供的接口返回结果一致而去修改以下代码:
> (需要注意的是,这里的修改是全局性的,意味着整个项目所有使用该 table 组件都需要遵守这个返回结果定义的字段。)
>
> 文档中的结构有可能由于组件 bug 进行修正而改动。实际修改请以当时最新版本为准
修改 `@/components/table/index.js` 第 156 行起
```javascript
result.then(r => {
this.localPagination = this.showPagination && Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
}) || false
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
if (r.data.length === 0 && this.showPagination && this.localPagination.current > 1) {
this.localPagination.current--
this.loadData()
return
}
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {
this.localPagination = false
}
console.log('loadData -> this.localPagination', this.localPagination)
this.localDataSource = r.data // 返回结果中的数组数据
this.localLoading = false
})
```
返回 JSON 例子:
```json
{
"message": "",
"result": {
"data": [{
id: 1,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
title: 'Alipay',
description: '那是一种内在的东西, 他们到达不了,也无法触及的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 2,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
title: 'Angular',
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 3,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
title: 'Ant Design',
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 4,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
title: 'Ant Design Pro',
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 5,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
title: 'Bootstrap',
description: '凛冬将至',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 6,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png',
title: 'Vue',
description: '生命就像一盒巧克力,结果往往出人意料',
status: 1,
updatedAt: '2018-07-26 00:00:00'
}
],
"pageSize": 10,
"pageNo": 0,
"totalPage": 6,
"totalCount": 57
},
"status": 200,
"timestamp": 1534955098193
}
```
更新时间
----
该文档最后更新于: 2019-06-23 PM 17:19
================================================
FILE: src/components/Table/index.js
================================================
import T from 'ant-design-vue/es/table/Table'
import get from 'lodash.get'
export default {
data () {
return {
needTotalList: [],
selectedRows: [],
selectedRowKeys: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, this.pagination),
// 存储表格onchange时的filters, sorter对象
filters: {},
sorter: {}
}
},
props: Object.assign({}, T.props, {
rowKey: {
type: [String, Function],
default: 'key'
},
data: {
type: Function,
required: true
},
pageNum: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
showSizeChanger: {
type: Boolean,
default: true
},
size: {
type: String,
default: 'default'
},
/**
* alert: {
* show: true,
* clear: Function
* }
*/
alert: {
type: [Object, Boolean],
default: null
},
rowSelection: {
type: Object,
default: null
},
/** @Deprecated */
showAlertInfo: {
type: Boolean,
default: false
},
showPagination: {
type: String | Boolean,
default: 'auto'
},
/**
* enable page URI mode
*
* e.g:
* /users/1
* /users/2
* /users/3?queryParam=test
* ...
*/
pageURI: {
type: Boolean,
default: false
}
}),
watch: {
'localPagination.current' (val) {
this.pageURI && this.$router.push({
...this.$route,
name: this.$route.name,
params: Object.assign({}, this.$route.params, {
pageNo: val
})
})
// change pagination, reset total data
this.needTotalList = this.initTotalList(this.columns)
this.selectedRowKeys = []
this.selectedRows = []
},
pageNum (val) {
Object.assign(this.localPagination, {
current: val
})
},
pageSize (val) {
Object.assign(this.localPagination, {
pageSize: val
})
},
showSizeChanger (val) {
Object.assign(this.localPagination, {
showSizeChanger: val
})
}
},
created () {
const { pageNo } = this.$route.params
const localPageNum = this.pageURI && (pageNo && parseInt(pageNo)) || this.pageNum
this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {
current: localPageNum,
pageSize: this.pageSize,
showSizeChanger: this.showSizeChanger
}) || false
this.needTotalList = this.initTotalList(this.columns)
this.loadData()
},
methods: {
/**
* 表格重新加载方法
* 如果参数为 true, 则强制刷新到第一页
* @param Boolean bool
*/
refresh (bool = false) {
bool && (this.localPagination = Object.assign({}, {
current: 1, pageSize: this.pageSize
}))
this.loadData()
},
/**
* 加载数据方法
* @param {Object} pagination 分页选项器
* @param {Object} filters 过滤条件
* @param {Object} sorter 排序条件
*/
loadData (pagination, filters = this.filters, sorter = this.sorter) {
this.filters = filters
this.sorter = sorter
this.localLoading = true
const parameter = Object.assign({
pageNo: (pagination && pagination.current) ||
this.showPagination && this.localPagination.current || this.pageNum,
pageSize: (pagination && pagination.pageSize) ||
this.showPagination && this.localPagination.pageSize || this.pageSize
},
(sorter && sorter.field && {
sortField: sorter.field
}) || {},
(sorter && sorter.order && {
sortOrder: sorter.order
}) || {}, {
...filters
}
)
const result = this.data(parameter)
// 对接自己的通用数据接口需要修改下方代码中的 r.pageNo, r.totalCount, r.data
// eslint-disable-next-line
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then(r => {
this.localPagination = this.showPagination && Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
}) || false
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
if (r.data.length === 0 && this.showPagination && this.localPagination.current > 1) {
this.localPagination.current--
this.loadData()
return
}
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {
this.localPagination = false
}
this.localDataSource = r.data // 返回结果中的数组数据
})
.finally(() => {
this.localLoading = false
})
}
},
initTotalList (columns) {
const totalList = []
columns && columns instanceof Array && columns.forEach(column => {
if (column.needTotal) {
totalList.push({
...column,
total: 0
})
}
})
return totalList
},
/**
* 用于更新已选中的列表数据 total 统计
* @param selectedRowKeys
* @param selectedRows
*/
updateSelect (selectedRowKeys, selectedRows) {
this.selectedRows = selectedRows
this.selectedRowKeys = selectedRowKeys
const list = this.needTotalList
this.needTotalList = list.map(item => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
const total = sum + parseInt(get(val, item.dataIndex))
return isNaN(total) ? 0 : total
}, 0)
}
})
},
/**
* 清空 table 已选中项
*/
clearSelected () {
if (this.rowSelection) {
this.rowSelection.onChange([], [])
this.updateSelect([], [])
}
},
/**
* 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用
* @param callback
* @returns {*}
*/
renderClear (callback) {
if (this.selectedRowKeys.length <= 0) return null
return (
{
callback()
this.clearSelected()
}}>清空
)
},
renderAlert () {
// 绘制统计列数据
const needTotalItems = this.needTotalList.map((item) => {
return (
{item.title}总计 {!item.customRender ? item.total : item.customRender(item.total)}
)
})
// 绘制 清空 按钮
const clearItem = (typeof this.alert.clear === 'boolean' && this.alert.clear) ? (
this.renderClear(this.clearSelected)
) : (this.alert !== null && typeof this.alert.clear === 'function') ? (
this.renderClear(this.alert.clear)
) : null
// 绘制 alert 组件
return (
已选择: {this.selectedRows.length}
{needTotalItems}
{clearItem}
)
}
},
render () {
const props = {}
const localKeys = Object.keys(this.$data)
const showAlert = (typeof this.alert === 'object' && this.alert !== null && this.alert.show) && typeof this.rowSelection.selectedRowKeys !== 'undefined' || this.alert
Object.keys(T.props).forEach(k => {
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
if (localKeys.includes(localKey)) {
props[k] = this[localKey]
return props[k]
}
if (k === 'rowSelection') {
if (showAlert && this.rowSelection) {
// 如果需要使用alert,则重新绑定 rowSelection 事件
props[k] = {
...this.rowSelection,
selectedRows: this.selectedRows,
selectedRowKeys: this.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.updateSelect(selectedRowKeys, selectedRows)
typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows)
}
}
return props[k]
} else if (!this.rowSelection) {
// 如果没打算开启 rowSelection 则清空默认的选择项
props[k] = null
return props[k]
}
}
this[k] && (props[k] = this[k])
return props[k]
})
const table = (
{ this.$emit('expand', expanded, record) } }>
{ Object.keys(this.$slots).map(name => ({this.$slots[name]} )) }
)
return (
{ showAlert ? this.renderAlert() : null }
{ table }
)
}
}
================================================
FILE: src/components/TagSelect/TagSelectOption.jsx
================================================
import { Tag } from 'ant-design-vue'
const { CheckableTag } = Tag
export default {
name: 'TagSelectOption',
props: {
prefixCls: {
type: String,
default: 'ant-pro-tag-select-option'
},
value: {
type: [String, Number, Object],
default: ''
},
checked: {
type: Boolean,
default: false
}
},
data () {
return {
localChecked: this.checked || false
}
},
watch: {
'checked' (val) {
this.localChecked = val
},
'$parent.items': {
handler: function (val) {
this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value])
},
deep: true
}
},
render () {
const { $slots, value } = this
const onChange = (checked) => {
this.$emit('change', { value, checked })
}
return (
{$slots.default}
)
}
}
================================================
FILE: src/components/TagSelect/index.jsx
================================================
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import Option from './TagSelectOption.jsx'
import { filterEmpty } from '@/components/_util/util'
export default {
Option,
name: 'TagSelect',
model: {
prop: 'checked',
event: 'change'
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-tag-select'
},
defaultValue: {
type: PropTypes.array,
default: null
},
value: {
type: PropTypes.array,
default: null
},
expandable: {
type: Boolean,
default: false
},
hideCheckAll: {
type: Boolean,
default: false
}
},
data () {
return {
expand: false,
localCheckAll: false,
items: this.getItemsKey(filterEmpty(this.$slots.default)),
val: this.value || this.defaultValue || []
}
},
methods: {
onChange (checked) {
const key = Object.keys(this.items).filter(key => key === checked.value)
this.items[key] = checked.checked
const bool = Object.values(this.items).lastIndexOf(false)
if (bool === -1) {
this.localCheckAll = true
} else {
this.localCheckAll = false
}
},
onCheckAll (checked) {
Object.keys(this.items).forEach(v => {
this.items[v] = checked.checked
})
this.localCheckAll = checked.checked
},
getItemsKey (items) {
const totalItem = {}
items.forEach(item => {
totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false
})
return totalItem
},
// CheckAll Button
renderCheckAll () {
const props = {
on: {
change: (checked) => {
this.onCheckAll(checked)
checked.value = 'total'
this.$emit('change', checked)
}
}
}
const checkAllElement = All
return !this.hideCheckAll && checkAllElement || null
},
// expandable
renderExpandable () {
},
// render option
renderTags (items) {
const listeners = {
change: (checked) => {
this.onChange(checked)
this.$emit('change', checked)
}
}
return items.map(vnode => {
const options = vnode.componentOptions
options.listeners = listeners
return vnode
})
}
},
render () {
const { $props: { prefixCls } } = this
const classString = {
[`${prefixCls}`]: true
}
const tagItems = filterEmpty(this.$slots.default)
return (
{this.renderCheckAll()}
{this.renderTags(tagItems)}
)
}
}
================================================
FILE: src/components/TextArea/index.jsx
================================================
import './style.less'
import { getStrFullLength, cutStrByFullLength } from '../_util/util'
import Input from 'ant-design-vue/es/input'
const TextArea = Input.TextArea
export default {
name: 'LimitTextArea',
model: {
prop: 'value',
event: 'change'
},
props: Object.assign({}, TextArea.props, {
prefixCls: {
type: String,
default: 'ant-textarea-limit'
},
// eslint-disable-next-line
value: {
type: String
},
limit: {
type: Number,
default: 200
}
}),
data () {
return {
currentLimit: 0
}
},
watch: {
value (val) {
this.calcLimitNum(val)
}
},
created () {
this.calcLimitNum(this.value)
},
methods: {
handleChange (e) {
const value = e.target.value
const len = getStrFullLength(value)
if (len <= this.limit) {
this.currentLimit = len
this.$emit('change', value)
return
} else {
const str = cutStrByFullLength(value, this.limit)
this.currentLimit = getStrFullLength(str)
this.$emit('change', str)
}
console.error('limit out! currentLimit:', this.currentLimit)
},
calcLimitNum (val) {
const len = getStrFullLength(val)
this.currentLimit = len
}
},
render () {
const { prefixCls, ...props } = this.$props
return (
{this.currentLimit}/{this.limit}
)
}
}
================================================
FILE: src/components/TextArea/style.less
================================================
.ant-textarea-limit {
position: relative;
.limit {
position: absolute;
right: 10px;
bottom: 5px;
font-size: 12px;
color: #909399;
background: #fff;
}
}
================================================
FILE: src/components/Tree/Tree.jsx
================================================
import { Menu, Icon, Input } from 'ant-design-vue'
const { Item, ItemGroup, SubMenu } = Menu
const { Search } = Input
export default {
name: 'Tree',
props: {
dataSource: {
type: Array,
required: true
},
openKeys: {
type: Array,
default: () => []
},
search: {
type: Boolean,
default: false
}
},
created () {
this.localOpenKeys = this.openKeys.slice(0)
},
data () {
return {
localOpenKeys: []
}
},
methods: {
handlePlus (item) {
this.$emit('add', item)
},
handleTitleClick (...args) {
this.$emit('titleClick', { args })
},
renderSearch () {
return (
)
},
renderIcon (icon) {
return icon && ( ) || null
},
renderMenuItem (item) {
return (
-
{ this.renderIcon(item.icon) }
{ item.title }
this.handlePlus(item) } }}>
)
},
renderItem (item) {
return item.children ? this.renderSubItem(item, item.key) : this.renderMenuItem(item, item.key)
},
renderItemGroup (item) {
const childrenItems = item.children.map(o => {
return this.renderItem(o, o.key)
})
return (
{ item.title }
新增
合并
移除
{ childrenItems }
)
},
renderSubItem (item, key) {
const childrenItems = item.children && item.children.map(o => {
return this.renderItem(o, o.key)
})
const title = (
{ this.renderIcon(item.icon) }
{ item.title }
)
if (item.group) {
return this.renderItemGroup(item)
}
// titleClick={this.handleTitleClick(item)}
return (
{ title }
{ childrenItems }
)
}
},
render () {
const { dataSource, search } = this.$props
// this.localOpenKeys = openKeys.slice(0)
const list = dataSource.map(item => {
return this.renderItem(item)
})
return (
{ search ? this.renderSearch() : null }
this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}>
{ list }
)
}
}
================================================
FILE: src/components/Trend/Trend.vue
================================================
================================================
FILE: src/components/Trend/index.js
================================================
import Trend from './Trend.vue'
export default Trend
================================================
FILE: src/components/Trend/index.less
================================================
@import '../index';
@trend-prefix-cls: ~"@{ant-pro-prefix}-trend";
.@{trend-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
line-height: 22px;
.up,
.down {
position: relative;
top: 1px;
margin-left: 4px;
i {
font-size: 12px;
transform: scale(.83);
}
}
.item-text {
display: inline-block;
margin-left: 8px;
color: rgb(0 0 0 / 85%);
}
.up {
color: @red-6;
}
.down {
top: -1px;
color: @green-6;
}
&.reverse-color .up {
color: @green-6;
}
&.reverse-color .down {
color: @red-6;
}
}
================================================
FILE: src/components/Trend/index.md
================================================
# Trend 趋势标记
趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。
引用方式:
```javascript
import Trend from '@/components/Trend'
export default {
components: {
Trend
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
5%
```
或
```html
工资
5%
```
或
```html
5%
```
## API
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| flag | 上升下降标识:`up|down` | string | - |
| reverseColor | 颜色反转 | Boolean | false |
================================================
FILE: src/components/_util/util.js
================================================
/**
* components util
*/
/**
* 清理空值,对象
* @param children
* @returns {*[]}
*/
export function filterEmpty (children = []) {
return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
}
/**
* 获取字符串长度,英文字符 长度1,中文字符长度2
* @param {*} str
*/
export const getStrFullLength = (str = '') =>
str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
return pre + 1
}
return pre + 2
}, 0)
/**
* 截取字符串,根据 maxLength 截取后返回
* @param {*} str
* @param {*} maxLength
*/
export const cutStrByFullLength = (str = '', maxLength) => {
let showLength = 0
return str.split('').reduce((pre, cur) => {
const charCode = cur.charCodeAt(0)
if (charCode >= 0 && charCode <= 128) {
showLength += 1
} else {
showLength += 2
}
if (showLength <= maxLength) {
return pre + cur
}
return pre
}, '')
}
================================================
FILE: src/components/index.js
================================================
// chart
import Bar from '@/components/Charts/Bar'
import ChartCard from '@/components/Charts/ChartCard'
import Liquid from '@/components/Charts/Liquid'
import MiniArea from '@/components/Charts/MiniArea'
import MiniSmoothArea from '@/components/Charts/MiniSmoothArea'
import MiniBar from '@/components/Charts/MiniBar'
import MiniProgress from '@/components/Charts/MiniProgress'
import Radar from '@/components/Charts/Radar'
import RankList from '@/components/Charts/RankList'
import TransferBar from '@/components/Charts/TransferBar'
import TagCloud from '@/components/Charts/TagCloud'
// pro components
import AvatarList from '@/components/AvatarList'
import Ellipsis from '@/components/Ellipsis'
import FooterToolbar from '@/components/FooterToolbar'
import NumberInfo from '@/components/NumberInfo'
import Tree from '@/components/Tree/Tree'
import Trend from '@/components/Trend'
import STable from '@/components/Table'
import MultiTab from '@/components/MultiTab'
import IconSelector from '@/components/IconSelector'
import TagSelect from '@/components/TagSelect'
import StandardFormRow from '@/components/StandardFormRow'
import ArticleListContent from '@/components/ArticleListContent'
import Dialog from '@/components/Dialog'
export {
AvatarList,
Bar,
ChartCard,
Liquid,
MiniArea,
MiniSmoothArea,
MiniBar,
MiniProgress,
Radar,
TagCloud,
RankList,
TransferBar,
Trend,
Ellipsis,
FooterToolbar,
NumberInfo,
Tree,
STable,
MultiTab,
IconSelector,
TagSelect,
StandardFormRow,
ArticleListContent,
Dialog
}
================================================
FILE: src/components/index.less
================================================
@import '~ant-design-vue/lib/style/index';
// The prefix to use on all css classes from ant-pro.
@ant-pro-prefix : ant-pro;
@ant-global-sider-zindex : 106;
@ant-global-header-zindex : 105;
================================================
FILE: src/components/tools/TwoStepCaptcha.vue
================================================
两步验证
================================================
FILE: src/config/defaultSettings.js
================================================
/**
* 项目默认配置项
* primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage
* navTheme - sidebar theme ['dark', 'light'] 两种主题
* colorWeak - 色盲模式
* layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局
* fixedHeader - 固定 Header : boolean
* fixSiderbar - 固定左侧菜单栏 : boolean
* contentWidth - 内容区布局: 流式 | 固定
*
* storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage)
*
*/
export default {
navTheme: 'dark', // theme for nav menu
primaryColor: '#1890ff', // '#F5222D', // primary color of ant design
layout: 'sidemenu', // nav menu position: `sidemenu` or `topmenu`
contentWidth: 'Fluid', // layout of content: `Fluid` or `Fixed`, only works when layout is topmenu
fixedHeader: false, // sticky header
fixSiderbar: false, // sticky siderbar
colorWeak: false,
menu: {
locale: true
},
title: 'Ant Design Pro',
pwa: false,
iconfontUrl: '',
production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true'
}
================================================
FILE: src/config/router.config.js
================================================
// eslint-disable-next-line
import { UserLayout, BasicLayout, BlankLayout } from '@/layouts'
import { bxAnaalyse } from '@/core/icons'
const RouteView = {
name: 'RouteView',
render: h => h('router-view')
}
export const asyncRouterMap = [
{
path: '/',
name: 'index',
component: BasicLayout,
meta: { title: 'menu.home' },
redirect: '/dashboard/workplace',
children: [
// dashboard
{
path: '/dashboard',
name: 'dashboard',
redirect: '/dashboard/workplace',
component: RouteView,
meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: ['dashboard'] },
children: [
{
path: '/dashboard/analysis/:pageNo([1-9]\\d*)?',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: { title: 'menu.dashboard.analysis', keepAlive: false, permission: ['dashboard'] }
},
// 外部链接
// {
// path: 'https://www.baidu.com/',
// name: 'Monitor',
// meta: { title: 'menu.dashboard.monitor', target: '_blank' }
// },
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: { title: 'menu.dashboard.workplace', keepAlive: true, permission: ['dashboard'] }
}
]
},
// forms
{
path: '/form',
redirect: '/form/base-form',
component: RouteView,
meta: { title: 'menu.form', icon: 'form', permission: ['form'] },
children: [
{
path: '/form/base-form',
name: 'BaseForm',
component: () => import('@/views/form/basicForm'),
meta: { title: 'menu.form.basic-form', keepAlive: true, permission: ['form'] }
},
{
path: '/form/step-form',
name: 'StepForm',
component: () => import('@/views/form/stepForm/StepForm'),
meta: { title: 'menu.form.step-form', keepAlive: true, permission: ['form'] }
},
{
path: '/form/advanced-form',
name: 'AdvanceForm',
component: () => import('@/views/form/advancedForm/AdvancedForm'),
meta: { title: 'menu.form.advanced-form', keepAlive: true, permission: ['form'] }
}
]
},
// list
{
path: '/list',
name: 'list',
component: RouteView,
redirect: '/list/table-list',
meta: { title: 'menu.list', icon: 'table', permission: ['table'] },
children: [
{
path: '/list/table-list/:pageNo([1-9]\\d*)?',
name: 'TableListWrapper',
hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
component: () => import('@/views/list/TableList'),
meta: { title: 'menu.list.table-list', keepAlive: true, permission: ['table'] }
},
{
path: '/list/basic-list',
name: 'BasicList',
component: () => import('@/views/list/BasicList'),
meta: { title: 'menu.list.basic-list', keepAlive: true, permission: ['table'] }
},
{
path: '/list/card',
name: 'CardList',
component: () => import('@/views/list/CardList'),
meta: { title: 'menu.list.card-list', keepAlive: true, permission: ['table'] }
},
{
path: '/list/search',
name: 'SearchList',
component: () => import('@/views/list/search/SearchLayout'),
redirect: '/list/search/article',
meta: { title: 'menu.list.search-list', keepAlive: true, permission: ['table'] },
children: [
{
path: '/list/search/article',
name: 'SearchArticles',
component: () => import('../views/list/search/Article'),
meta: { title: 'menu.list.search-list.articles', permission: ['table'] }
},
{
path: '/list/search/project',
name: 'SearchProjects',
component: () => import('../views/list/search/Projects'),
meta: { title: 'menu.list.search-list.projects', permission: ['table'] }
},
{
path: '/list/search/application',
name: 'SearchApplications',
component: () => import('../views/list/search/Applications'),
meta: { title: 'menu.list.search-list.applications', permission: ['table'] }
}
]
}
]
},
// profile
{
path: '/profile',
name: 'profile',
component: RouteView,
redirect: '/profile/basic',
meta: { title: 'menu.profile', icon: 'profile', permission: ['profile'] },
children: [
{
path: '/profile/basic',
name: 'ProfileBasic',
component: () => import('@/views/profile/basic'),
meta: { title: 'menu.profile.basic', permission: ['profile'] }
},
{
path: '/profile/advanced',
name: 'ProfileAdvanced',
component: () => import('@/views/profile/advanced/Advanced'),
meta: { title: 'menu.profile.advanced', permission: ['profile'] }
}
]
},
// result
{
path: '/result',
name: 'result',
component: RouteView,
redirect: '/result/success',
meta: { title: 'menu.result', icon: 'check-circle-o', permission: ['result'] },
children: [
{
path: '/result/success',
name: 'ResultSuccess',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
meta: { title: 'menu.result.success', keepAlive: false, hiddenHeaderContent: true, permission: ['result'] }
},
{
path: '/result/fail',
name: 'ResultFail',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
meta: { title: 'menu.result.fail', keepAlive: false, hiddenHeaderContent: true, permission: ['result'] }
}
]
},
// Exception
{
path: '/exception',
name: 'exception',
component: RouteView,
redirect: '/exception/403',
meta: { title: 'menu.exception', icon: 'warning', permission: ['exception'] },
children: [
{
path: '/exception/403',
name: 'Exception403',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
meta: { title: 'menu.exception.not-permission', permission: ['exception'] }
},
{
path: '/exception/404',
name: 'Exception404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
meta: { title: 'menu.exception.not-find', permission: ['exception'] }
},
{
path: '/exception/500',
name: 'Exception500',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
meta: { title: 'menu.exception.server-error', permission: ['exception'] }
}
]
},
// account
{
path: '/account',
component: RouteView,
redirect: '/account/center',
name: 'account',
meta: { title: 'menu.account', icon: 'user', keepAlive: true, permission: ['user'] },
children: [
{
path: '/account/center',
name: 'center',
component: () => import('@/views/account/center'),
meta: { title: 'menu.account.center', keepAlive: true, permission: ['user'] }
},
{
path: '/account/settings',
name: 'settings',
component: () => import('@/views/account/settings/Index'),
meta: { title: 'menu.account.settings', hideHeader: true, permission: ['user'] },
redirect: '/account/settings/basic',
hideChildrenInMenu: true,
children: [
{
path: '/account/settings/basic',
name: 'BasicSettings',
component: () => import('@/views/account/settings/BasicSetting'),
meta: { title: 'account.settings.menuMap.basic', hidden: true, permission: ['user'] }
},
{
path: '/account/settings/security',
name: 'SecuritySettings',
component: () => import('@/views/account/settings/Security'),
meta: {
title: 'account.settings.menuMap.security',
hidden: true,
keepAlive: true,
permission: ['user']
}
},
{
path: '/account/settings/custom',
name: 'CustomSettings',
component: () => import('@/views/account/settings/Custom'),
meta: { title: 'account.settings.menuMap.custom', hidden: true, keepAlive: true, permission: ['user'] }
},
{
path: '/account/settings/binding',
name: 'BindingSettings',
component: () => import('@/views/account/settings/Binding'),
meta: { title: 'account.settings.menuMap.binding', hidden: true, keepAlive: true, permission: ['user'] }
},
{
path: '/account/settings/notification',
name: 'NotificationSettings',
component: () => import('@/views/account/settings/Notification'),
meta: {
title: 'account.settings.menuMap.notification',
hidden: true,
keepAlive: true,
permission: ['user']
}
}
]
}
]
}
// other
/*
{
path: '/other',
name: 'otherPage',
component: PageView,
meta: { title: '其他组件', icon: 'slack', permission: [ 'dashboard' ] },
redirect: '/other/icon-selector',
children: [
{
path: '/other/icon-selector',
name: 'TestIconSelect',
component: () => import('@/views/other/IconSelectorView'),
meta: { title: 'IconSelector', icon: 'tool', keepAlive: true, permission: [ 'dashboard' ] }
},
{
path: '/other/list',
component: RouteView,
meta: { title: '业务布局', icon: 'layout', permission: [ 'support' ] },
redirect: '/other/list/tree-list',
children: [
{
path: '/other/list/tree-list',
name: 'TreeList',
component: () => import('@/views/other/TreeList'),
meta: { title: '树目录表格', keepAlive: true }
},
{
path: '/other/list/edit-table',
name: 'EditList',
component: () => import('@/views/other/TableInnerEditList'),
meta: { title: '内联编辑表格', keepAlive: true }
},
{
path: '/other/list/user-list',
name: 'UserList',
component: () => import('@/views/other/UserList'),
meta: { title: '用户列表', keepAlive: true }
},
{
path: '/other/list/role-list',
name: 'RoleList',
component: () => import('@/views/other/RoleList'),
meta: { title: '角色列表', keepAlive: true }
},
{
path: '/other/list/system-role',
name: 'SystemRole',
component: () => import('@/views/role/RoleList'),
meta: { title: '角色列表2', keepAlive: true }
},
{
path: '/other/list/permission-list',
name: 'PermissionList',
component: () => import('@/views/other/PermissionList'),
meta: { title: '权限列表', keepAlive: true }
}
]
}
]
}
*/
]
},
{
path: '*',
redirect: '/404',
hidden: true
}
]
/**
* 基础路由
* @type { *[] }
*/
export const constantRouterMap = [
{
path: '/user',
component: UserLayout,
redirect: '/user/login',
hidden: true,
children: [
{
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login')
},
{
path: 'register',
name: 'register',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Register')
},
{
path: 'register-result',
name: 'registerResult',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/RegisterResult')
},
{
path: 'recover',
name: 'recover',
component: undefined
}
]
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404')
}
]
================================================
FILE: src/core/bootstrap.js
================================================
import store from '@/store'
import storage from 'store'
import {
ACCESS_TOKEN,
APP_LANGUAGE,
TOGGLE_CONTENT_WIDTH,
TOGGLE_FIXED_HEADER,
TOGGLE_FIXED_SIDEBAR, TOGGLE_HIDE_HEADER,
TOGGLE_LAYOUT, TOGGLE_NAV_THEME, TOGGLE_WEAK,
TOGGLE_COLOR, TOGGLE_MULTI_TAB
} from '@/store/mutation-types'
import { printANSI } from '@/utils/screenLog'
import defaultSettings from '@/config/defaultSettings'
export default function Initializer () {
printANSI() // 请自行移除该行. please remove this line
store.commit(TOGGLE_LAYOUT, storage.get(TOGGLE_LAYOUT, defaultSettings.layout))
store.commit(TOGGLE_FIXED_HEADER, storage.get(TOGGLE_FIXED_HEADER, defaultSettings.fixedHeader))
store.commit(TOGGLE_FIXED_SIDEBAR, storage.get(TOGGLE_FIXED_SIDEBAR, defaultSettings.fixSiderbar))
store.commit(TOGGLE_CONTENT_WIDTH, storage.get(TOGGLE_CONTENT_WIDTH, defaultSettings.contentWidth))
store.commit(TOGGLE_HIDE_HEADER, storage.get(TOGGLE_HIDE_HEADER, defaultSettings.autoHideHeader))
store.commit(TOGGLE_NAV_THEME, storage.get(TOGGLE_NAV_THEME, defaultSettings.navTheme))
store.commit(TOGGLE_WEAK, storage.get(TOGGLE_WEAK, defaultSettings.colorWeak))
store.commit(TOGGLE_COLOR, storage.get(TOGGLE_COLOR, defaultSettings.primaryColor))
store.commit(TOGGLE_MULTI_TAB, storage.get(TOGGLE_MULTI_TAB, defaultSettings.multiTab))
store.commit('SET_TOKEN', storage.get(ACCESS_TOKEN))
store.dispatch('setLang', storage.get(APP_LANGUAGE, 'en-US'))
// last step
}
================================================
FILE: src/core/directives/action.js
================================================
import Vue from 'vue'
import store from '@/store'
/**
* Action 权限指令
* 指令用法:
* - 在需要控制 action 级别权限的组件上使用 v-action:[method] , 如下:
* 添加用户
* 删除用户
* 修改
*
* - 当前用户没有权限时,组件上使用了该指令则会被隐藏
* - 当后台权限跟 pro 提供的模式不同时,只需要针对这里的权限过滤进行修改即可
*
* @see https://github.com/vueComponent/ant-design-vue-pro/pull/53
*/
const action = Vue.directive('action', {
inserted: function (el, binding, vnode) {
const actionName = binding.arg
const roles = store.getters.roles
const elVal = vnode.context.$route.meta.permission
const permissionId = Object.prototype.toString.call(elVal) === '[object String]' && [elVal] || elVal
roles.permissions.forEach(p => {
if (!permissionId.includes(p.permissionId)) {
return
}
if (p.actionList && !p.actionList.includes(actionName)) {
el.parentNode && el.parentNode.removeChild(el) || (el.style.display = 'none')
}
})
}
})
export default action
================================================
FILE: src/core/icons.js
================================================
/**
* Custom icon list
* All icons are loaded here for easy management
* @see https://vue.ant.design/components/icon/#Custom-Font-Icon
*
* 自定义图标加载表
* 所有图标均从这里加载,方便管理
*/
import bxAnaalyse from '@/assets/icons/bx-analyse.svg?inline' // path to your '*.svg?inline' file.
export { bxAnaalyse }
================================================
FILE: src/core/lazy_use.js
================================================
import Vue from 'vue'
// base library
import {
ConfigProvider,
Layout,
Input,
InputNumber,
Button,
Switch,
Radio,
Checkbox,
Select,
Card,
Form,
Row,
Col,
Modal,
Table,
Tabs,
Icon,
Badge,
Popover,
Dropdown,
List,
Avatar,
Breadcrumb,
Steps,
Spin,
Menu,
Drawer,
Tooltip,
Alert,
Tag,
Divider,
DatePicker,
TimePicker,
Upload,
Progress,
Skeleton,
Popconfirm,
PageHeader,
Result,
Statistic,
Descriptions,
Space,
message,
notification
} from 'ant-design-vue'
import Viser from 'viser-vue'
// ext library
import VueCropper from 'vue-cropper'
import Dialog from '@/components/Dialog'
import MultiTab from '@/components/MultiTab'
import PageLoading from '@/components/PageLoading'
import PermissionHelper from '@/core/permission/permission'
import './directives/action'
Vue.use(ConfigProvider)
Vue.use(Layout)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Button)
Vue.use(Switch)
Vue.use(Radio)
Vue.use(Checkbox)
Vue.use(Select)
Vue.use(Card)
Vue.use(Form)
Vue.use(Row)
Vue.use(Col)
Vue.use(Modal)
Vue.use(Table)
Vue.use(Tabs)
Vue.use(Icon)
Vue.use(Badge)
Vue.use(Popover)
Vue.use(Dropdown)
Vue.use(List)
Vue.use(Avatar)
Vue.use(Breadcrumb)
Vue.use(Steps)
Vue.use(Spin)
Vue.use(Menu)
Vue.use(Drawer)
Vue.use(Tooltip)
Vue.use(Alert)
Vue.use(Tag)
Vue.use(Divider)
Vue.use(DatePicker)
Vue.use(TimePicker)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Skeleton)
Vue.use(Popconfirm)
Vue.use(PageHeader)
Vue.use(Result)
Vue.use(Statistic)
Vue.use(Descriptions)
Vue.use(Space)
Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message
Vue.prototype.$notification = notification
Vue.prototype.$info = Modal.info
Vue.prototype.$success = Modal.success
Vue.prototype.$error = Modal.error
Vue.prototype.$warning = Modal.warning
Vue.use(Viser)
Vue.use(Dialog) // this.$dialog func
Vue.use(MultiTab)
Vue.use(PageLoading)
Vue.use(PermissionHelper)
Vue.use(VueCropper)
process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] NOTICE: Antd use lazy-load.')
================================================
FILE: src/core/permission/permission.js
================================================
export const PERMISSION_ENUM = {
'add': { key: 'add', label: '新增' },
'delete': { key: 'delete', label: '删除' },
'edit': { key: 'edit', label: '修改' },
'query': { key: 'query', label: '查询' },
'get': { key: 'get', label: '详情' },
'enable': { key: 'enable', label: '启用' },
'disable': { key: 'disable', label: '禁用' },
'import': { key: 'import', label: '导入' },
'export': { key: 'export', label: '导出' }
}
/**
* Button
* @param Vue
*/
function plugin (Vue) {
if (plugin.installed) {
return
}
!Vue.prototype.$auth && Object.defineProperties(Vue.prototype, {
$auth: {
get () {
const _this = this
return (permissions) => {
const [permission, action] = permissions.split('.')
const permissionList = _this.$store.getters.roles.permissions
return permissionList.find((val) => {
return val.permissionId === permission
}).actionList.findIndex((val) => {
return val === action
}) > -1
}
}
}
})
!Vue.prototype.$enum && Object.defineProperties(Vue.prototype, {
$enum: {
get () {
// const _this = this;
return (val) => {
let result = PERMISSION_ENUM
val && val.split('.').forEach(v => {
result = result && result[v] || null
})
return result
}
}
}
})
}
export default plugin
================================================
FILE: src/core/use.js
================================================
import Vue from 'vue'
// base library
import Antd from 'ant-design-vue'
import Viser from 'viser-vue'
import VueCropper from 'vue-cropper'
import 'ant-design-vue/dist/antd.less'
// ext library
import VueClipboard from 'vue-clipboard2'
import MultiTab from '@/components/MultiTab'
import PageLoading from '@/components/PageLoading'
import PermissionHelper from '@/core/permission/permission'
// import '@/components/use'
import './directives/action'
VueClipboard.config.autoSetContainer = true
Vue.use(Antd)
Vue.use(Viser)
Vue.use(MultiTab)
Vue.use(PageLoading)
Vue.use(VueClipboard)
Vue.use(PermissionHelper)
Vue.use(VueCropper)
process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] WARNING: Antd now use fulled imported.')
================================================
FILE: src/global.less
================================================
@import '~ant-design-vue/es/style/themes/default.less';
html,
body,
#app,
#root {
height: 100%;
}
.colorWeak {
filter: invert(80%);
}
.ant-layout.layout-basic {
height: 100vh;
min-height: 100vh;
}
canvas {
display: block;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizelegibility;
}
ul,
ol {
list-style: none;
}
// 数据列表 样式
.table-alert {
margin-bottom: 16px;
}
// 数据列表 操作
.table-operator {
margin-bottom: 18px;
button {
margin-right: 8px;
}
}
// 数据列表 搜索条件
.table-page-search-wrapper {
.ant-form-inline {
.ant-form-item {
display: flex;
margin-right: 0;
margin-bottom: 24px;
.ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
vertical-align: middle;
}
> .ant-form-item-label {
width: auto;
padding-right: 8px;
line-height: 32px;
}
.ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
}
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
@media (max-width: @screen-xs) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}
================================================
FILE: src/layouts/BasicLayout.less
================================================
@import '~ant-design-vue/es/style/themes/default.less';
.ant-pro-global-header-index-right {
margin-right: 8px;
&.ant-pro-global-header-index-dark {
.ant-pro-global-header-index-action {
color: hsl(0deg 0% 100% / 85%);
&:hover {
background: #1890ff;
}
}
}
.ant-pro-account-avatar {
.antd-pro-global-header-index-avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgb(255 255 255 / 85%);
}
}
.menu {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 100px;
}
}
}
================================================
FILE: src/layouts/BasicLayout.vue
================================================
{{ title }}
{ $message.info('只是一个DEMO') }" />
This is SettingDrawer custom footer content.
================================================
FILE: src/layouts/BlankLayout.vue
================================================
================================================
FILE: src/layouts/PageView.vue
================================================
================================================
FILE: src/layouts/RouteView.vue
================================================
================================================
FILE: src/layouts/UserLayout.vue
================================================
{{ $t('layouts.userLayout.title') }}
================================================
FILE: src/layouts/index.js
================================================
import UserLayout from './UserLayout'
import BlankLayout from './BlankLayout'
import BasicLayout from './BasicLayout'
import RouteView from './RouteView'
import PageView from './PageView'
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }
================================================
FILE: src/locales/index.js
================================================
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import storage from 'store'
import moment from 'moment'
// default lang
import enUS from './lang/en-US'
Vue.use(VueI18n)
export const defaultLang = 'en-US'
const messages = {
'en-US': {
...enUS
}
}
const i18n = new VueI18n({
silentTranslationWarn: true,
locale: defaultLang,
fallbackLocale: defaultLang,
messages
})
const loadedLanguages = [defaultLang]
function setI18nLanguage (lang) {
i18n.locale = lang
// request.headers['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang = defaultLang) {
return new Promise(resolve => {
// 缓存语言设置
storage.set('lang', lang)
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(/* webpackChunkName: "lang-[request]" */ `./lang/${lang}`).then(msg => {
const locale = msg.default
i18n.setLocaleMessage(lang, locale)
loadedLanguages.push(lang)
moment.updateLocale(locale.momentName, locale.momentLocale)
return setI18nLanguage(lang)
})
}
return resolve(setI18nLanguage(lang))
}
return resolve(lang)
})
}
export function i18nRender (key) {
return i18n.t(`${key}`)
}
export default i18n
================================================
FILE: src/locales/lang/en-US/account/settings.js
================================================
export default {
'account.settings.menuMap.basic': 'Basic Settings',
'account.settings.menuMap.security': 'Security Settings',
'account.settings.menuMap.custom': 'Custom Settings',
'account.settings.menuMap.binding': 'Account Binding',
'account.settings.menuMap.notification': 'New Message Notification',
'account.settings.basic.avatar': 'Avatar',
'account.settings.basic.change-avatar': 'Change avatar',
'account.settings.basic.email': 'Email',
'account.settings.basic.email-message': 'Please input your email!',
'account.settings.basic.nickname': 'Nickname',
'account.settings.basic.nickname-message': 'Please input your Nickname!',
'account.settings.basic.profile': 'Personal profile',
'account.settings.basic.profile-message': 'Please input your personal profile!',
'account.settings.basic.profile-placeholder': 'Brief introduction to yourself',
'account.settings.basic.country': 'Country/Region',
'account.settings.basic.country-message': 'Please input your country!',
'account.settings.basic.geographic': 'Province or city',
'account.settings.basic.geographic-message': 'Please input your geographic info!',
'account.settings.basic.address': 'Street Address',
'account.settings.basic.address-message': 'Please input your address!',
'account.settings.basic.phone': 'Phone Number',
'account.settings.basic.phone-message': 'Please input your phone!',
'account.settings.basic.update': 'Update Information',
'account.settings.basic.update.success': 'Update basic information successfully',
'account.settings.security.strong': 'Strong',
'account.settings.security.medium': 'Medium',
'account.settings.security.weak': 'Weak',
'account.settings.security.password': 'Account Password',
'account.settings.security.password-description': 'Current password strength:',
'account.settings.security.phone': 'Security Phone',
'account.settings.security.phone-description': 'Bound phone:',
'account.settings.security.question': 'Security Question',
'account.settings.security.question-description':
'The security question is not set, and the security policy can effectively protect the account security',
'account.settings.security.email': 'Backup Email',
'account.settings.security.email-description': 'Bound Email:',
'account.settings.security.mfa': 'MFA Device',
'account.settings.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
'account.settings.security.modify': 'Modify',
'account.settings.security.set': 'Set',
'account.settings.security.bind': 'Bind',
'account.settings.binding.taobao': 'Binding Taobao',
'account.settings.binding.taobao-description': 'Currently unbound Taobao account',
'account.settings.binding.alipay': 'Binding Alipay',
'account.settings.binding.alipay-description': 'Currently unbound Alipay account',
'account.settings.binding.dingding': 'Binding DingTalk',
'account.settings.binding.dingding-description': 'Currently unbound DingTalk account',
'account.settings.binding.bind': 'Bind',
'account.settings.notification.password': 'Account Password',
'account.settings.notification.password-description':
'Messages from other users will be notified in the form of a station letter',
'account.settings.notification.messages': 'System Messages',
'account.settings.notification.messages-description':
'System messages will be notified in the form of a station letter',
'account.settings.notification.todo': 'To-do Notification',
'account.settings.notification.todo-description':
'The to-do list will be notified in the form of a letter from the station',
'account.settings.settings.open': 'Open',
'account.settings.settings.close': 'Close'
}
================================================
FILE: src/locales/lang/en-US/account.js
================================================
import settings from './account/settings'
export default {
...settings
}
================================================
FILE: src/locales/lang/en-US/dashboard/analysis.js
================================================
export default {
'dashboard.analysis.test': 'Gongzhuan No.{no} shop',
'dashboard.analysis.introduce': 'Introduce',
'dashboard.analysis.total-sales': 'Total Sales',
'dashboard.analysis.day-sales': 'Daily Sales',
'dashboard.analysis.visits': 'Visits',
'dashboard.analysis.visits-trend': 'Visits Trend',
'dashboard.analysis.visits-ranking': 'Visits Ranking',
'dashboard.analysis.day-visits': 'Daily Visits',
'dashboard.analysis.week': 'WoW Change',
'dashboard.analysis.day': 'DoD Change',
'dashboard.analysis.payments': 'Payments',
'dashboard.analysis.conversion-rate': 'Conversion Rate',
'dashboard.analysis.operational-effect': 'Operational Effect',
'dashboard.analysis.sales-trend': 'Stores Sales Trend',
'dashboard.analysis.sales-ranking': 'Sales Ranking',
'dashboard.analysis.all-year': 'All Year',
'dashboard.analysis.all-month': 'All Month',
'dashboard.analysis.all-week': 'All Week',
'dashboard.analysis.all-day': 'All day',
'dashboard.analysis.search-users': 'Search Users',
'dashboard.analysis.per-capita-search': 'Per Capita Search',
'dashboard.analysis.online-top-search': 'Online Top Search',
'dashboard.analysis.the-proportion-of-sales': 'The Proportion Of Sales',
'dashboard.analysis.dropdown-option-one': 'Operation one',
'dashboard.analysis.dropdown-option-two': 'Operation two',
'dashboard.analysis.channel.all': 'ALL',
'dashboard.analysis.channel.online': 'Online',
'dashboard.analysis.channel.stores': 'Stores',
'dashboard.analysis.sales': 'Sales',
'dashboard.analysis.traffic': 'Traffic',
'dashboard.analysis.table.rank': 'Rank',
'dashboard.analysis.table.search-keyword': 'Keyword',
'dashboard.analysis.table.users': 'Users',
'dashboard.analysis.table.weekly-range': 'Weekly Range'
}
================================================
FILE: src/locales/lang/en-US/dashboard.js
================================================
import analysis from './dashboard/analysis'
export default {
...analysis
}
================================================
FILE: src/locales/lang/en-US/form/basicForm.js
================================================
export default {
'form.basic-form.basic.title': 'Basic form',
'form.basic-form.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'form.basic-form.title.label': 'Title',
'form.basic-form.title.placeholder': 'Give the target a name',
'form.basic-form.title.required': 'Please enter a title',
'form.basic-form.date.label': 'Start and end date',
'form.basic-form.placeholder.start': 'Start date',
'form.basic-form.placeholder.end': 'End date',
'form.basic-form.date.required': 'Please select the start and end date',
'form.basic-form.goal.label': 'Goal description',
'form.basic-form.goal.placeholder': 'Please enter your work goals',
'form.basic-form.goal.required': 'Please enter a description of the goal',
'form.basic-form.standard.label': 'Metrics',
'form.basic-form.standard.placeholder': 'Please enter a metric',
'form.basic-form.standard.required': 'Please enter a metric',
'form.basic-form.client.label': 'Client',
'form.basic-form.label.tooltip': 'Target service object',
'form.basic-form.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'form.basic-form.client.required': 'Please describe the customers you serve',
'form.basic-form.invites.label': 'Inviting critics',
'form.basic-form.invites.placeholder':
'Please direct @ Name / job number, you can invite up to 5 people',
'form.basic-form.weight.label': 'Weight',
'form.basic-form.weight.placeholder': 'Please enter weight',
'form.basic-form.public.label': 'Target disclosure',
'form.basic-form.label.help': 'Customers and invitees are shared by default',
'form.basic-form.radio.public': 'Public',
'form.basic-form.radio.partially-public': 'Partially public',
'form.basic-form.radio.private': 'Private',
'form.basic-form.publicUsers.placeholder': 'Open to',
'form.basic-form.option.A': 'Colleague A',
'form.basic-form.option.B': 'Colleague B',
'form.basic-form.option.C': 'Colleague C',
'form.basic-form.email.required': 'Please enter your email!',
'form.basic-form.email.wrong-format': 'The email address is in the wrong format!',
'form.basic-form.userName.required': 'Please enter your userName!',
'form.basic-form.password.required': 'Please enter your password!',
'form.basic-form.password.twice': 'The passwords entered twice do not match!',
'form.basic-form.strength.msg':
"Please enter at least 6 characters and don't use passwords that are easy to guess.",
'form.basic-form.strength.strong': 'Strength: strong',
'form.basic-form.strength.medium': 'Strength: medium',
'form.basic-form.strength.short': 'Strength: too short',
'form.basic-form.confirm-password.required': 'Please confirm your password!',
'form.basic-form.phone-number.required': 'Please enter your phone number!',
'form.basic-form.phone-number.wrong-format': 'Malformed phone number!',
'form.basic-form.verification-code.required': 'Please enter the verification code!',
'form.basic-form.form.get-captcha': 'Get Captcha',
'form.basic-form.captcha.second': 'sec',
'form.basic-form.form.optional': ' (optional) ',
'form.basic-form.form.submit': 'Submit',
'form.basic-form.form.save': 'Save',
'form.basic-form.email.placeholder': 'Email',
'form.basic-form.password.placeholder': 'Password',
'form.basic-form.confirm-password.placeholder': 'Confirm password',
'form.basic-form.phone-number.placeholder': 'Phone number',
'form.basic-form.verification-code.placeholder': 'Verification code'
}
================================================
FILE: src/locales/lang/en-US/form.js
================================================
import basicForm from './form/basicForm'
export default {
...basicForm
}
================================================
FILE: src/locales/lang/en-US/global.js
================================================
export default {
submit: 'Submit',
save: 'Save',
'submit.ok': 'Submit successfully',
'save.ok': 'Saved successfully'
}
================================================
FILE: src/locales/lang/en-US/menu.js
================================================
export default {
'menu.welcome': 'Welcome',
'menu.home': 'Home',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Analysis',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Workplace',
'menu.form': 'Form',
'menu.form.basic-form': 'Basic Form',
'menu.form.step-form': 'Step Form',
'menu.form.step-form.info': 'Step Form(write transfer information)',
'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
'menu.form.step-form.result': 'Step Form(finished)',
'menu.form.advanced-form': 'Advanced Form',
'menu.list': 'List',
'menu.list.table-list': 'Search Table',
'menu.list.basic-list': 'Basic List',
'menu.list.card-list': 'Card List',
'menu.list.search-list': 'Search List',
'menu.list.search-list.articles': 'Search List(articles)',
'menu.list.search-list.projects': 'Search List(projects)',
'menu.list.search-list.applications': 'Search List(applications)',
'menu.profile': 'Profile',
'menu.profile.basic': 'Basic Profile',
'menu.profile.advanced': 'Advanced Profile',
'menu.result': 'Result',
'menu.result.success': 'Success',
'menu.result.fail': 'Fail',
'menu.exception': 'Exception',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Trigger',
'menu.account': 'Account',
'menu.account.center': 'Account Center',
'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout'
}
================================================
FILE: src/locales/lang/en-US/result/fail.js
================================================
export default {
'result.fail.error.title': 'Submission Failed',
'result.fail.error.description':
'Please check and modify the following information before resubmitting.',
'result.fail.error.hint-title': 'The content you submitted has the following error:',
'result.fail.error.hint-text1': 'Your account has been frozen',
'result.fail.error.hint-btn1': 'Thaw immediately',
'result.fail.error.hint-text2': 'Your account is not yet eligible to apply',
'result.fail.error.hint-btn2': 'Upgrade immediately',
'result.fail.error.btn-text': 'Return to modify'
}
================================================
FILE: src/locales/lang/en-US/result/success.js
================================================
export default {
'result.success.title': 'Submission Success',
'result.success.description':
'The submission results page is used to feed back the results of a series of operational tasks. If it is a simple operation, use the Message global prompt feedback. This text area can show a simple supplementary explanation. If there is a similar requirement for displaying “documents”, the following gray area can present more complicated content.',
'result.success.operate-title': 'Project Name',
'result.success.operate-id': 'Project ID',
'result.success.principal': 'Principal',
'result.success.operate-time': 'Effective time',
'result.success.step1-title': 'Create project',
'result.success.step1-operator': 'Qu Lili',
'result.success.step2-title': 'Departmental preliminary review',
'result.success.step2-operator': 'Zhou Maomao',
'result.success.step2-extra': 'Urge',
'result.success.step3-title': 'Financial review',
'result.success.step4-title': 'Finish',
'result.success.btn-return': 'Back List',
'result.success.btn-project': 'View Project',
'result.success.btn-print': 'Print'
}
================================================
FILE: src/locales/lang/en-US/result.js
================================================
import success from './result/success'
import fail from './result/fail'
export default {
...success,
...fail
}
================================================
FILE: src/locales/lang/en-US/setting.js
================================================
export default {
'app.setting.pagestyle': 'Page style setting',
'app.setting.pagestyle.light': 'Light style',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.realdark': 'RealDark style',
'app.setting.themecolor': 'Theme Color',
'app.setting.navigationmode': 'Navigation Mode',
'app.setting.content-width': 'Content Width',
'app.setting.fixedheader': 'Fixed Header',
'app.setting.fixedsidebar': 'Fixed Sidebar',
'app.setting.sidemenu': 'Side Menu Layout',
'app.setting.topmenu': 'Top Menu Layout',
'app.setting.content-width.fixed': 'Fixed',
'app.setting.content-width.fluid': 'Fluid',
'app.setting.othersettings': 'Other Settings',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copy Setting',
'app.setting.loading': 'Loading theme',
'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/config/defaultSettings.js',
'app.setting.production.hint': 'Setting panel shows in development environment only, please manually modify',
'app.setting.themecolor.daybreak': 'Daybreak Blue',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.geekblue': 'Geek Blue',
'app.setting.themecolor.purple': 'Golden Purple'
}
================================================
FILE: src/locales/lang/en-US/user.js
================================================
export default {
'user.login.userName': 'userName',
'user.login.password': 'password',
'user.login.username.placeholder': 'Account: admin',
'user.login.password.placeholder': 'password: admin or ant.design',
'user.login.message-invalid-credentials':
'Invalid username or password(admin/ant.design)',
'user.login.message-invalid-verification-code': 'Invalid verification code',
'user.login.tab-login-credentials': 'Credentials',
'user.login.tab-login-mobile': 'Mobile number',
'user.login.mobile.placeholder': 'Mobile number',
'user.login.mobile.verification-code.placeholder': 'Verification code',
'user.login.remember-me': 'Remember me',
'user.login.forgot-password': 'Forgot your password?',
'user.login.sign-in-with': 'Sign in with',
'user.login.signup': 'Sign up',
'user.login.login': 'Login',
'user.register.register': 'Register',
'user.register.email.placeholder': 'Email',
'user.register.password.placeholder': 'Password ',
'user.register.password.popover-message': 'Please enter at least 6 characters. Please do not use passwords that are easy to guess. ',
'user.register.confirm-password.placeholder': 'Confirm password',
'user.register.get-verification-code': 'Get code',
'user.register.sign-in': 'Already have an account?',
'user.register-result.msg': 'Account:registered at {email}',
'user.register-result.activation-email':
'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
'user.register-result.back-home': 'Back to home',
'user.register-result.view-mailbox': 'View mailbox',
'user.email.required': 'Please enter your email!',
'user.email.wrong-format': 'The email address is in the wrong format!',
'user.userName.required': 'Please enter account name or email address',
'user.password.required': 'Please enter your password!',
'user.password.twice.msg': 'The passwords entered twice do not match!',
'user.password.strength.msg':
'The password is not strong enough',
'user.password.strength.strong': 'Strength: strong',
'user.password.strength.medium': 'Strength: medium',
'user.password.strength.low': 'Strength: low',
'user.password.strength.short': 'Strength: too short',
'user.confirm-password.required': 'Please confirm your password!',
'user.phone-number.required': 'Please enter your phone number!',
'user.phone-number.wrong-format': 'Please enter a valid phone number',
'user.verification-code.required': 'Please enter the verification code!'
}
================================================
FILE: src/locales/lang/en-US.js
================================================
import antdEnUS from 'ant-design-vue/es/locale-provider/en_US'
import momentEU from 'moment/locale/eu'
import global from './en-US/global'
import menu from './en-US/menu'
import setting from './en-US/setting'
import user from './en-US/user'
import dashboard from './en-US/dashboard'
import form from './en-US/form'
import result from './en-US/result'
import account from './en-US/account'
const components = {
antLocale: antdEnUS,
momentName: 'eu',
momentLocale: momentEU
}
export default {
message: '-',
'layouts.usermenu.dialog.title': 'Message',
'layouts.usermenu.dialog.content': 'Are you sure you would like to logout?',
'layouts.userLayout.title': 'Ant Design is the most influential web design specification in Xihu district',
...components,
...global,
...menu,
...setting,
...user,
...dashboard,
...form,
...result,
...account
}
================================================
FILE: src/locales/lang/zh-CN/account/settings.js
================================================
export default {
'account.settings.menuMap.basic': '基本设置',
'account.settings.menuMap.security': '安全设置',
'account.settings.menuMap.custom': '个性化',
'account.settings.menuMap.binding': '账号绑定',
'account.settings.menuMap.notification': '新消息通知',
'account.settings.basic.avatar': '头像',
'account.settings.basic.change-avatar': '更换头像',
'account.settings.basic.email': '邮箱',
'account.settings.basic.email-message': '请输入您的邮箱!',
'account.settings.basic.nickname': '昵称',
'account.settings.basic.nickname-message': '请输入您的昵称!',
'account.settings.basic.profile': '个人简介',
'account.settings.basic.profile-message': '请输入个人简介!',
'account.settings.basic.profile-placeholder': '个人简介',
'account.settings.basic.country': '国家/地区',
'account.settings.basic.country-message': '请输入您的国家或地区!',
'account.settings.basic.geographic': '所在省市',
'account.settings.basic.geographic-message': '请输入您的所在省市!',
'account.settings.basic.address': '街道地址',
'account.settings.basic.address-message': '请输入您的街道地址!',
'account.settings.basic.phone': '联系电话',
'account.settings.basic.phone-message': '请输入您的联系电话!',
'account.settings.basic.update': '更新基本信息',
'account.settings.basic.update.success': '更新基本信息成功',
'account.settings.security.strong': '强',
'account.settings.security.medium': '中',
'account.settings.security.weak': '弱',
'account.settings.security.password': '账户密码',
'account.settings.security.password-description': '当前密码强度:',
'account.settings.security.phone': '密保手机',
'account.settings.security.phone-description': '已绑定手机:',
'account.settings.security.question': '密保问题',
'account.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',
'account.settings.security.email': '备用邮箱',
'account.settings.security.email-description': '已绑定邮箱:',
'account.settings.security.mfa': 'MFA 设备',
'account.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
'account.settings.security.modify': '修改',
'account.settings.security.set': '设置',
'account.settings.security.bind': '绑定',
'account.settings.binding.taobao': '绑定淘宝',
'account.settings.binding.taobao-description': '当前未绑定淘宝账号',
'account.settings.binding.alipay': '绑定支付宝',
'account.settings.binding.alipay-description': '当前未绑定支付宝账号',
'account.settings.binding.dingding': '绑定钉钉',
'account.settings.binding.dingding-description': '当前未绑定钉钉账号',
'account.settings.binding.bind': '绑定',
'account.settings.notification.password': '账户密码',
'account.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',
'account.settings.notification.messages': '系统消息',
'account.settings.notification.messages-description': '系统消息将以站内信的形式通知',
'account.settings.notification.todo': '待办任务',
'account.settings.notification.todo-description': '待办任务将以站内信的形式通知',
'account.settings.settings.open': '开',
'account.settings.settings.close': '关'
}
================================================
FILE: src/locales/lang/zh-CN/account.js
================================================
import settings from './account/settings'
export default {
...settings
}
================================================
FILE: src/locales/lang/zh-CN/dashboard/analysis.js
================================================
export default {
'dashboard.analysis.test': '工专路 {no} 号店',
'dashboard.analysis.introduce': '指标说明',
'dashboard.analysis.total-sales': '总销售额',
'dashboard.analysis.day-sales': '日均销售额¥',
'dashboard.analysis.visits': '访问量',
'dashboard.analysis.visits-trend': '访问量趋势',
'dashboard.analysis.visits-ranking': '门店访问量排名',
'dashboard.analysis.day-visits': '日访问量',
'dashboard.analysis.week': '周同比',
'dashboard.analysis.day': '日同比',
'dashboard.analysis.payments': '支付笔数',
'dashboard.analysis.conversion-rate': '转化率',
'dashboard.analysis.operational-effect': '运营活动效果',
'dashboard.analysis.sales-trend': '销售趋势',
'dashboard.analysis.sales-ranking': '门店销售额排名',
'dashboard.analysis.all-year': '全年',
'dashboard.analysis.all-month': '本月',
'dashboard.analysis.all-week': '本周',
'dashboard.analysis.all-day': '今日',
'dashboard.analysis.search-users': '搜索用户数',
'dashboard.analysis.per-capita-search': '人均搜索次数',
'dashboard.analysis.online-top-search': '线上热门搜索',
'dashboard.analysis.the-proportion-of-sales': '销售额类别占比',
'dashboard.analysis.dropdown-option-one': '操作一',
'dashboard.analysis.dropdown-option-two': '操作二',
'dashboard.analysis.channel.all': '全部渠道',
'dashboard.analysis.channel.online': '线上',
'dashboard.analysis.channel.stores': '门店',
'dashboard.analysis.sales': '销售额',
'dashboard.analysis.traffic': '客流量',
'dashboard.analysis.table.rank': '排名',
'dashboard.analysis.table.search-keyword': '搜索关键词',
'dashboard.analysis.table.users': '用户数',
'dashboard.analysis.table.weekly-range': '周涨幅'
}
================================================
FILE: src/locales/lang/zh-CN/dashboard.js
================================================
import analysis from './dashboard/analysis'
export default {
...analysis
}
================================================
FILE: src/locales/lang/zh-CN/form/basicForm.js
================================================
export default {
'form.basic-form.basic.title': '基础表单',
'form.basic-form.basic.description':
'表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。',
'form.basic-form.title.label': '标题',
'form.basic-form.title.placeholder': '给目标起个名字',
'form.basic-form.title.required': '请输入标题',
'form.basic-form.date.label': '起止日期',
'form.basic-form.placeholder.start': '开始日期',
'form.basic-form.placeholder.end': '结束日期',
'form.basic-form.date.required': '请选择起止日期',
'form.basic-form.goal.label': '目标描述',
'form.basic-form.goal.placeholder': '请输入你的阶段性工作目标',
'form.basic-form.goal.required': '请输入目标描述',
'form.basic-form.standard.label': '衡量标准',
'form.basic-form.standard.placeholder': '请输入衡量标准',
'form.basic-form.standard.required': '请输入衡量标准',
'form.basic-form.client.label': '客户',
'form.basic-form.client.required': '请描述你服务的客户',
'form.basic-form.label.tooltip': '目标的服务对象',
'form.basic-form.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
'form.basic-form.invites.label': '邀评人',
'form.basic-form.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
'form.basic-form.weight.label': '权重',
'form.basic-form.weight.placeholder': '请输入',
'form.basic-form.public.label': '目标公开',
'form.basic-form.label.help': '客户、邀评人默认被分享',
'form.basic-form.radio.public': '公开',
'form.basic-form.radio.partially-public': '部分公开',
'form.basic-form.radio.private': '不公开',
'form.basic-form.publicUsers.placeholder': '公开给',
'form.basic-form.option.A': '同事一',
'form.basic-form.option.B': '同事二',
'form.basic-form.option.C': '同事三',
'form.basic-form.email.required': '请输入邮箱地址!',
'form.basic-form.email.wrong-format': '邮箱地址格式错误!',
'form.basic-form.userName.required': '请输入用户名!',
'form.basic-form.password.required': '请输入密码!',
'form.basic-form.password.twice': '两次输入的密码不匹配!',
'form.basic-form.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'form.basic-form.strength.strong': '强度:强',
'form.basic-form.strength.medium': '强度:中',
'form.basic-form.strength.short': '强度:太短',
'form.basic-form.confirm-password.required': '请确认密码!',
'form.basic-form.phone-number.required': '请输入手机号!',
'form.basic-form.phone-number.wrong-format': '手机号格式错误!',
'form.basic-form.verification-code.required': '请输入验证码!',
'form.basic-form.form.get-captcha': '获取验证码',
'form.basic-form.captcha.second': '秒',
'form.basic-form.form.optional': '(选填)',
'form.basic-form.form.submit': '提交',
'form.basic-form.form.save': '保存',
'form.basic-form.email.placeholder': '邮箱',
'form.basic-form.password.placeholder': '至少6位密码,区分大小写',
'form.basic-form.confirm-password.placeholder': '确认密码',
'form.basic-form.phone-number.placeholder': '手机号',
'form.basic-form.verification-code.placeholder': '验证码'
}
================================================
FILE: src/locales/lang/zh-CN/form.js
================================================
import basicForm from './form/basicForm'
export default {
...basicForm
}
================================================
FILE: src/locales/lang/zh-CN/global.js
================================================
export default {
submit: '提交',
save: '保存',
'submit.ok': '提交成功',
'save.ok': '保存成功'
}
================================================
FILE: src/locales/lang/zh-CN/menu.js
================================================
export default {
'menu.welcome': '欢迎',
'menu.home': '主页',
'menu.dashboard': '仪表盘',
'menu.dashboard.analysis': '分析页',
'menu.dashboard.monitor': '监控页',
'menu.dashboard.workplace': '工作台',
'menu.form': '表单页',
'menu.form.basic-form': '基础表单',
'menu.form.step-form': '分步表单',
'menu.form.step-form.info': '分步表单(填写转账信息)',
'menu.form.step-form.confirm': '分步表单(确认转账信息)',
'menu.form.step-form.result': '分步表单(完成)',
'menu.form.advanced-form': '高级表单',
'menu.list': '列表页',
'menu.list.table-list': '查询表格',
'menu.list.basic-list': '标准列表',
'menu.list.card-list': '卡片列表',
'menu.list.search-list': '搜索列表',
'menu.list.search-list.articles': '搜索列表(文章)',
'menu.list.search-list.projects': '搜索列表(项目)',
'menu.list.search-list.applications': '搜索列表(应用)',
'menu.profile': '详情页',
'menu.profile.basic': '基础详情页',
'menu.profile.advanced': '高级详情页',
'menu.result': '结果页',
'menu.result.success': '成功页',
'menu.result.fail': '失败页',
'menu.exception': '异常页',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '触发错误',
'menu.account': '个人页',
'menu.account.center': '个人中心',
'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录'
}
================================================
FILE: src/locales/lang/zh-CN/result/fail.js
================================================
export default {
'result.fail.error.title': '提交失败',
'result.fail.error.description': '请核对并修改以下信息后,再重新提交。',
'result.fail.error.hint-title': '您提交的内容有如下错误:',
'result.fail.error.hint-text1': '您的账户已被冻结',
'result.fail.error.hint-btn1': '立即解冻',
'result.fail.error.hint-text2': '您的账户还不具备申请资格',
'result.fail.error.hint-btn2': '立即升级',
'result.fail.error.btn-text': '返回修改'
}
================================================
FILE: src/locales/lang/zh-CN/result/success.js
================================================
export default {
'result.success.title': '提交成功',
'result.success.description':
'提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,使用 Message 全局提示反馈即可。 本文字区域可以展示简单的补充说明,如果有类似展示 “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。',
'result.success.operate-title': '项目名称',
'result.success.operate-id': '项目 ID',
'result.success.principal': '负责人',
'result.success.operate-time': '生效时间',
'result.success.step1-title': '创建项目',
'result.success.step1-operator': '曲丽丽',
'result.success.step2-title': '部门初审',
'result.success.step2-operator': '周毛毛',
'result.success.step2-extra': '催一下',
'result.success.step3-title': '财务复核',
'result.success.step4-title': '完成',
'result.success.btn-return': '返回列表',
'result.success.btn-project': '查看项目',
'result.success.btn-print': '打印'
}
================================================
FILE: src/locales/lang/zh-CN/result.js
================================================
import success from './result/success'
import fail from './result/fail'
export default {
...success,
...fail
}
================================================
FILE: src/locales/lang/zh-CN/setting.js
================================================
export default {
'app.setting.pagestyle': '整体风格设置',
'app.setting.pagestyle.light': '亮色菜单风格',
'app.setting.pagestyle.dark': '暗色菜单风格',
'app.setting.pagestyle.realdark': '暗黑模式',
'app.setting.themecolor': '主题色',
'app.setting.navigationmode': '导航模式',
'app.setting.content-width': '内容区域宽度',
'app.setting.fixedheader': '固定 Header',
'app.setting.fixedsidebar': '固定侧边栏',
'app.setting.sidemenu': '侧边菜单布局',
'app.setting.topmenu': '顶部菜单布局',
'app.setting.content-width.fixed': 'Fixed',
'app.setting.content-width.fluid': 'Fluid',
'app.setting.othersettings': '其他设置',
'app.setting.weakmode': '色弱模式',
'app.setting.copy': '拷贝设置',
'app.setting.loading': '加载主题中',
'app.setting.copyinfo': '拷贝设置成功 src/config/defaultSettings.js',
'app.setting.production.hint': '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
'app.setting.themecolor.daybreak': '拂晓蓝',
'app.setting.themecolor.dust': '薄暮',
'app.setting.themecolor.volcano': '火山',
'app.setting.themecolor.sunset': '日暮',
'app.setting.themecolor.cyan': '明青',
'app.setting.themecolor.green': '极光绿',
'app.setting.themecolor.geekblue': '极客蓝',
'app.setting.themecolor.purple': '酱紫'
}
================================================
FILE: src/locales/lang/zh-CN/user.js
================================================
export default {
'user.login.userName': '用户名',
'user.login.password': '密码',
'user.login.username.placeholder': '账户: admin',
'user.login.password.placeholder': '密码: admin or ant.design',
'user.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)',
'user.login.message-invalid-verification-code': '验证码错误',
'user.login.tab-login-credentials': '账户密码登录',
'user.login.tab-login-mobile': '手机号登录',
'user.login.mobile.placeholder': '手机号',
'user.login.mobile.verification-code.placeholder': '验证码',
'user.login.remember-me': '自动登录',
'user.login.forgot-password': '忘记密码',
'user.login.sign-in-with': '其他登录方式',
'user.login.signup': '注册账户',
'user.login.login': '登录',
'user.register.register': '注册',
'user.register.email.placeholder': '邮箱',
'user.register.password.placeholder': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'user.register.password.popover-message': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'user.register.confirm-password.placeholder': '确认密码',
'user.register.get-verification-code': '获取验证码',
'user.register.sign-in': '使用已有账户登录',
'user.register-result.msg': '你的账户:{email} 注册成功',
'user.register-result.activation-email':
'激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
'user.register-result.back-home': '返回首页',
'user.register-result.view-mailbox': '查看邮箱',
'user.email.required': '请输入邮箱地址!',
'user.email.wrong-format': '邮箱地址格式错误!',
'user.userName.required': '请输入帐户名或邮箱地址',
'user.password.required': '请输入密码!',
'user.password.twice.msg': '两次输入的密码不匹配!',
'user.password.strength.msg': '密码强度不够 ',
'user.password.strength.strong': '强度:强',
'user.password.strength.medium': '强度:中',
'user.password.strength.low': '强度:低',
'user.password.strength.short': '强度:太短',
'user.confirm-password.required': '请确认密码!',
'user.phone-number.required': '请输入正确的手机号',
'user.phone-number.wrong-format': '手机号格式错误!',
'user.verification-code.required': '请输入验证码!'
}
================================================
FILE: src/locales/lang/zh-CN.js
================================================
import antd from 'ant-design-vue/es/locale-provider/zh_CN'
import momentCN from 'moment/locale/zh-cn'
import global from './zh-CN/global'
import menu from './zh-CN/menu'
import setting from './zh-CN/setting'
import user from './zh-CN/user'
import dashboard from './zh-CN/dashboard'
import form from './zh-CN/form'
import result from './zh-CN/result'
import account from './zh-CN/account'
const components = {
antLocale: antd,
momentName: 'zh-cn',
momentLocale: momentCN
}
export default {
message: '-',
'layouts.usermenu.dialog.title': '信息',
'layouts.usermenu.dialog.content': '您确定要注销吗?',
'layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
...components,
...global,
...menu,
...setting,
...user,
...dashboard,
...form,
...result,
...account
}
================================================
FILE: src/main.js
================================================
// with polyfills
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/'
import i18n from './locales'
import { VueAxios } from './utils/request'
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
import themePluginConfig from '../config/themePluginConfig'
// mock
// WARNING: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV.
import './mock'
import bootstrap from './core/bootstrap'
import './core/lazy_use' // use lazy load components
import './permission' // permission control
import './utils/filter' // global filter
import './global.less' // global style
Vue.config.productionTip = false
// mount axios to `Vue.$http` and `this.$http`
Vue.use(VueAxios)
// use pro-layout components
Vue.component('pro-layout', ProLayout)
Vue.component('page-container', PageHeaderWrapper)
Vue.component('page-header-wrapper', PageHeaderWrapper)
window.umi_plugin_ant_themeVar = themePluginConfig.theme
new Vue({
router,
store,
i18n,
// init localstorage, vuex, Logo message
created: bootstrap,
render: h => h(App)
}).$mount('#app')
================================================
FILE: src/mock/index.js
================================================
import { isIE } from '@/utils/util'
// 判断环境不是 prod 或者 preview 是 true 时,加载 mock 服务
if (process.env.NODE_ENV !== 'production' || process.env.VUE_APP_PREVIEW === 'true') {
if (isIE()) {
console.error('[antd-pro] ERROR: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV.')
}
// 使用同步加载依赖
// 防止 vuex 中的 GetInfo 早于 mock 运行,导致无法 mock 请求返回结果
console.log('[antd-pro] mock mounting')
const Mock = require('mockjs2')
require('./services/auth')
require('./services/user')
require('./services/manage')
require('./services/other')
require('./services/tagCloud')
require('./services/article')
Mock.setup({
timeout: 800 // setter delay time
})
console.log('[antd-pro] mock mounted')
}
================================================
FILE: src/mock/services/article.js
================================================
import Mock from 'mockjs2'
import { builder, getQueryParameters } from '../util'
const titles = [
'Alipay',
'Angular',
'Ant Design',
'Ant Design Pro',
'Bootstrap',
'React',
'Vue',
'Webpack'
]
const avatar = ['https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png'
]
const covers = [
'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png',
'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png',
'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png',
'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png'
]
const owner = [
'付小小',
'吴加好',
'周星星',
'林东东',
'曲丽丽'
]
const content = '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。'
const description = '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。'
const href = 'https://ant.design'
const article = (options) => {
const queryParameters = getQueryParameters(options)
console.log('queryParameters', queryParameters)
if (queryParameters && !queryParameters.count) {
queryParameters.count = 5
}
const data = []
for (let i = 0; i < queryParameters.count; i++) {
const tmpKey = i + 1
const num = parseInt(Math.random() * (4 + 1), 10)
data.push({
id: tmpKey,
avatar: avatar[num],
owner: owner[num],
content: content,
star: Mock.mock('@integer(1, 999)'),
percent: Mock.mock('@integer(1, 999)'),
like: Mock.mock('@integer(1, 999)'),
message: Mock.mock('@integer(1, 999)'),
description: description,
href: href,
title: titles[ i % 8 ],
updatedAt: Mock.mock('@datetime'),
members: [
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',
name: '曲丽丽',
id: 'member1'
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png',
name: '王昭君',
id: 'member2'
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png',
name: '董娜娜',
id: 'member3'
}
],
activeUser: Math.ceil(Math.random() * 100000) + 100000,
newUser: Math.ceil(Math.random() * 1000) + 1000,
cover: parseInt(i / 4, 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)]
})
}
return builder(data)
}
Mock.mock(/\/list\/article/, 'get', article)
================================================
FILE: src/mock/services/auth.js
================================================
import Mock from 'mockjs2'
import { builder, getBody } from '../util'
const username = ['admin', 'super']
// 强硬要求 ant.design 相同密码
// '21232f297a57a5a743894a0e4a801fc3',
const password = ['8914de686ab28dc22f30d3d8e107ff6c', '21232f297a57a5a743894a0e4a801fc3'] // admin, ant.design
const login = (options) => {
const body = getBody(options)
console.log('mock: body', body)
if (!username.includes(body.username) || !password.includes(body.password)) {
return builder({ isLogin: true }, '账户或密码错误', 401)
}
return builder({
'id': Mock.mock('@guid'),
'name': Mock.mock('@name'),
'username': 'admin',
'password': '',
'avatar': 'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png',
'status': 1,
'telephone': '',
'lastLoginIp': '27.154.74.117',
'lastLoginTime': 1534837621348,
'creatorId': 'admin',
'createTime': 1497160610259,
'deleted': 0,
'roleId': 'admin',
'lang': 'zh-CN',
'token': '4291d7da9005377ec9aec4a71ea837f'
}, '', 200, { 'Custom-Header': Mock.mock('@guid') })
}
const logout = () => {
return builder({}, '[测试接口] 注销成功')
}
const smsCaptcha = () => {
return builder({ captcha: Mock.mock('@integer(10000, 99999)') })
}
const twofactor = () => {
return builder({ stepCode: Mock.mock('@integer(0, 1)') })
}
Mock.mock(/\/auth\/login/, 'post', login)
Mock.mock(/\/auth\/logout/, 'post', logout)
Mock.mock(/\/account\/sms/, 'post', smsCaptcha)
Mock.mock(/\/auth\/2step-code/, 'post', twofactor)
================================================
FILE: src/mock/services/manage.js
================================================
import Mock from 'mockjs2'
import { builder, getQueryParameters } from '../util'
const totalCount = 5701
const serverList = (options) => {
const parameters = getQueryParameters(options)
const result = []
const pageNo = parseInt(parameters.pageNo)
const pageSize = parseInt(parameters.pageSize)
const totalPage = Math.ceil(totalCount / pageSize)
const key = (pageNo - 1) * pageSize
const next = (pageNo >= totalPage ? (totalCount % pageSize) : pageSize) + 1
for (let i = 1; i < next; i++) {
const tmpKey = key + i
result.push({
key: tmpKey,
id: tmpKey,
no: 'No ' + tmpKey,
description: '这是一段描述',
callNo: Mock.mock('@integer(1, 999)'),
status: Mock.mock('@integer(0, 3)'),
updatedAt: Mock.mock('@datetime'),
editable: false
})
}
return builder({
pageSize: pageSize,
pageNo: pageNo,
totalCount: totalCount,
totalPage: totalPage,
data: result
})
}
const projects = () => {
return builder({
'data': [{
id: 1,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
title: 'Alipay',
description: '那是一种内在的东西, 他们到达不了,也无法触及的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 2,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png',
title: 'Angular',
description: '希望是一个好东西,也许是最好的,好东西是不会消亡的',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 3,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png',
title: 'Ant Design',
description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 4,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png',
title: 'Ant Design Pro',
description: '那时候我只会想自己想要什么,从不想自己拥有什么',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 5,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png',
title: 'Bootstrap',
description: '凛冬将至',
status: 1,
updatedAt: '2018-07-26 00:00:00'
},
{
id: 6,
cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png',
title: 'Vue',
description: '生命就像一盒巧克力,结果往往出人意料',
status: 1,
updatedAt: '2018-07-26 00:00:00'
}
],
'pageSize': 10,
'pageNo': 0,
'totalPage': 6,
'totalCount': 57
})
}
const activity = () => {
return builder([{
id: 1,
user: {
nickname: '@name',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'
},
project: {
name: '白鹭酱油开发组',
action: '更新',
event: '番组计划'
},
time: '2018-08-23 14:47:00'
},
{
id: 1,
user: {
nickname: '蓝莓酱',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png'
},
project: {
name: '白鹭酱油开发组',
action: '更新',
event: '番组计划'
},
time: '2018-08-23 09:35:37'
},
{
id: 1,
user: {
nickname: '@name',
avatar: '@image(64x64)'
},
project: {
name: '白鹭酱油开发组',
action: '创建',
event: '番组计划'
},
time: '2017-05-27 00:00:00'
},
{
id: 1,
user: {
nickname: '曲丽丽',
avatar: '@image(64x64)'
},
project: {
name: '高逼格设计天团',
action: '更新',
event: '六月迭代'
},
time: '2018-08-23 14:47:00'
},
{
id: 1,
user: {
nickname: '@name',
avatar: '@image(64x64)'
},
project: {
name: '高逼格设计天团',
action: 'created',
event: '六月迭代'
},
time: '2018-08-23 14:47:00'
},
{
id: 1,
user: {
nickname: '曲丽丽',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'
},
project: {
name: '高逼格设计天团',
action: 'created',
event: '六月迭代'
},
time: '2018-08-23 14:47:00'
}
])
}
const teams = () => {
return builder([{
id: 1,
name: '科学搬砖组',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'
},
{
id: 2,
name: '程序员日常',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png'
},
{
id: 1,
name: '设计天团',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png'
},
{
id: 1,
name: '中二少女团',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png'
},
{
id: 1,
name: '骗你学计算机',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png'
}
])
}
const radar = () => {
return builder([{
item: '引用',
'个人': 70,
'团队': 30,
'部门': 40
},
{
item: '口碑',
'个人': 60,
'团队': 70,
'部门': 40
},
{
item: '产量',
'个人': 50,
'团队': 60,
'部门': 40
},
{
item: '贡献',
'个人': 40,
'团队': 50,
'部门': 40
},
{
item: '热度',
'个人': 60,
'团队': 70,
'部门': 40
},
{
item: '引用',
'个人': 70,
'团队': 50,
'部门': 40
}
])
}
Mock.mock(/\/service/, 'get', serverList)
Mock.mock(/\/list\/search\/projects/, 'get', projects)
Mock.mock(/\/workplace\/activity/, 'get', activity)
Mock.mock(/\/workplace\/teams/, 'get', teams)
Mock.mock(/\/workplace\/radar/, 'get', radar)
================================================
FILE: src/mock/services/other.js
================================================
import Mock from 'mockjs2'
import { builder } from '../util'
const orgTree = () => {
return builder([{
'key': 'key-01',
'title': '研发中心',
'icon': 'mail',
'children': [{
'key': 'key-01-01',
'title': '后端组',
'icon': null,
'group': true,
children: [{
'key': 'key-01-01-01',
'title': 'JAVA',
'icon': null
},
{
'key': 'key-01-01-02',
'title': 'PHP',
'icon': null
},
{
'key': 'key-01-01-03',
'title': 'Golang',
'icon': null
}
]
}, {
'key': 'key-01-02',
'title': '前端组',
'icon': null,
'group': true,
children: [{
'key': 'key-01-02-01',
'title': 'React',
'icon': null
},
{
'key': 'key-01-02-02',
'title': 'Vue',
'icon': null
},
{
'key': 'key-01-02-03',
'title': 'Angular',
'icon': null
}
]
}]
}, {
'key': 'key-02',
'title': '财务部',
'icon': 'dollar',
'children': [{
'key': 'key-02-01',
'title': '会计核算',
'icon': null
}, {
'key': 'key-02-02',
'title': '成本控制',
'icon': null
}, {
'key': 'key-02-03',
'title': '内部控制',
'icon': null,
'children': [{
'key': 'key-02-03-01',
'title': '财务制度建设',
'icon': null
},
{
'key': 'key-02-03-02',
'title': '会计核算',
'icon': null
}
]
}]
}])
}
const role = () => {
return builder({
'data': [{
'id': 'admin',
'name': '管理员',
'describe': '拥有所有权限',
'status': 1,
'creatorId': 'system',
'createTime': 1497160610259,
'deleted': 0,
'permissions': [{
'roleId': 'admin',
'permissionId': 'comment',
'permissionName': '评论管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}],
'actionList': ['delete', 'edit'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'member',
'permissionName': '会员管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': ['query', 'get', 'edit', 'delete'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'menu',
'permissionName': '菜单管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'import',
'describe': '导入',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
}
],
'actionList': ['add', 'import'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'order',
'permissionName': '订单管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': ['query', 'add', 'get'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'permission',
'permissionName': '权限管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': ['add', 'get', 'edit', 'delete'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'role',
'permissionName': '角色管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'test',
'permissionName': '测试权限',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'user',
'permissionName': '用户管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"},{"action":"export","defaultCheck":false,"describe":"导出"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'import',
'describe': '导入',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
},
{
'action': 'export',
'describe': '导出',
'defaultCheck': false
}
],
'actionList': ['add', 'get'],
'dataAccess': null
}
]
},
{
'id': 'svip',
'name': 'SVIP',
'describe': '超级会员',
'status': 1,
'creatorId': 'system',
'createTime': 1532417744846,
'deleted': 0,
'permissions': [{
'roleId': 'admin',
'permissionId': 'comment',
'permissionName': '评论管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': ['add', 'get', 'delete'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'member',
'permissionName': '会员管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
}
],
'actionList': ['add', 'query', 'get'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'menu',
'permissionName': '菜单管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'import',
'describe': '导入',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
}
],
'actionList': ['add', 'get'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'order',
'permissionName': '订单管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
}
],
'actionList': ['add', 'query'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'permission',
'permissionName': '权限管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
}
],
'actionList': ['add', 'get', 'edit'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'role',
'permissionName': '角色管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
},
{
'action': 'delete',
'describe': '删除',
'defaultCheck': false
}
],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'test',
'permissionName': '测试权限',
'actions': '[]',
'actionEntitySet': [],
'actionList': ['add', 'edit'],
'dataAccess': null
},
{
'roleId': 'admin',
'permissionId': 'user',
'permissionName': '用户管理',
'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"},{"action":"export","defaultCheck":false,"describe":"导出"}]',
'actionEntitySet': [{
'action': 'add',
'describe': '新增',
'defaultCheck': false
},
{
'action': 'import',
'describe': '导入',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
},
{
'action': 'edit',
'describe': '修改',
'defaultCheck': false
}
],
'actionList': ['add'],
'dataAccess': null
}
]
},
{
'id': 'user',
'name': '普通会员',
'describe': '普通用户,只能查询',
'status': 1,
'creatorId': 'system',
'createTime': 1497160610259,
'deleted': 0,
'permissions': [{
'roleId': 'user',
'permissionId': 'comment',
'permissionName': '评论管理',
'actions': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"}]',
'actionEntitySet': [{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
}
],
'actionList': ['query'],
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'marketing',
'permissionName': '营销管理',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'member',
'permissionName': '会员管理',
'actions': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"}]',
'actionEntitySet': [{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
}
],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'menu',
'permissionName': '菜单管理',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'order',
'permissionName': '订单管理',
'actions': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"}]',
'actionEntitySet': [{
'action': 'query',
'describe': '查询',
'defaultCheck': false
},
{
'action': 'get',
'describe': '详情',
'defaultCheck': false
}
],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'permission',
'permissionName': '权限管理',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'role',
'permissionName': '角色管理',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'test',
'permissionName': '测试权限',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
},
{
'roleId': 'user',
'permissionId': 'user',
'permissionName': '用户管理',
'actions': '[]',
'actionEntitySet': [],
'actionList': null,
'dataAccess': null
}
]
}
],
'pageSize': 10,
'pageNo': 0,
'totalPage': 1,
'totalCount': 5
})
}
const permissionNoPager = () => {
return builder([{
'id': 'marketing',
'name': '营销管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': null,
'parents': null,
'type': null,
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'member',
'name': '会员管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'menu',
'name': '菜单管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'import',
'get',
'edit'
]
},
{
'id': 'order',
'name': '订单管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'permission',
'name': '权限管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get',
'edit',
'delete'
]
},
{
'id': 'role',
'name': '角色管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get',
'edit',
'delete'
]
},
{
'id': 'test',
'name': '测试权限',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get'
]
},
{
'id': 'user',
'name': '用户管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"export","defaultCheck":false,"describe":"导出"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get'
]
}
])
}
const permissions = () => {
return builder({
'data': [{
'id': 'marketing',
'name': '营销管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': null,
'parents': null,
'type': null,
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'member',
'name': '会员管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'menu',
'name': '菜单管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'import',
'get',
'edit'
]
},
{
'id': 'order',
'name': '订单管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'query',
'get',
'edit',
'delete'
]
},
{
'id': 'permission',
'name': '权限管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get',
'edit',
'delete'
]
},
{
'id': 'role',
'name': '角色管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"查询"},{"action":"edit","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get',
'edit',
'delete'
]
},
{
'id': 'test',
'name': '测试权限',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get'
]
},
{
'id': 'user',
'name': '用户管理',
'describe': null,
'status': 1,
'actionData': '[{"action":"add","describe":"新增","defaultCheck":false},{"action":"get","describe":"查询","defaultCheck":false}]',
'sptDaTypes': null,
'optionalFields': '[]',
'parents': null,
'type': 'default',
'deleted': 0,
'actions': [
'add',
'get'
]
}
],
'pageSize': 10,
'pageNo': 0,
'totalPage': 1,
'totalCount': 5
})
}
Mock.mock(/\/org\/tree/, 'get', orgTree)
Mock.mock(/\/role/, 'get', role)
Mock.mock(/\/permission\/no-pager/, 'get', permissionNoPager)
Mock.mock(/\/permission/, 'get', permissions)
================================================
FILE: src/mock/services/tagCloud.js
================================================
import Mock from 'mockjs2'
import { builder } from '../util'
//
const tagCloudData = () => {
return builder([{ 'value': 9, 'name': 'AntV' }, { 'value': 8, 'name': 'F2' }, { 'value': 8, 'name': 'G2' }, { 'value': 8, 'name': 'G6' }, { 'value': 8, 'name': 'DataSet' }, { 'value': 8, 'name': '墨者学院' }, { 'value': 6, 'name': 'Analysis' }, { 'value': 6, 'name': 'Data Mining' }, { 'value': 6, 'name': 'Data Vis' }, { 'value': 6, 'name': 'Design' }, { 'value': 6, 'name': 'Grammar' }, { 'value': 6, 'name': 'Graphics' }, { 'value': 6, 'name': 'Graph' }, { 'value': 6, 'name': 'Hierarchy' }, { 'value': 6, 'name': 'Labeling' }, { 'value': 6, 'name': 'Layout' }, { 'value': 6, 'name': 'Quantitative' }, { 'value': 6, 'name': 'Relation' }, { 'value': 6, 'name': 'Statistics' }, { 'value': 6, 'name': '可视化' }, { 'value': 6, 'name': '数据' }, { 'value': 6, 'name': '数据可视化' }, { 'value': 4, 'name': 'Arc Diagram' }, { 'value': 4, 'name': 'Bar Chart' }, { 'value': 4, 'name': 'Canvas' }, { 'value': 4, 'name': 'Chart' }, { 'value': 4, 'name': 'DAG' }, { 'value': 4, 'name': 'DG' }, { 'value': 4, 'name': 'Facet' }, { 'value': 4, 'name': 'Geo' }, { 'value': 4, 'name': 'Line' }, { 'value': 4, 'name': 'MindMap' }, { 'value': 4, 'name': 'Pie' }, { 'value': 4, 'name': 'Pizza Chart' }, { 'value': 4, 'name': 'Punch Card' }, { 'value': 4, 'name': 'SVG' }, { 'value': 4, 'name': 'Sunburst' }, { 'value': 4, 'name': 'Tree' }, { 'value': 4, 'name': 'UML' }, { 'value': 3, 'name': 'Chart' }, { 'value': 3, 'name': 'View' }, { 'value': 3, 'name': 'Geom' }, { 'value': 3, 'name': 'Shape' }, { 'value': 3, 'name': 'Scale' }, { 'value': 3, 'name': 'Animate' }, { 'value': 3, 'name': 'Global' }, { 'value': 3, 'name': 'Slider' }, { 'value': 3, 'name': 'Connector' }, { 'value': 3, 'name': 'Transform' }, { 'value': 3, 'name': 'Util' }, { 'value': 3, 'name': 'DomUtil' }, { 'value': 3, 'name': 'MatrixUtil' }, { 'value': 3, 'name': 'PathUtil' }, { 'value': 3, 'name': 'G' }, { 'value': 3, 'name': '2D' }, { 'value': 3, 'name': '3D' }, { 'value': 3, 'name': 'Line' }, { 'value': 3, 'name': 'Area' }, { 'value': 3, 'name': 'Interval' }, { 'value': 3, 'name': 'Schema' }, { 'value': 3, 'name': 'Edge' }, { 'value': 3, 'name': 'Polygon' }, { 'value': 3, 'name': 'Heatmap' }, { 'value': 3, 'name': 'Render' }, { 'value': 3, 'name': 'Tooltip' }, { 'value': 3, 'name': 'Axis' }, { 'value': 3, 'name': 'Guide' }, { 'value': 3, 'name': 'Coord' }, { 'value': 3, 'name': 'Legend' }, { 'value': 3, 'name': 'Path' }, { 'value': 3, 'name': 'Helix' }, { 'value': 3, 'name': 'Theta' }, { 'value': 3, 'name': 'Rect' }, { 'value': 3, 'name': 'Polar' }, { 'value': 3, 'name': 'Dsv' }, { 'value': 3, 'name': 'Csv' }, { 'value': 3, 'name': 'Tsv' }, { 'value': 3, 'name': 'GeoJSON' }, { 'value': 3, 'name': 'TopoJSON' }, { 'value': 3, 'name': 'Filter' }, { 'value': 3, 'name': 'Map' }, { 'value': 3, 'name': 'Pick' }, { 'value': 3, 'name': 'Rename' }, { 'value': 3, 'name': 'Filter' }, { 'value': 3, 'name': 'Map' }, { 'value': 3, 'name': 'Pick' }, { 'value': 3, 'name': 'Rename' }, { 'value': 3, 'name': 'Reverse' }, { 'value': 3, 'name': 'sort' }, { 'value': 3, 'name': 'Subset' }, { 'value': 3, 'name': 'Partition' }, { 'value': 3, 'name': 'Imputation' }, { 'value': 3, 'name': 'Fold' }, { 'value': 3, 'name': 'Aggregate' }, { 'value': 3, 'name': 'Proportion' }, { 'value': 3, 'name': 'Histogram' }, { 'value': 3, 'name': 'Quantile' }, { 'value': 3, 'name': 'Treemap' }, { 'value': 3, 'name': 'Hexagon' }, { 'value': 3, 'name': 'Binning' }, { 'value': 3, 'name': 'kernel' }, { 'value': 3, 'name': 'Regression' }, { 'value': 3, 'name': 'Density' }, { 'value': 3, 'name': 'Sankey' }, { 'value': 3, 'name': 'Voronoi' }, { 'value': 3, 'name': 'Projection' }, { 'value': 3, 'name': 'Centroid' }, { 'value': 3, 'name': 'H5' }, { 'value': 3, 'name': 'Mobile' }, { 'value': 3, 'name': 'K线图' }, { 'value': 3, 'name': '关系图' }, { 'value': 3, 'name': '烛形图' }, { 'value': 3, 'name': '股票图' }, { 'value': 3, 'name': '直方图' }, { 'value': 3, 'name': '金字塔图' }, { 'value': 3, 'name': '分面' }, { 'value': 3, 'name': '南丁格尔玫瑰图' }, { 'value': 3, 'name': '饼图' }, { 'value': 3, 'name': '线图' }, { 'value': 3, 'name': '点图' }, { 'value': 3, 'name': '散点图' }, { 'value': 3, 'name': '子弹图' }, { 'value': 3, 'name': '柱状图' }, { 'value': 3, 'name': '仪表盘' }, { 'value': 3, 'name': '气泡图' }, { 'value': 3, 'name': '漏斗图' }, { 'value': 3, 'name': '热力图' }, { 'value': 3, 'name': '玉玦图' }, { 'value': 3, 'name': '直方图' }, { 'value': 3, 'name': '矩形树图' }, { 'value': 3, 'name': '箱形图' }, { 'value': 3, 'name': '色块图' }, { 'value': 3, 'name': '螺旋图' }, { 'value': 3, 'name': '词云' }, { 'value': 3, 'name': '词云图' }, { 'value': 3, 'name': '雷达图' }, { 'value': 3, 'name': '面积图' }, { 'value': 3, 'name': '马赛克图' }, { 'value': 3, 'name': '盒须图' }, { 'value': 3, 'name': '坐标轴' }, { 'value': 3, 'name': '' }, { 'value': 3, 'name': 'Jacques Bertin' }, { 'value': 3, 'name': 'Leland Wilkinson' }, { 'value': 3, 'name': 'William Playfair' }, { 'value': 3, 'name': '关联' }, { 'value': 3, 'name': '分布' }, { 'value': 3, 'name': '区间' }, { 'value': 3, 'name': '占比' }, { 'value': 3, 'name': '地图' }, { 'value': 3, 'name': '时间' }, { 'value': 3, 'name': '比较' }, { 'value': 3, 'name': '流程' }, { 'value': 3, 'name': '趋势' }, { 'value': 2, 'name': '亦叶' }, { 'value': 2, 'name': '再飞' }, { 'value': 2, 'name': '完白' }, { 'value': 2, 'name': '巴思' }, { 'value': 2, 'name': '张初尘' }, { 'value': 2, 'name': '御术' }, { 'value': 2, 'name': '有田' }, { 'value': 2, 'name': '沉鱼' }, { 'value': 2, 'name': '玉伯' }, { 'value': 2, 'name': '画康' }, { 'value': 2, 'name': '祯逸' }, { 'value': 2, 'name': '绝云' }, { 'value': 2, 'name': '罗宪' }, { 'value': 2, 'name': '萧庆' }, { 'value': 2, 'name': '董珊珊' }, { 'value': 2, 'name': '陆沉' }, { 'value': 2, 'name': '顾倾' }, { 'value': 2, 'name': 'Domo' }, { 'value': 2, 'name': 'GPL' }, { 'value': 2, 'name': 'PAI' }, { 'value': 2, 'name': 'SPSS' }, { 'value': 2, 'name': 'SYSTAT' }, { 'value': 2, 'name': 'Tableau' }, { 'value': 2, 'name': 'D3' }, { 'value': 2, 'name': 'Vega' }, { 'value': 2, 'name': '统计图表' }])
}
Mock.mock(/\/data\/antv\/tag-cloud/, 'get', tagCloudData)
================================================
FILE: src/mock/services/user.js
================================================
import Mock from 'mockjs2'
import { builder } from '../util'
const info = options => {
console.log('options', options)
const userInfo = {
id: '4291d7da9005377ec9aec4a71ea837f',
name: '天野远子',
username: 'admin',
password: '',
avatar: '/avatar2.jpg',
status: 1,
telephone: '',
lastLoginIp: '27.154.74.117',
lastLoginTime: 1534837621348,
creatorId: 'admin',
createTime: 1497160610259,
merchantCode: 'TLif2btpzg079h15bk',
deleted: 0,
roleId: 'admin',
role: {}
}
// role
const roleObj = {
id: 'admin',
name: '管理员',
describe: '拥有所有权限',
status: 1,
creatorId: 'system',
createTime: 1497160610259,
deleted: 0,
permissions: [
{
roleId: 'admin',
permissionId: 'dashboard',
permissionName: '仪表盘',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'exception',
permissionName: '异常页面权限',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'result',
permissionName: '结果权限',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'profile',
permissionName: '详细页权限',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'table',
permissionName: '表格权限',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'import',
describe: '导入',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'form',
permissionName: '表单权限',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'order',
permissionName: '订单管理',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'permission',
permissionName: '权限管理',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'role',
permissionName: '角色管理',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'table',
permissionName: '桌子管理',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'query',
describe: '查询',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
},
{
roleId: 'admin',
permissionId: 'user',
permissionName: '用户管理',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"},{"action":"export","defaultCheck":false,"describe":"导出"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'import',
describe: '导入',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
},
{
action: 'export',
describe: '导出',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
}
]
}
roleObj.permissions.push({
roleId: 'admin',
permissionId: 'support',
permissionName: '超级模块',
actions:
'[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"import","defaultCheck":false,"describe":"导入"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"},{"action":"export","defaultCheck":false,"describe":"导出"}]',
actionEntitySet: [
{
action: 'add',
describe: '新增',
defaultCheck: false
},
{
action: 'import',
describe: '导入',
defaultCheck: false
},
{
action: 'get',
describe: '详情',
defaultCheck: false
},
{
action: 'update',
describe: '修改',
defaultCheck: false
},
{
action: 'delete',
describe: '删除',
defaultCheck: false
},
{
action: 'export',
describe: '导出',
defaultCheck: false
}
],
actionList: null,
dataAccess: null
})
userInfo.role = roleObj
return builder(userInfo)
}
/**
* 使用 用户登录的 token 获取用户有权限的菜单
* 返回结构必须按照这个结构体形式处理,或根据
* /src/router/generator-routers.js 文件的菜单结构处理函数对应即可
* @param {*} options
* @returns
*/
const userNav = options => {
const nav = [
// dashboard
{
name: 'dashboard',
parentId: 0,
id: 1,
meta: {
title: 'menu.dashboard',
icon: 'dashboard',
show: true
},
component: 'RouteView',
redirect: '/dashboard/workplace'
},
{
name: 'workplace',
parentId: 1,
id: 7,
meta: {
title: 'menu.dashboard.monitor',
show: true
},
component: 'Workplace'
},
{
name: 'monitor',
path: 'https://www.baidu.com/',
parentId: 1,
id: 3,
meta: {
title: 'menu.dashboard.workplace',
target: '_blank',
show: true
}
},
{
name: 'Analysis',
parentId: 1,
id: 2,
meta: {
title: 'menu.dashboard.analysis',
show: true
},
component: 'Analysis',
path: '/dashboard/analysis'
},
// form
{
name: 'form',
parentId: 0,
id: 10,
meta: {
icon: 'form',
title: 'menu.form'
},
redirect: '/form/base-form',
component: 'RouteView'
},
{
name: 'basic-form',
parentId: 10,
id: 6,
meta: {
title: 'menu.form.basic-form'
},
component: 'BasicForm'
},
{
name: 'step-form',
parentId: 10,
id: 5,
meta: {
title: 'menu.form.step-form'
},
component: 'StepForm'
},
{
name: 'advanced-form',
parentId: 10,
id: 4,
meta: {
title: 'menu.form.advanced-form'
},
component: 'AdvanceForm'
},
// list
{
name: 'list',
parentId: 0,
id: 10010,
meta: {
icon: 'table',
title: 'menu.list',
show: true
},
redirect: '/list/table-list',
component: 'RouteView'
},
{
name: 'table-list',
parentId: 10010,
id: 10011,
path: '/list/table-list/:pageNo([1-9]\\d*)?',
meta: {
title: 'menu.list.table-list',
show: true
},
component: 'TableList'
},
{
name: 'basic-list',
parentId: 10010,
id: 10012,
meta: {
title: 'menu.list.basic-list',
show: true
},
component: 'StandardList'
},
{
name: 'card',
parentId: 10010,
id: 10013,
meta: {
title: 'menu.list.card-list',
show: true
},
component: 'CardList'
},
{
name: 'search',
parentId: 10010,
id: 10014,
meta: {
title: 'menu.list.search-list',
show: true
},
redirect: '/list/search/article',
component: 'SearchLayout'
},
{
name: 'article',
parentId: 10014,
id: 10015,
meta: {
title: 'menu.list.search-list.articles',
show: true
},
component: 'SearchArticles'
},
{
name: 'project',
parentId: 10014,
id: 10016,
meta: {
title: 'menu.list.search-list.projects',
show: true
},
component: 'SearchProjects'
},
{
name: 'application',
parentId: 10014,
id: 10017,
meta: {
title: 'menu.list.search-list.applications',
show: true
},
component: 'SearchApplications'
},
// profile
{
name: 'profile',
parentId: 0,
id: 10018,
meta: {
title: 'menu.profile',
icon: 'profile',
show: true
},
redirect: '/profile/basic',
component: 'RouteView'
},
{
name: 'basic',
parentId: 10018,
id: 10019,
meta: {
title: 'menu.profile.basic',
show: true
},
component: 'ProfileBasic'
},
{
name: 'advanced',
parentId: 10018,
id: 10020,
meta: {
title: 'menu.profile.advanced',
show: true
},
component: 'ProfileAdvanced'
},
// result
{
name: 'result',
parentId: 0,
id: 10021,
meta: {
title: 'menu.result',
icon: 'check-circle-o',
show: true
},
redirect: '/result/success',
component: 'PageView'
},
{
name: 'success',
parentId: 10021,
id: 10022,
meta: {
title: 'menu.result.success',
hiddenHeaderContent: true,
show: true
},
component: 'ResultSuccess'
},
{
name: 'fail',
parentId: 10021,
id: 10023,
meta: {
title: 'menu.result.fail',
hiddenHeaderContent: true,
show: true
},
component: 'ResultFail'
},
// Exception
{
name: 'exception',
parentId: 0,
id: 10024,
meta: {
title: 'menu.exception',
icon: 'warning',
show: true
},
redirect: '/exception/403',
component: 'RouteView'
},
{
name: '403',
parentId: 10024,
id: 10025,
meta: {
title: 'menu.exception.not-permission',
show: true
},
component: 'Exception403'
},
{
name: '404',
parentId: 10024,
id: 10026,
meta: {
title: 'menu.exception.not-find',
show: true
},
component: 'Exception404'
},
{
name: '500',
parentId: 10024,
id: 10027,
meta: {
title: 'menu.exception.server-error',
show: true
},
component: 'Exception500'
},
// account
{
name: 'account',
parentId: 0,
id: 10028,
meta: {
title: 'menu.account',
icon: 'user',
show: true
},
redirect: '/account/center',
component: 'RouteView'
},
{
name: 'center',
parentId: 10028,
id: 10029,
meta: {
title: 'menu.account.center',
show: true
},
component: 'AccountCenter'
},
// 特殊三级菜单
{
name: 'settings',
parentId: 10028,
id: 10030,
meta: {
title: 'menu.account.settings',
hideHeader: true,
hideChildren: true,
show: true
},
redirect: '/account/settings/basic',
component: 'AccountSettings'
},
{
name: 'BasicSettings',
path: '/account/settings/basic',
parentId: 10030,
id: 10031,
meta: {
title: 'account.settings.menuMap.basic',
show: false
},
component: 'BasicSetting'
},
{
name: 'SecuritySettings',
path: '/account/settings/security',
parentId: 10030,
id: 10032,
meta: {
title: 'account.settings.menuMap.security',
show: false
},
component: 'SecuritySettings'
},
{
name: 'CustomSettings',
path: '/account/settings/custom',
parentId: 10030,
id: 10033,
meta: {
title: 'account.settings.menuMap.custom',
show: false
},
component: 'CustomSettings'
},
{
name: 'BindingSettings',
path: '/account/settings/binding',
parentId: 10030,
id: 10034,
meta: {
title: 'account.settings.menuMap.binding',
show: false
},
component: 'BindingSettings'
},
{
name: 'NotificationSettings',
path: '/account/settings/notification',
parentId: 10030,
id: 10034,
meta: {
title: 'account.settings.menuMap.notification',
show: false
},
component: 'NotificationSettings'
}
]
const json = builder(nav)
console.log('json', json)
return json
}
Mock.mock(/\/api\/user\/info/, 'get', info)
Mock.mock(/\/api\/user\/nav/, 'get', userNav)
================================================
FILE: src/mock/util.js
================================================
const responseBody = {
message: '',
timestamp: 0,
result: null,
code: 0
}
export const builder = (data, message, code = 0, headers = {}) => {
responseBody.result = data
if (message !== undefined && message !== null) {
responseBody.message = message
}
if (code !== undefined && code !== 0) {
responseBody.code = code
responseBody._status = code
}
if (headers !== null && typeof headers === 'object' && Object.keys(headers).length > 0) {
responseBody._headers = headers
}
responseBody.timestamp = new Date().getTime()
return responseBody
}
export const getQueryParameters = (options) => {
const url = options.url
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse('{"' + decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') + '"}')
}
export const getBody = (options) => {
return options.body && JSON.parse(options.body)
}
================================================
FILE: src/permission.js
================================================
import router, { resetRouter } from './router'
import store from './store'
import storage from 'store'
import NProgress from 'nprogress' // progress bar
import '@/components/NProgress/nprogress.less' // progress bar custom style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { i18nRender } from '@/locales'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const allowList = ['login', 'register', 'registerResult'] // no redirect allowList
const loginRoutePath = '/user/login'
const defaultRoutePath = '/dashboard/workplace'
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
to.meta && typeof to.meta.title !== 'undefined' && setDocumentTitle(`${i18nRender(to.meta.title)} - ${domTitle}`)
/* has token */
const token = storage.get(ACCESS_TOKEN)
if (token) {
if (to.path === loginRoutePath) {
next({ path: defaultRoutePath })
NProgress.done()
} else {
// check login user.roles is null
if (store.getters.roles.length === 0) {
// request login userInfo
store
.dispatch('GetInfo')
.then(res => {
console.log('res', res)
// 根据用户权限信息生成可访问的路由表
store.dispatch('GenerateRoutes', { token, ...res }).then(() => {
// 动态添加可访问路由表
// VueRouter@3.5.0+ New API
resetRouter() // 重置路由 防止退出重新登录或者 token 过期后页面未刷新,导致的路由重复添加
store.getters.addRouters.forEach(r => {
router.addRoute(r)
})
// 请求带有 redirect 重定向时,登录自动重定向到该地址
const redirect = decodeURIComponent(from.query.redirect || to.path)
if (to.path === redirect) {
// set the replace: true so the navigation will not leave a history record
next({ ...to, replace: true })
} else {
// 跳转到目的路由
next({ path: redirect })
}
})
})
.catch(() => {
notification.error({
message: '错误',
description: '请求用户信息失败,请重试'
})
// 失败时,获取用户信息失败时,调用登出,来清空历史保留信息
store.dispatch('Logout').then(() => {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
})
})
} else {
next()
}
}
} else {
if (allowList.includes(to.name)) {
// 在免登录名单,直接进入
next()
} else {
next({ path: loginRoutePath, query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
================================================
FILE: src/router/README.md
================================================
路由/菜单说明
====
格式和说明
----
```ecmascript 6
const routerObject = {
redirect: noredirect,
name: 'router-name',
hidden: true,
meta: {
title: 'title',
icon: 'a-icon',
target: '_blank|_self|_top|_parent',
keepAlive: true,
hiddenHeaderContent: true,
}
}
```
`{ Route }` 对象
| 参数 | 说明 | 类型 | 默认值 |
| -------- | ----------------------------------------- | ------- | ------ |
| hidden | 控制路由是否显示在 sidebar | boolean | false |
| redirect | 重定向地址, 访问这个路由时,自定进行重定向 | string | - |
| name | 路由名称, 必须设置,且不能重名 | string | - |
| meta | 路由元信息(路由附带扩展信息) | object | {} |
| hideChildrenInMenu | 强制菜单显示为Item而不是SubItem(配合 meta.hidden) | boolean | - |
`{ Meta }` 路由元信息对象
| 参数 | 说明 | 类型 | 默认值 |
| ------------------- | ------------------------------------------------------------ | ------- | ------ |
| title | 路由标题, 用于显示面包屑, 页面标题 *推荐设置 | string | - |
| icon | 路由在 menu 上显示的图标 | [string,svg] | - |
| keepAlive | 缓存该路由 | boolean | false |
| target | 菜单链接跳转目标(参考 html a 标记) | string | - |
| hidden | 配合`hideChildrenInMenu`使用,用于隐藏菜单时,提供递归到父菜单显示 选中菜单项_(可参考 个人页 配置方式)_ | boolean | false |
| hiddenHeaderContent | *特殊 隐藏 [PageHeader](https://github.com/vueComponent/ant-design-vue-pro/blob/master/src/components/PageHeader/PageHeader.vue#L6) 组件中的页面带的 面包屑和页面标题栏 | boolean | false |
| permission | 与项目提供的权限拦截匹配的权限,如果不匹配,则会被禁止访问该路由页面 | array | [] |
> 路由自定义 `Icon` 请引入自定义 `svg` Icon 文件,然后传递给路由的 `meta.icon` 参数即可
路由构建例子方案1
路由例子
----
```ecmascript 6
const asyncRouterMap = [
{
path: '/',
name: 'index',
component: BasicLayout,
meta: { title: '首页' },
redirect: '/dashboard/analysis',
children: [
{
path: '/dashboard',
component: RouteView,
name: 'dashboard',
redirect: '/dashboard/workplace',
meta: {title: '仪表盘', icon: 'dashboard', permission: ['dashboard']},
children: [
{
path: '/dashboard/analysis',
name: 'Analysis',
component: () => import('@/views/dashboard/Analysis'),
meta: {title: '分析页', permission: ['dashboard']}
},
{
path: '/dashboard/monitor',
name: 'Monitor',
hidden: true,
component: () => import('@/views/dashboard/Monitor'),
meta: {title: '监控页', permission: ['dashboard']}
},
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/Workplace'),
meta: {title: '工作台', permission: ['dashboard']}
}
]
},
// result
{
path: '/result',
name: 'result',
component: PageView,
redirect: '/result/success',
meta: { title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },
children: [
{
path: '/result/success',
name: 'ResultSuccess',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
// 该页面隐藏面包屑和页面标题栏
meta: { title: '成功', hiddenHeaderContent: true, permission: [ 'result' ] }
},
{
path: '/result/fail',
name: 'ResultFail',
component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
// 该页面隐藏面包屑和页面标题栏
meta: { title: '失败', hiddenHeaderContent: true, permission: [ 'result' ] }
}
]
},
...
]
},
]
```
> 1. 请注意 `component: () => import('..') ` 方式引入路由的页面组件为 懒加载模式。具体可以看 [Vue 官方文档](https://router.vuejs.org/zh/guide/advanced/lazy-loading.html)
> 2. 增加新的路由应该增加在 '/' (index) 路由的 `children` 内
> 3. 子路由的父级路由必须有 `router-view` 才能让子路由渲染出来,请仔细查阅 vue-router 文档
> 4. `permission` 可以进行自定义修改,只需要对这个模块进行自定义修改即可 [src/store/modules/permission.js#L10](https://github.com/vueComponent/ant-design-vue-pro/blob/master/src/store/modules/permission.js#L10)
附权限路由结构:

第二种前端路由由后端动态生成的设计,可以前往官网文档 https://pro.antdv.com/docs/authority-management 参考
================================================
FILE: src/router/generator-routers.js
================================================
// eslint-disable-next-line
import * as loginService from '@/api/login'
// eslint-disable-next-line
import { BasicLayout, BlankLayout, PageView, RouteView } from '@/layouts'
// 前端路由表 (基于动态)
const constantRouterComponents = {
// 基础页面 layout 必须引入
BasicLayout: BasicLayout,
BlankLayout: BlankLayout,
RouteView: RouteView,
PageView: PageView,
'403': () => import(/* webpackChunkName: "error" */ '@/views/exception/403'),
'404': () => import(/* webpackChunkName: "error" */ '@/views/exception/404'),
'500': () => import(/* webpackChunkName: "error" */ '@/views/exception/500'),
// 你需要动态引入的页面组件
Workplace: () => import('@/views/dashboard/Workplace'),
Analysis: () => import('@/views/dashboard/Analysis'),
// form
BasicForm: () => import('@/views/form/basicForm'),
StepForm: () => import('@/views/form/stepForm/StepForm'),
AdvanceForm: () => import('@/views/form/advancedForm/AdvancedForm'),
// list
TableList: () => import('@/views/list/TableList'),
StandardList: () => import('@/views/list/BasicList'),
CardList: () => import('@/views/list/CardList'),
SearchLayout: () => import('@/views/list/search/SearchLayout'),
SearchArticles: () => import('@/views/list/search/Article'),
SearchProjects: () => import('@/views/list/search/Projects'),
SearchApplications: () => import('@/views/list/search/Applications'),
ProfileBasic: () => import('@/views/profile/basic'),
ProfileAdvanced: () => import('@/views/profile/advanced/Advanced'),
// result
ResultSuccess: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
ResultFail: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
// exception
Exception403: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
Exception404: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
Exception500: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
// account
AccountCenter: () => import('@/views/account/center'),
AccountSettings: () => import('@/views/account/settings/Index'),
BasicSetting: () => import('@/views/account/settings/BasicSetting'),
SecuritySettings: () => import('@/views/account/settings/Security'),
CustomSettings: () => import('@/views/account/settings/Custom'),
BindingSettings: () => import('@/views/account/settings/Binding'),
NotificationSettings: () => import('@/views/account/settings/Notification')
// 'TestWork': () => import(/* webpackChunkName: "TestWork" */ '@/views/dashboard/TestWork')
}
// 前端未找到页面路由(固定不用改)
const notFoundRouter = {
path: '*',
redirect: '/404',
hidden: true
}
// 根级菜单
const rootRouter = {
key: '',
name: 'index',
path: '',
component: 'BasicLayout',
redirect: '/dashboard',
meta: {
title: '首页'
},
children: []
}
// export const generatorStaticRouter = () => {
// }
/**
* 动态生成菜单
* @param token
* @returns {Promise}
*/
export const generatorDynamicRouter = token => {
return new Promise((resolve, reject) => {
loginService
.getCurrentUserNav(token)
.then(res => {
console.log('generatorDynamicRouter response:', res)
const { result } = res
const menuNav = []
const childrenNav = []
// 后端数据, 根级树数组, 根级 PID
listToTree(result, childrenNav, 0)
rootRouter.children = childrenNav
menuNav.push(rootRouter)
console.log('menuNav', menuNav)
const routers = generator(menuNav)
routers.push(notFoundRouter)
console.log('routers', routers)
resolve(routers)
})
.catch(err => {
reject(err)
})
})
}
/**
* 格式化树形结构数据 生成 vue-router 层级路由表
*
* @param routerMap
* @param parent
* @returns {*}
*/
export const generator = (routerMap, parent) => {
return routerMap.map(item => {
const { title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {}
const currentRouter = {
// 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
path: item.path || `${(parent && parent.path) || ''}/${item.key}`,
// 路由名称,建议唯一
name: item.name || item.key || '',
// 该路由对应页面的 组件 :方案1
// component: constantRouterComponents[item.component || item.key],
// 该路由对应页面的 组件 :方案2 (动态加载)
component: constantRouterComponents[item.component || item.key] || (() => import(`@/views/${item.component}`)),
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
meta: {
title: title,
icon: icon || undefined,
hiddenHeaderContent: hiddenHeaderContent,
target: target,
permission: item.name
}
}
// 是否设置了隐藏菜单
if (show === false) {
currentRouter.hidden = true
}
// 是否设置了隐藏子菜单
if (hideChildren) {
currentRouter.hideChildrenInMenu = true
}
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
if (!currentRouter.path.startsWith('http')) {
currentRouter.path = currentRouter.path.replace('//', '/')
}
// 重定向
item.redirect && (currentRouter.redirect = item.redirect)
// 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) {
// Recursion
currentRouter.children = generator(item.children, currentRouter)
}
return currentRouter
})
}
/**
* 数组转树形结构
* @param list 源数组
* @param tree 树
* @param parentId 父ID
*/
const listToTree = (list, tree, parentId) => {
list.forEach(item => {
// 判断是否为父级菜单
if (item.parentId === parentId) {
const child = {
...item,
key: item.key || item.name,
children: []
}
// 迭代 list, 找到当前菜单相符合的所有子菜单
listToTree(list, child.children, item.id)
// 删掉不存在 children 值的属性
if (child.children.length <= 0) {
delete child.children
}
// 加入到树中
tree.push(child)
}
})
}
================================================
FILE: src/router/index.js
================================================
import Vue from 'vue'
import Router from 'vue-router'
import { constantRouterMap } from '@/config/router.config'
// hack router push callback
const originalPush = Router.prototype.push
Router.prototype.push = function push (location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}
Vue.use(Router)
const createRouter = () =>
new Router({
mode: 'history',
routes: constantRouterMap
})
const router = createRouter()
// 定义一个resetRouter 方法,在退出登录后或token过期后 需要重新登录时,调用即可
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
================================================
FILE: src/store/app-mixin.js
================================================
import { mapState } from 'vuex'
const baseMixin = {
computed: {
...mapState({
layout: state => state.app.layout,
navTheme: state => state.app.theme,
primaryColor: state => state.app.color,
colorWeak: state => state.app.weak,
fixedHeader: state => state.app.fixedHeader,
fixedSidebar: state => state.app.fixedSidebar,
contentWidth: state => state.app.contentWidth,
autoHideHeader: state => state.app.autoHideHeader,
isMobile: state => state.app.isMobile,
sideCollapsed: state => state.app.sideCollapsed,
multiTab: state => state.app.multiTab
}),
isTopMenu () {
return this.layout === 'topmenu'
}
},
methods: {
isSideMenu () {
return !this.isTopMenu
}
}
}
export {
baseMixin
}
================================================
FILE: src/store/device-mixin.js
================================================
import { mapState } from 'vuex'
const deviceMixin = {
computed: {
...mapState({
isMobile: state => state.app.isMobile
})
}
}
export { deviceMixin }
================================================
FILE: src/store/getters.js
================================================
const getters = {
isMobile: state => state.app.isMobile,
lang: state => state.app.lang,
theme: state => state.app.theme,
color: state => state.app.color,
token: state => state.user.token,
avatar: state => state.user.avatar,
nickname: state => state.user.name,
welcome: state => state.user.welcome,
roles: state => state.user.roles,
userInfo: state => state.user.info,
addRouters: state => state.permission.addRouters,
multiTab: state => state.app.multiTab
}
export default getters
================================================
FILE: src/store/i18n-mixin.js
================================================
import { mapState } from 'vuex'
const i18nMixin = {
computed: {
...mapState({
currentLang: state => state.app.lang
})
},
methods: {
setLang (lang) {
this.$store.dispatch('setLang', lang)
}
}
}
export default i18nMixin
================================================
FILE: src/store/index.js
================================================
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import user from './modules/user'
// default router permission control
// 默认路由模式为静态路由 (router.config.js)
import permission from './modules/static-router'
// dynamic router permission control (Experimental)
// 动态路由模式(api请求后端生成)
// import permission from './modules/async-router'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app,
user,
permission
},
state: {},
mutations: {},
actions: {},
getters
})
================================================
FILE: src/store/modules/app.js
================================================
import storage from 'store'
import {
SIDEBAR_TYPE,
TOGGLE_MOBILE_TYPE,
TOGGLE_NAV_THEME,
TOGGLE_LAYOUT,
TOGGLE_FIXED_HEADER,
TOGGLE_FIXED_SIDEBAR,
TOGGLE_CONTENT_WIDTH,
TOGGLE_HIDE_HEADER,
TOGGLE_COLOR,
TOGGLE_WEAK,
TOGGLE_MULTI_TAB,
// i18n
APP_LANGUAGE
} from '@/store/mutation-types'
import { loadLanguageAsync } from '@/locales'
const app = {
state: {
sideCollapsed: false,
isMobile: false,
theme: 'dark',
layout: '',
contentWidth: '',
fixedHeader: false,
fixedSidebar: false,
autoHideHeader: false,
color: '',
weak: false,
multiTab: true,
lang: 'en-US',
_antLocale: {}
},
mutations: {
[SIDEBAR_TYPE]: (state, type) => {
state.sideCollapsed = type
storage.set(SIDEBAR_TYPE, type)
},
[TOGGLE_MOBILE_TYPE]: (state, isMobile) => {
state.isMobile = isMobile
},
[TOGGLE_NAV_THEME]: (state, theme) => {
state.theme = theme
storage.set(TOGGLE_NAV_THEME, theme)
},
[TOGGLE_LAYOUT]: (state, mode) => {
state.layout = mode
storage.set(TOGGLE_LAYOUT, mode)
},
[TOGGLE_FIXED_HEADER]: (state, mode) => {
state.fixedHeader = mode
storage.set(TOGGLE_FIXED_HEADER, mode)
},
[TOGGLE_FIXED_SIDEBAR]: (state, mode) => {
state.fixedSidebar = mode
storage.set(TOGGLE_FIXED_SIDEBAR, mode)
},
[TOGGLE_CONTENT_WIDTH]: (state, type) => {
state.contentWidth = type
storage.set(TOGGLE_CONTENT_WIDTH, type)
},
[TOGGLE_HIDE_HEADER]: (state, type) => {
state.autoHideHeader = type
storage.set(TOGGLE_HIDE_HEADER, type)
},
[TOGGLE_COLOR]: (state, color) => {
state.color = color
storage.set(TOGGLE_COLOR, color)
},
[TOGGLE_WEAK]: (state, mode) => {
state.weak = mode
storage.set(TOGGLE_WEAK, mode)
},
[APP_LANGUAGE]: (state, lang, antd = {}) => {
state.lang = lang
state._antLocale = antd
storage.set(APP_LANGUAGE, lang)
},
[TOGGLE_MULTI_TAB]: (state, bool) => {
storage.set(TOGGLE_MULTI_TAB, bool)
state.multiTab = bool
}
},
actions: {
setLang ({ commit }, lang) {
return new Promise((resolve, reject) => {
commit(APP_LANGUAGE, lang)
loadLanguageAsync(lang).then(() => {
resolve()
}).catch((e) => {
reject(e)
})
})
}
}
}
export default app
================================================
FILE: src/store/modules/async-router.js
================================================
/**
* 向后端请求用户的菜单,动态生成路由
*/
import { constantRouterMap } from '@/config/router.config'
import { generatorDynamicRouter } from '@/router/generator-routers'
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes ({ commit }, data) {
return new Promise((resolve, reject) => {
const { token } = data
generatorDynamicRouter(token).then(routers => {
commit('SET_ROUTERS', routers)
resolve()
}).catch(e => {
reject(e)
})
})
}
}
}
export default permission
================================================
FILE: src/store/modules/static-router.js
================================================
import { asyncRouterMap, constantRouterMap } from '@/config/router.config'
import cloneDeep from 'lodash.clonedeep'
/**
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
*
* @param permission
* @param route
* @returns {boolean}
*/
function hasPermission (permission, route) {
if (route.meta && route.meta.permission) {
console.log('hasPermission', permission)
if (permission === undefined) {
return false
}
let flag = false
for (let i = 0, len = permission.length; i < len; i++) {
flag = route.meta.permission.includes(permission[i])
if (flag) {
return true
}
}
return false
}
return true
}
/**
* 单账户多角色时,使用该方法可过滤角色不存在的菜单
*
* @param roles
* @param route
* @returns {*}
*/
// eslint-disable-next-line
function hasRole(roles, route) {
if (route.meta && route.meta.roles) {
return route.meta.roles.includes(roles.id)
} else {
return true
}
}
function filterAsyncRouter (routerMap, role) {
const accessedRouters = routerMap.filter(route => {
if (hasPermission(role.permissionList, route)) {
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, role)
}
return true
}
return false
})
return accessedRouters
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
const { role } = data
const routerMap = cloneDeep(asyncRouterMap)
const accessedRouters = filterAsyncRouter(routerMap, role)
commit('SET_ROUTERS', accessedRouters)
resolve()
})
}
}
}
export default permission
================================================
FILE: src/store/modules/user.js
================================================
import storage from 'store'
import expirePlugin from 'store/plugins/expire'
import { login, getInfo, logout } from '@/api/login'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { welcome } from '@/utils/util'
storage.addPlugin(expirePlugin)
const user = {
state: {
token: '',
name: '',
welcome: '',
avatar: '',
roles: [],
info: {}
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, { name, welcome }) => {
state.name = name
state.welcome = welcome
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
},
SET_INFO: (state, info) => {
state.info = info
}
},
actions: {
// 登录
Login ({ commit }, userInfo) {
return new Promise((resolve, reject) => {
login(userInfo).then(response => {
const result = response.result
storage.set(ACCESS_TOKEN, result.token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
commit('SET_TOKEN', result.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo ({ commit }) {
return new Promise((resolve, reject) => {
// 请求后端获取用户信息 /api/user/info
getInfo().then(response => {
const { result } = response
if (result.role && result.role.permissions.length > 0) {
const role = { ...result.role }
role.permissions = result.role.permissions.map(permission => {
const per = {
...permission,
actionList: (permission.actionEntitySet || {}).map(item => item.action)
}
return per
})
role.permissionList = role.permissions.map(permission => { return permission.permissionId })
// 覆盖响应体的 role, 供下游使用
result.role = role
commit('SET_ROLES', role)
commit('SET_INFO', result)
commit('SET_NAME', { name: result.name, welcome: welcome() })
commit('SET_AVATAR', result.avatar)
// 下游
resolve(result)
} else {
reject(new Error('getInfo: roles must be a non-null array !'))
}
}).catch(error => {
reject(error)
})
})
},
// 登出
Logout ({ commit, state }) {
return new Promise((resolve) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
storage.remove(ACCESS_TOKEN)
resolve()
}).catch((err) => {
console.log('logout fail:', err)
// resolve()
}).finally(() => {
})
})
}
}
}
export default user
================================================
FILE: src/store/mutation-types.js
================================================
export const ACCESS_TOKEN = 'Access-Token'
export const SIDEBAR_TYPE = 'sidebar_type'
export const TOGGLE_MOBILE_TYPE = 'is_mobile'
export const TOGGLE_NAV_THEME = 'nav_theme'
export const TOGGLE_LAYOUT = 'layout'
export const TOGGLE_FIXED_HEADER = 'fixed_header'
export const TOGGLE_FIXED_SIDEBAR = 'fixed_sidebar'
export const TOGGLE_CONTENT_WIDTH = 'content_width'
export const TOGGLE_HIDE_HEADER = 'auto_hide_header'
export const TOGGLE_COLOR = 'color'
export const TOGGLE_WEAK = 'weak'
export const TOGGLE_MULTI_TAB = 'multi_tab'
export const APP_LANGUAGE = 'app_language'
export const CONTENT_WIDTH_TYPE = {
Fluid: 'Fluid',
Fixed: 'Fixed'
}
export const NAV_THEME = {
LIGHT: 'light',
DARK: 'dark'
}
================================================
FILE: src/utils/axios.js
================================================
const VueAxios = {
vm: {},
// eslint-disable-next-line no-unused-vars
install (Vue, instance) {
if (this.installed) {
return
}
this.installed = true
if (!instance) {
// eslint-disable-next-line no-console
console.error('You have to install axios')
return
}
Vue.axios = instance
Object.defineProperties(Vue.prototype, {
axios: {
get: function get () {
return instance
}
},
$http: {
get: function get () {
return instance
}
}
})
}
}
export {
VueAxios
}
================================================
FILE: src/utils/domUtil.js
================================================
import config from '@/config/defaultSettings'
export const setDocumentTitle = function (title) {
document.title = title
const ua = navigator.userAgent
// eslint-disable-next-line
const regex = /\bMicroMessenger\/([\d\.]+)/
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe')
i.src = '/favicon.ico'
i.style.display = 'none'
i.onload = function () {
setTimeout(function () {
i.remove()
}, 9)
}
document.body.appendChild(i)
}
}
export const domTitle = config.title
================================================
FILE: src/utils/filter.js
================================================
import Vue from 'vue'
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
Vue.filter('NumberFormat', function (value) {
if (!value) {
return '0'
}
const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') // 将整数部分逢三一断
return intPartFormat
})
Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(dataStr).format(pattern)
})
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
return moment(dataStr).format(pattern)
})
================================================
FILE: src/utils/request.js
================================================
import axios from 'axios'
import store from '@/store'
import storage from 'store'
import notification from 'ant-design-vue/es/notification'
import { VueAxios } from './axios'
import { ACCESS_TOKEN } from '@/store/mutation-types'
// 创建 axios 实例
const request = axios.create({
// API 请求的默认前缀
baseURL: process.env.VUE_APP_API_BASE_URL,
timeout: 6000 // 请求超时时间
})
// 异常拦截处理器
const errorHandler = (error) => {
if (error.response) {
const data = error.response.data
// 从 localstorage 获取 token
const token = storage.get(ACCESS_TOKEN)
if (error.response.status === 403) {
notification.error({
message: 'Forbidden',
description: data.message
})
}
if (error.response.status === 401 && !(data.result && data.result.isLogin)) {
notification.error({
message: 'Unauthorized',
description: 'Authorization verification failed'
})
if (token) {
store.dispatch('Logout').then(() => {
setTimeout(() => {
window.location.reload()
}, 1500)
})
}
}
}
return Promise.reject(error)
}
// request interceptor
request.interceptors.request.use(config => {
const token = storage.get(ACCESS_TOKEN)
// 如果 token 存在
// 让每个请求携带自定义 token 请根据实际情况自行修改
if (token) {
config.headers[ACCESS_TOKEN] = token
}
return config
}, errorHandler)
// response interceptor
request.interceptors.response.use((response) => {
return response.data
}, errorHandler)
const installer = {
vm: {},
install (Vue) {
Vue.use(VueAxios, request)
}
}
export default request
export {
installer as VueAxios,
request as axios
}
================================================
FILE: src/utils/routeConvert.js
================================================
import cloneDeep from 'lodash.clonedeep'
export function convertRoutes (nodes) {
if (!nodes) return null
nodes = cloneDeep(nodes)
let queue = Array.isArray(nodes) ? nodes.concat() : [nodes]
while (queue.length) {
const levelSize = queue.length
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()
if (!node.children || !node.children.length) continue
node.children.forEach(child => {
// 转化相对路径
if (child.path[0] !== '/' && !child.path.startsWith('http')) {
child.path = node.path.replace(/(\w*)[/]*$/, `$1/${child.path}`)
}
})
queue = queue.concat(node.children)
}
}
return nodes
}
================================================
FILE: src/utils/screenLog.js
================================================
/* eslint-disable */
export const printANSI = () => {
// console.clear()
console.log('[antd pro] created()')
// ASCII - ANSI Shadow
let text = `
█████╗ ███╗ ██╗████████╗██████╗ ██████╗ ██████╗ ██████╗
██╔══██╗████╗ ██║╚══██╔══╝██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗
███████║██╔██╗ ██║ ██║ ██║ ██║ ██████╔╝██████╔╝██║ ██║
██╔══██║██║╚██╗██║ ██║ ██║ ██║ ██╔═══╝ ██╔══██╗██║ ██║
██║ ██║██║ ╚████║ ██║ ██████╔╝ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
\t\t\t\t\tPublished ${APP_VERSION}-${GIT_HASH} @ antdv.com
\t\t\t\t\tBuild date: ${BUILD_DATE}`
console.log(`%c${text}`, 'color: #fc4d50')
console.log('%c感谢使用 antd pro!', 'color: #000; font-size: 14px; font-family: Hiragino Sans GB,Microsoft YaHei,\\\\5FAE\\8F6F\\96C5\\9ED1,Droid Sans Fallback,Source Sans,Wenquanyi Micro Hei,WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei,Apple LiGothic Medium,SimHei,ST Heiti,WenQuanYi Zen Hei Sharp,sans-serif;')
console.log('%cThanks for using antd pro!', 'color: #fff; font-size: 14px; font-weight: 300; text-shadow:#000 1px 0 0,#000 0 1px 0,#000 -1px 0 0,#000 0 -1px 0;')
console.log('')
console.log('%c默认使用的路由初始化模式可能是 静态路由 / 动态路由, 请前往 src/store/index.js 确认 import permission from 哪一个文件.', 'color: #000; font-size: 14px; font-family: Hiragino Sans GB,Microsoft YaHei,\\\\5FAE\\8F6F\\96C5\\9ED1,Droid Sans Fallback,Source Sans,Wenquanyi Micro Hei,WenQuanYi Micro Hei Mono,WenQuanYi Zen Hei,Apple LiGothic Medium,SimHei,ST Heiti,WenQuanYi Zen Hei Sharp,sans-serif;')
console.log('')
}
================================================
FILE: src/utils/util.js
================================================
export function timeFix () {
const time = new Date()
const hour = time.getHours()
return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好'
}
export function welcome () {
const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
const index = Math.floor(Math.random() * arr.length)
return arr[index]
}
/**
* 触发 window.resize
*/
export function triggerWindowResizeEvent () {
const event = document.createEvent('HTMLEvents')
event.initEvent('resize', true, true)
event.eventType = 'message'
window.dispatchEvent(event)
}
export function handleScrollHeader (callback) {
let timer = 0
let beforeScrollTop = window.pageYOffset
callback = callback || function () {}
window.addEventListener(
'scroll',
event => {
clearTimeout(timer)
timer = setTimeout(() => {
let direction = 'up'
const afterScrollTop = window.pageYOffset
const delta = afterScrollTop - beforeScrollTop
if (delta === 0) {
return false
}
direction = delta > 0 ? 'down' : 'up'
callback(direction)
beforeScrollTop = afterScrollTop
}, 50)
},
false
)
}
export function isIE () {
const bw = window.navigator.userAgent
const compare = (s) => bw.indexOf(s) >= 0
const ie11 = (() => 'ActiveXObject' in window)()
return compare('MSIE') || ie11
}
/**
* Remove loading animate
* @param id parent element id or class
* @param timeout
*/
export function removeLoadingAnimate (id = '', timeout = 1500) {
if (id === '') {
return
}
setTimeout(() => {
document.body.removeChild(document.getElementById(id))
}, timeout)
}
export function scorePassword (pass) {
let score = 0
if (!pass) {
return score
}
// award every unique letter until 5 repetitions
const letters = {}
for (let i = 0; i < pass.length; i++) {
letters[pass[i]] = (letters[pass[i]] || 0) + 1
score += 5.0 / letters[pass[i]]
}
// bonus points for mixing it up
const variations = {
digits: /\d/.test(pass),
lower: /[a-z]/.test(pass),
upper: /[A-Z]/.test(pass),
nonWords: /\W/.test(pass)
}
let variationCount = 0
for (var check in variations) {
variationCount += (variations[check] === true) ? 1 : 0
}
score += (variationCount - 1) * 10
return parseInt(score)
}
================================================
FILE: src/utils/utils.less
================================================
.textOverflow() {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
.textOverflowMulti(@line: 3, @bg: #fff) {
position: relative;
max-height: @line * 1.5em;
padding-right: 1em;
margin-right: -1em;
overflow: hidden;
line-height: 1.5em;
text-align: justify;
&::before {
position: absolute;
right: 14px;
bottom: 0;
padding: 0 1px;
background: @bg;
content: '...';
}
&::after {
position: absolute;
right: 14px;
width: 1em;
height: 1em;
margin-top: .2em;
background: white;
content: '';
}
}
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
height: 0;
clear: both;
font-size: 0;
visibility: hidden;
}
}
================================================
FILE: src/views/404.vue
================================================
404 page
================================================
FILE: src/views/account/center/index.vue
================================================
交互专家
蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED
浙江省
杭州市
handleTabChange(key, 'noTitleKey')"
>
================================================
FILE: src/views/account/center/page/App.vue
================================================
================================================
FILE: src/views/account/center/page/Article.vue
================================================
{{ item.title }}
Ant Design
设计语言
蚂蚁金服
================================================
FILE: src/views/account/center/page/Project.vue
================================================
{{ item.description }}
{{ item.updatedAt | fromNow }}
================================================
FILE: src/views/account/center/page/index.js
================================================
import AppPage from './App'
import ArticlePage from './Article'
import ProjectPage from './Project'
export { AppPage, ArticlePage, ProjectPage }
================================================
FILE: src/views/account/settings/AvatarModal.vue
================================================
选择图片
保存
================================================
FILE: src/views/account/settings/BasicSetting.vue
================================================
{{ $t('account.settings.basic.update') }}
================================================
FILE: src/views/account/settings/Binding.vue
================================================
================================================
FILE: src/views/account/settings/Custom.vue
================================================
风格配色
整体风格配色设置
主题色
页面风格配色: {{ colorFilter(primaryColor) }}
================================================
FILE: src/views/account/settings/Index.vue
================================================
================================================
FILE: src/views/account/settings/Notification.vue
================================================
================================================
FILE: src/views/account/settings/Security.vue
================================================
{{ item.title }}
{{ item.description }}
:
{{ item.value }}
{{ item.actions.title }}
================================================
FILE: src/views/dashboard/Analysis.vue
================================================
{{ $t('dashboard.analysis.week') }}
12%
{{ $t('dashboard.analysis.day') }}
11%
{{ $t('dashboard.analysis.day-sales') }}¥ 234.56
{{ $t('dashboard.analysis.day-visits') }} {{ '1234' | NumberFormat }}
{{ $t('dashboard.analysis.conversion-rate') }} 60%
{{ $t('dashboard.analysis.week') }}
12%
{{ $t('dashboard.analysis.day') }}
80%
{{ $t('dashboard.analysis.dropdown-option-one') }}
{{ $t('dashboard.analysis.dropdown-option-two') }}
{{ $t('dashboard.analysis.search-users') }}
{{ $t('dashboard.analysis.per-capita-search') }}
{{ $t('dashboard.analysis.sales') }}
================================================
FILE: src/views/dashboard/Monitor.vue
================================================
Monitor
================================================
FILE: src/views/dashboard/TestWork.vue
================================================
本页面内容均为测试功能,暂不提供稳定性保证
多标签组件测试功能
关闭当前页
打开 任务列表
visible = !visible" style="margin-right: 16px;">修改当前 Tab 名称
visible2 = false" okText="确定" cancelText="取消">
页面 KEY 是由页面的路由 path 决定的
如果要修改某一个页面标题,该页面必须已经被打开在 Tab 栏
后期可以考虑优化到编程式 Tab 栏,就可以没有这种限制
visible2 = !visible2">修改某一个 Tab 名称
全局遮罩测试
打开遮罩(5s 自动关闭)
打开遮罩(自定义提示语)
================================================
FILE: src/views/dashboard/Workplace.less
================================================
@import '~ant-design-vue/es/style/themes/default.less';
.text-overflow() {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
height: 0;
clear: both;
font-size: 0;
visibility: hidden;
}
}
.page-header-content {
display: flex;
.avatar {
flex: 0 1 72px;
& > span {
display: block;
width: 72px;
height: 72px;
border-radius: 72px;
}
}
.content {
position: relative;
top: 4px;
margin-left: 24px;
line-height: 22px;
color: @text-color-secondary;
flex: 1 1 auto;
.content-title {
margin-bottom: 12px;
font-size: 20px;
font-weight: 500;
line-height: 28px;
color: @heading-color;
}
}
}
.extra-content {
.clearfix();
float: right;
white-space: nowrap;
.stat-item {
position: relative;
display: inline-block;
padding: 0 32px;
> p:first-child {
margin-bottom: 4px;
font-size: @font-size-base;
line-height: 22px;
color: @text-color-secondary;
}
> p {
margin: 0;
font-size: 30px;
line-height: 38px;
color: @heading-color;
> span {
font-size: 20px;
color: @text-color-secondary;
}
}
&::after {
position: absolute;
top: 8px;
right: 0;
width: 1px;
height: 40px;
background-color: @border-color-split;
content: '';
}
&:last-child {
padding-right: 0;
&::after {
display: none;
}
}
}
}
================================================
FILE: src/views/dashboard/Workplace.vue
================================================
================================================
FILE: src/views/exception/403.vue
================================================
Back Home
================================================
FILE: src/views/exception/404.vue
================================================
Back Home
================================================
FILE: src/views/exception/500.vue
================================================
Back Home
================================================
FILE: src/views/form/advancedForm/AdvancedForm.vue
================================================
handleChange(e.target.value, record.key, col)"
/>
{{ text }}
添加
删除
保存
取消
编辑
删除
新增成员
{{ item.message }}
{{ item.fieldLabel }}
{{ errors.length }}
提交
================================================
FILE: src/views/form/advancedForm/RepositoryForm.vue
================================================
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
================================================
FILE: src/views/form/advancedForm/TaskForm.vue
================================================
黄丽丽
李大刀
王伟
李红军
定时执行
周期执行
Submit
================================================
FILE: src/views/form/basicForm/index.vue
================================================
%
{{ $t('form.basic-form.radio.public') }}
{{ $t('form.basic-form.radio.partially-public') }}
{{ $t('form.basic-form.radio.private') }}
{{ $t('form.basic-form.option.A') }}
{{ $t('form.basic-form.option.B') }}
{{ $t('form.basic-form.option.C') }}
{{ $t('form.basic-form.form.submit') }}
{{ $t('form.basic-form.form.save') }}
================================================
FILE: src/views/form/stepForm/Step1.vue
================================================
ant-design@alipay.com
支付宝
微信
下一步
================================================
FILE: src/views/form/stepForm/Step2.vue
================================================
ant-design@alipay.com
test@example.com
Alex
¥ 5,000.00
提交
上一步
================================================
FILE: src/views/form/stepForm/Step3.vue
================================================
================================================
FILE: src/views/form/stepForm/StepForm.vue
================================================
将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。
================================================
FILE: src/views/list/BasicList.vue
================================================
{{ item.title }}
================================================
FILE: src/views/list/CardList.vue
================================================
新增产品
{{ item.title }}
{{ item.content }}
操作一
操作二
================================================
FILE: src/views/list/QueryList.vue
================================================
================================================
FILE: src/views/list/TableList.vue
================================================
全部
关闭
运行中
全部
关闭
运行中
全部
关闭
运行中
查询
this.queryParam = {}">重置
{{ advanced ? '收起' : '展开' }}
{{ index + 1 }}
{{ text }}
配置
订阅报警
================================================
FILE: src/views/list/components/Info.vue
================================================
================================================
FILE: src/views/list/modules/CreateForm.vue
================================================
{ $emit('ok') }"
@cancel="() => { $emit('cancel') }"
>
================================================
FILE: src/views/list/modules/StepByStepModal.vue
================================================
上一步
取消
{{ currentStep === 2 && '完成' || '下一步' }}
================================================
FILE: src/views/list/modules/TaskForm.vue
================================================
付晓晓
周毛毛
================================================
FILE: src/views/list/search/Applications.vue
================================================
类目一
类目二
类目三
类目四
类目五
类目六
类目七
类目八
类目九
类目十
王昭君
优秀
普通
================================================
FILE: src/views/list/search/Article.vue
================================================
类目一
类目二
类目三
类目四
类目五
类目六
类目七
类目八
类目九
类目十
{{ item.name }}
只看自己的
李三
优秀
{{ item.title }}
Ant Design
设计语言
蚂蚁金服
================================================
FILE: src/views/list/search/Projects.vue
================================================
类目一
类目二
类目三
类目四
类目五
类目六
类目七
类目八
类目九
类目十
王昭君
优秀
普通
{{ item.description }}
{{ item.updatedAt | fromNow }}
================================================
FILE: src/views/list/search/SearchLayout.vue
================================================
================================================
FILE: src/views/list/search/components/CardInfo.vue
================================================
================================================
FILE: src/views/list/search/components/IconText.vue
================================================
{{ text }}
================================================
FILE: src/views/list/table/Edit.vue
================================================
Option 1
Option 2
Option 3
提交
返回
================================================
FILE: src/views/list/table/List.vue
================================================
================================================
FILE: src/views/other/BigForm.vue
================================================
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
王同学
李同学
黄同学
王晓丽
李军
公开
私密
Submit
handleChange(e.target.value, record.key, col)"
/>
{{ text }}
添加
删除
保存
取消
编辑
删除
新增成员
================================================
FILE: src/views/other/IconSelectorView.vue
================================================
测试 IconSelector 组件 v-model 功能
改变 Icon-down
改变 Icon-cloud-download
================================================
FILE: src/views/other/PermissionList.vue
================================================
{{ action.describe }}
{{ text | statusFilter }}
编辑
更多
详情
禁用
删除
正常
禁用
{{ action.label }}
================================================
FILE: src/views/other/RoleList.vue
================================================
{{ role.permissionName }}:
{{ action.describe }}
-
编辑
更多
详情
禁用
删除
================================================
FILE: src/views/other/TableInnerEditList.vue
================================================
全部
关闭
运行中
全部
关闭
运行中
全部
关闭
运行中
查询
重置
{{ advanced ? '收起' : '展开' }}
handleChange(e.target.value, record.key, col, record)"
/>
{{ text }}
================================================
FILE: src/views/other/TreeList.vue
================================================
编辑
更多
详情
禁用
删除
================================================
FILE: src/views/other/UserList.vue
================================================
{{ role.permissionName }}:
{{ action | permissionFilter }}
-
{{ text | statusFilter }}
{{ text | moment }}
编辑
更多
详情
禁用
删除
正常
禁用
拥有权限
全选
================================================
FILE: src/views/other/modules/OrgModal.vue
================================================
================================================
FILE: src/views/other/modules/RoleModal.vue
================================================
Finished
This is a description.
================================================
FILE: src/views/profile/advanced/Advanced.vue
================================================
曲丽丽
XX 服务
2017-07-07
12421
2017-07-07 ~ 2017-08-08
请于两个工作日内确认
操作
操作
主操作
状态
待审批
订单金额
¥ 568.08
创建项目
部门初审
付晓晓
32943898021309809423
3321944288191034921
18112345678
浙江省杭州市西湖区黄姑山路工专路交叉路口
725
2018-08-08
725
2018-08-08
林东东
1234567
XX公司-YY部
2018-08-08
这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...
Citrullus lanatus (Thunb.) Matsum. et Nakai一年生蔓生藤本;茎、枝粗壮,具明显的棱。卷须较粗..
付小小
1234567
{this.operationActiveTabKey = key}"
>
================================================
FILE: src/views/profile/basic/index.vue
================================================
1000000000
已取货
1234123421
3214321432
付小小
18100000000
菜鸟仓储
浙江省杭州市西湖区万塘路18号
无
退货商品
退货进度
================================================
FILE: src/views/result/Error.vue
================================================
{{ $t('result.fail.error.btn-text') }}
{{ $t('result.fail.error.hint-title') }}
================================================
FILE: src/views/result/Success.vue
================================================
{{ $t('result.success.btn-return') }}
{{ $t('result.success.btn-project') }}
{{ $t('result.success.btn-print') }}
{{ $t('result.success.operate-title') }}
{{ $t('result.success.operate-id') }}:
20180724089
{{ $t('result.success.principal') }}:
曲丽丽是谁?
{{ $t('result.success.operate-time') }}:
2016-12-12 ~ 2017-12-12
{{ $t('result.success.step1-title') }}
{{ $t('result.success.step2-title') }}
{{ $t('result.success.step3-title') }}
{{ $t('result.success.step4-title') }}
================================================
FILE: src/views/user/Login.vue
================================================
{{ $t('user.login.remember-me') }}
{{ $t('user.login.forgot-password') }}
{{ $t('user.login.login') }}
{{ $t('user.login.sign-in-with') }}
{{ $t('user.login.signup') }}
================================================
FILE: src/views/user/Register.vue
================================================
{{ $t('user.register.register') }}
{{ $t(passwordLevelName) }}
{{ $t('user.register.password.popover-message') }}
+86
+87
{{ $t('user.register.register') }}
{{ $t('user.register.sign-in') }}
================================================
FILE: src/views/user/RegisterResult.vue
================================================
查看邮箱
返回首页
================================================
FILE: tests/unit/.eslintrc.js
================================================
module.exports = {
env: {
jest: true
}
}
================================================
FILE: vue.config.js
================================================
const path = require('path')
const webpack = require('webpack')
const packageJson = require('./package.json')
const GitRevisionPlugin = require('git-revision-webpack-plugin')
const GitRevision = new GitRevisionPlugin()
const buildDate = JSON.stringify(new Date().toLocaleString())
const createThemeColorReplacerPlugin = require('./config/plugin.config')
function resolve (dir) {
return path.join(__dirname, dir)
}
// check Git
function getGitHash () {
try {
return GitRevision.version()
} catch (e) {}
return 'unknown'
}
// eslint-disable-next-line no-unused-vars
const isProd = process.env.NODE_ENV === 'production'
// eslint-disable-next-line no-unused-vars
const assetsCDN = {
// webpack build externals
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
},
css: [],
// https://unpkg.com/browse/vue@2.6.10/
js: [
'//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js',
'//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js',
'//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js'
]
}
// vue.config.js
const vueConfig = {
configureWebpack: {
// webpack plugins
plugins: [
// Ignore all locale files of moment.js - disabled for webpack 5 compatibility
// new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
APP_VERSION: `"${packageJson.version}"`,
GIT_HASH: JSON.stringify(getGitHash()),
BUILD_DATE: buildDate
})
]
// en_US: `if prod, add externals`
// zh_CN: `这里是用来控制编译忽略外部依赖的,与 config.plugin('html') 配合可以编译时引入外部CDN文件依赖`
// externals: isProd ? assetsCDN.externals : {}
},
chainWebpack: config => {
config.resolve.alias.set('@$', resolve('src'))
// Ignore moment.js locale files to reduce bundle size (webpack 5 compatible)
config.resolve.alias.set('moment/locale', false)
// fixed svg-loader by https://github.com/damianstasik/vue-svg-loader/issues/185#issuecomment-1126721069
const svgRule = config.module.rule('svg')
// Remove regular svg config from root rules list
config.module.rules.delete('svg')
config.module.rule('svg')
// Use svg component rule
.oneOf('svg_as_component')
.resourceQuery(/inline/)
.test(/\.(svg)(\?.*)?$/)
.use('babel-loader')
.loader('babel-loader')
.end()
.use('vue-svg-loader')
.loader('vue-svg-loader')
.options({
svgo: {
plugins: [
{ prefixIds: true },
{ cleanupIDs: true },
{ convertShapeToPath: false },
{ convertStyleToAttrs: true }
]
}
})
.end()
.end()
// Otherwise use original svg rule
.oneOf('svg_as_regular')
.merge(svgRule.toConfig())
.end()
// en_US: If prod is on assets require on cdn
// zh_CN: 如果是 prod 模式,则引入 CDN 依赖文件,有需要减少包大小请自行解除依赖
//
// if (isProd) {
// config.plugin('html').tap(args => {
// args[0].cdn = assetsCDN
// return args
// })
// }
},
css: {
loaderOptions: {
less: {
modifyVars: {
// less vars,customize ant design theme
// 'primary-color': '#F5222D',
// 'link-color': '#F5222D',
'border-radius-base': '2px'
},
// DO NOT REMOVE THIS LINE
javascriptEnabled: true
}
}
},
devServer: {
// development server port 8000
port: 8000,
// If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
// proxy: {
// '/api': {
// target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
// ws: false,
// changeOrigin: true
// }
// }
},
// disable source map in production
productionSourceMap: false,
lintOnSave: undefined,
// babel-loader no-ignore node_modules/*
transpileDependencies: []
}
// preview.pro.loacg.com only do not use in your production;
if (process.env.VUE_APP_PREVIEW === 'true') {
// add `ThemeColorReplacer` plugin to webpack plugins
vueConfig.configureWebpack.plugins.push(createThemeColorReplacerPlugin())
}
module.exports = vueConfig
================================================
FILE: wrangler.jsonc
================================================
{
"name": "antdv-vue2-pro-preview",
"compatibility_date": "2025-09-19",
"assets": {
"directory": "./dist",
"not_found_handling": "single-page-application"
},
"routes": [
{
"pattern": "preview.pro.antdv.com/*",
"zone_name": "antdv.com"
}
]
}