master 616df9e063a4 cached
255 files
566.6 KB
165.3k tokens
119 symbols
1 requests
Download .txt
Showing preview only (624K chars total). Download the full file or copy to clipboard to get everything.
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)

<h1 align="center">Ant Design Vue Pro for Vue 2</h1>
<div align="center">
An out-of-box UI solution for enterprise applications as a Vue boilerplate. based on  <a href="https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/" target="_blank">Ant Design of Vue</a>
</div>

<div align="center">
  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)
</div>

<div align="center">

[![License](https://img.shields.io/npm/l/package.json.svg?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/blob/master/LICENSE)
[![Release](https://img.shields.io/github/release/vueComponent/ant-design-vue-pro.svg?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[![Support Vue Version](https://img.shields.io/badge/Support-Vue2-green?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[![Travis branch](https://travis-ci.org/vueComponent/ant-design-vue-pro.svg?branch=master)](https://travis-ci.org/vueComponent/ant-design-vue-pro)

</div>

- 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

![dashboard](https://static-2.loacg.com/open/static/github/SP1.png)

### 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.

| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>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.
<a href="https://github.com/vueComponent/ant-design-vue-pro/graphs/contributors"><img src="https://opencollective.com/ant-design-pro-vue/contributors.svg?width=890&button=false" /></a>


================================================
FILE: README.zh-CN.md
================================================
[English](./README.md) | 简体中文

<h1 align="center">Ant Design Vue Pro for Vue 2</h1>
<div align="center">
An out-of-box UI solution for enterprise applications as a Vue boilerplate. based on  <a href="https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/" target="_blank">Ant Design of Vue</a>
</div>

<div align="center">
  如果需要 Vue 3 版本,请访问 [Ant Design Vue Pro for Vue 3](https://store.antdv.com/items/admin-pro)
</div>

<div align="center">

[![License](https://img.shields.io/npm/l/package.json.svg?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/blob/master/LICENSE)
[![Release](https://img.shields.io/github/release/vueComponent/ant-design-vue-pro.svg?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[![Support Vue Version](https://img.shields.io/badge/Support-Vue2-green?style=flat)](https://github.com/vueComponent/ant-design-vue-pro/releases/latest)
[![Travis branch](https://travis-ci.org/vueComponent/ant-design-vue-pro.svg?branch=master)](https://travis-ci.org/vueComponent/ant-design-vue-pro)

</div>

- 预览: 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/)

![dashboard](https://static-2.loacg.com/open/static/github/SP1.png)

## 环境和依赖

- 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.

| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>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.
<a href="https://github.com/vueComponent/ant-design-vue-pro/graphs/contributors"><img src="https://opencollective.com/ant-design-pro-vue/contributors.svg?width=890&button=false" /></a>


================================================
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` 文件的 `<head></head>` 最好写成内联 `<style>动画样式</style>` 



----

目前提供有两个样式,均在 `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: {
    '^@/(.*)$': '<rootDir>/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
================================================
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>logo.png">
    <title>Ant Design Pro</title>
    <style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
    <!-- require cdn assets css -->
    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
    <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
    <% } %>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
      <div class="first-loading-wrp">
        <h1>Pro</h1>
        <div class="loading-wrp">
          <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
        </div>
        <div style="display: flex; justify-content: center; align-items: center;">Ant Design</div>
      </div>
    </div>
    <!-- require cdn assets js -->
    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
    <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
    <% } %>
    <!-- built files will be auto injected -->
  </body>
</html>


================================================
FILE: src/App.vue
================================================
<template>
  <a-config-provider :locale="locale">
    <div id="app">
      <router-view/>
    </div>
  </a-config-provider>
</template>

<script>
import { domTitle, setDocumentTitle } from '@/utils/domUtil'
import { i18nRender } from '@/locales'

export default {
  data () {
    return {
    }
  },
  computed: {
    locale () {
      // 只是为了切换语言时,更新标题
      const { title } = this.$route.meta
      title && (setDocumentTitle(`${i18nRender(title)} - ${domTitle}`))

      return this.$i18n.getLocaleMessage(this.$store.getters.lang).antLocale
    }
  }
}
</script>


================================================
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
================================================
<template>
  <div class="antd-pro-components-article-list-content-index-listContent">
    <div class="description">
      <slot>
        {{ description }}
      </slot>
    </div>
    <div class="extra">
      <a-avatar :src="avatar" size="small" />
      <a :href="href">{{ owner }}</a> 发布在 <a :href="href">{{ href }}</a>
      <em>{{ updateAt | moment }}</em>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ArticleListContent',
  props: {
    prefixCls: {
      type: String,
      default: 'antd-pro-components-article-list-content-index-listContent'
    },
    description: {
      type: String,
      default: ''
    },
    owner: {
      type: String,
      required: true
    },
    avatar: {
      type: String,
      required: true
    },
    href: {
      type: String,
      required: true
    },
    updateAt: {
      type: String,
      required: true
    }
  }
}
</script>

<style lang="less" scoped>
@import '../index.less';

.antd-pro-components-article-list-content-index-listContent {
  .description {
    max-width: 720px;
    line-height: 22px;
  }
  .extra {
    margin-top: 16px;
    color: @text-color-secondary;
    line-height: 22px;

    & :deep(.ant-avatar) {
      position: relative;
      top: 1px;
      width: 20px;
      height: 20px;
      margin-right: 8px;
      vertical-align: top;
    }

    & > em {
      margin-left: 16px;
      color: @disabled-color;
      font-style: normal;
    }
  }
}

@media screen and (max-width: @screen-xs) {
  .antd-pro-components-article-list-content-index-listContent {
    .extra {
      & > em {
        display: block;
        margin-top: 8px;
        margin-left: 0;
      }
    }
  }
}
</style>


================================================
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 = <Avatar size={size || 'small'} src={this.src} />
    return (this.tips && <Tooltip title={this.tips}>{AvatarDom}</Tooltip>) || <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 ? <ul class={`${prefixCls}-items`}>{this.getItems(items)}</ul> : null
    return (
      <div class={className}>
        {itemsDom}
      </div>
    )
  },
  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((<Avatar size={this.size === 'mini' ? 'small' : this.size} style={this.excessItemsStyle}>{`+${totalSize - this.maxLength}`}</Avatar>))
      }
      return items.map((item) => (
        <li class={className}>{item}</li>
      ))
    }
  }
}

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
<avatar-list size="mini">
    <avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
    <avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```
或
```html
<avatar-list :max-length="3">
    <avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
    <avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
    <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```



## API

### AvatarList

| 参数               | 说明       | 类型                                 | 默认值       |
| ---------------- | -------- | ---------------------------------- | --------- |
| size             | 头像大小     | `large`、`small` 、`mini`, `default` | `default` |
| maxLength        | 要显示的最大项目 | number                             | -         |
| excessItemsStyle | 多余的项目风格  | CSSProperties                      | -         |

### AvatarList.Item

| 参数   | 说明     | 类型        | 默认值 |
| ---- | ------ | --------- | --- |
| tips | 头像展示文案 | string | -   |
| src  | 头像图片连接 | string    | -   |



================================================
FILE: src/components/Charts/Bar.vue
================================================
<template>
  <div :style="{ padding: '0 0 32px 32px' }">
    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
    <v-chart
      height="254"
      :data="data"
      :forceFit="true"
      :padding="['auto', 'auto', '40', '50']">
      <v-tooltip />
      <v-axis />
      <v-bar position="x*y"/>
    </v-chart>
  </div>
</template>

<script>
export default {
  name: 'Bar',
  props: {
    title: {
      type: String,
      default: ''
    },
    data: {
      type: Array,
      default: () => {
        return []
      }
    },
    scale: {
      type: Array,
      default: () => {
        return [{
          dataKey: 'x',
          min: 2
        }, {
          dataKey: 'y',
          title: '时间',
          min: 1,
          max: 22
        }]
      }
    },
    tooltip: {
      type: Array,
      default: () => {
        return [
          'x*y',
          (x, y) => ({
            name: x,
            value: y
          })
        ]
      }
    }
  },
  data () {
    return {
    }
  }
}
</script>


================================================
FILE: src/components/Charts/ChartCard.vue
================================================
<template>
  <a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
    <div class="chart-card-header">
      <div class="meta">
        <span class="chart-card-title">
          <slot name="title">
            {{ title }}
          </slot>
        </span>
        <span class="chart-card-action">
          <slot name="action"></slot>
        </span>
      </div>
      <div class="total">
        <slot name="total">
          <span>{{ typeof total === 'function' && total() || total }}</span>
        </slot>
      </div>
    </div>
    <div class="chart-card-content">
      <div class="content-fix">
        <slot></slot>
      </div>
    </div>
    <div class="chart-card-footer">
      <div class="field">
        <slot name="footer"></slot>
      </div>
    </div>
  </a-card>
</template>

<script>
export default {
  name: 'ChartCard',
  props: {
    title: {
      type: String,
      default: ''
    },
    total: {
      type: [Function, Number, String],
      required: false,
      default: null
    },
    loading: {
      type: Boolean,
      default: false
    }
  }
}
</script>

<style lang="less" scoped>
  .chart-card-header {
    position: relative;
    overflow: hidden;
    width: 100%;

    .meta {
      position: relative;
      overflow: hidden;
      width: 100%;
      color: rgba(0, 0, 0, .45);
      font-size: 14px;
      line-height: 22px;
    }
  }

  .chart-card-action {
    cursor: pointer;
    position: absolute;
    top: 0;
    right: 0;
  }

  .chart-card-footer {
    border-top: 1px solid #e8e8e8;
    padding-top: 9px;
    margin-top: 8px;

    > * {
      position: relative;
    }

    .field {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      margin: 0;
    }
  }

  .chart-card-content {
    margin-bottom: 12px;
    position: relative;
    height: 46px;
    width: 100%;

    .content-fix {
      position: absolute;
      left: 0;
      bottom: 0;
      width: 100%;
    }
  }

  .total {
    overflow: hidden;
    text-overflow: ellipsis;
    word-break: break-all;
    white-space: nowrap;
    color: #000;
    margin-top: 4px;
    margin-bottom: 0;
    font-size: 30px;
    line-height: 38px;
    height: 38px;
  }
</style>


================================================
FILE: src/components/Charts/Liquid.vue
================================================
<template>
  <div>
    <v-chart
      :forceFit="true"
      :height="height"
      :width="width"
      :data="data"
      :scale="scale"
      :padding="0">
      <v-tooltip />
      <v-interval
        :shape="['liquid-fill-gauge']"
        position="transfer*value"
        color=""
        :v-style="{
          lineWidth: 10,
          opacity: 0.75
        }"
        :tooltip="[
          'transfer*value',
          (transfer, value) => {
            return {
              name: transfer,
              value,
            };
          },
        ]"
      ></v-interval>
      <v-guide
        v-for="(row, index) in data"
        :key="index"
        type="text"
        :top="true"
        :position="{
          gender: row.transfer,
          value: 45
        }"
        :content="row.value + '%'"
        :v-style="{
          fontSize: 100,
          textAlign: 'center',
          opacity: 0.75,
        }"
      />
    </v-chart>
  </div>
</template>

<script>
export default {
  name: 'Liquid',
  props: {
    height: {
      type: Number,
      default: 0
    },
    width: {
      type: Number,
      default: 0
    }
  }
}
</script>

<style scoped>

</style>


================================================
FILE: src/components/Charts/MiniArea.vue
================================================
<template>
  <div class="antv-chart-mini">
    <div class="chart-wrapper" :style="{ height: 46 }">
      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
        <v-tooltip />
        <v-smooth-area position="x*y" />
      </v-chart>
    </div>
  </div>
</template>

<script>
import moment from 'moment'
const data = []
const beginDay = new Date().getTime()

for (let i = 0; i < 10; i++) {
  data.push({
    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
    y: Math.round(Math.random() * 10)
  })
}

const tooltip = [
  'x*y',
  (x, y) => ({
    name: x,
    value: y
  })
]
const scale = [{
  dataKey: 'x',
  min: 2
}, {
  dataKey: 'y',
  title: '时间',
  min: 1,
  max: 22
}]

export default {
  name: 'MiniArea',
  data () {
    return {
      data,
      tooltip,
      scale,
      height: 100
    }
  }
}
</script>

<style lang="less" scoped>
  @import "chart";
</style>


================================================
FILE: src/components/Charts/MiniBar.vue
================================================
<template>
  <div class="antv-chart-mini">
    <div class="chart-wrapper" :style="{ height: 46 }">
      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
        <v-tooltip />
        <v-bar position="x*y" />
      </v-chart>
    </div>
  </div>
</template>

<script>
import moment from 'moment'
const data = []
const beginDay = new Date().getTime()

for (let i = 0; i < 10; i++) {
  data.push({
    x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
    y: Math.round(Math.random() * 10)
  })
}

const tooltip = [
  'x*y',
  (x, y) => ({
    name: x,
    value: y
  })
]

const scale = [{
  dataKey: 'x',
  min: 2
}, {
  dataKey: 'y',
  title: '时间',
  min: 1,
  max: 30
}]

export default {
  name: 'MiniBar',
  data () {
    return {
      data,
      tooltip,
      scale,
      height: 100
    }
  }
}
</script>

<style lang="less" scoped>
  @import "chart";
</style>


================================================
FILE: src/components/Charts/MiniProgress.vue
================================================
<template>
  <div class="chart-mini-progress">
    <div class="target" :style="{ left: target + '%'}">
      <span :style="{ backgroundColor: color }" />
      <span :style="{ backgroundColor: color }"/>
    </div>
    <div class="progress-wrapper">
      <div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MiniProgress',
  props: {
    target: {
      type: Number,
      default: 0
    },
    height: {
      type: String,
      default: '10px'
    },
    color: {
      type: String,
      default: '#13C2C2'
    },
    percentage: {
      type: Number,
      default: 0
    }
  }
}
</script>

<style lang="less" scoped>
  .chart-mini-progress {
    padding: 5px 0;
    position: relative;
    width: 100%;

    .target {
      position: absolute;
      top: 0;
      bottom: 0;

      span {
        border-radius: 100px;
        position: absolute;
        top: 0;
        left: 0;
        height: 4px;
        width: 2px;

        &:last-child {
          top: auto;
          bottom: 0;
        }
      }
    }
    .progress-wrapper {
      background-color: #f5f5f5;
      position: relative;

      .progress {
        transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
        border-radius: 1px 0 0 1px;
        background-color: #1890ff;
        width: 0;
        height: 100%;
      }
    }
  }
</style>


================================================
FILE: src/components/Charts/MiniSmoothArea.vue
================================================
<template>
  <div :class="prefixCls">
    <div class="chart-wrapper" :style="{ height: 46 }">
      <v-chart :force-fit="true" :height="100" :data="dataSource" :scale="scale" :padding="[36, 0, 18, 0]">
        <v-tooltip />
        <v-smooth-line position="x*y" :size="2" />
        <v-smooth-area position="x*y" />
      </v-chart>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MiniSmoothArea',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-smooth-area'
    },
    scale: {
      type: [Object, Array],
      required: true
    },
    dataSource: {
      type: Array,
      required: true
    }
  },
  data () {
    return {
      height: 100
    }
  }
}
</script>

<style lang="less" scoped>
  @import "smooth.area.less";
</style>


================================================
FILE: src/components/Charts/Radar.vue
================================================
<template>
  <v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
    <v-tooltip></v-tooltip>
    <v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
    <v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
    <v-legend dataKey="user" marker="circle" :offset="30" />
    <v-coord type="polar" radius="0.8" />
    <v-line position="item*score" color="user" :size="2" />
    <v-point position="item*score" color="user" :size="4" shape="circle" />
  </v-chart>
</template>

<script>
const axis1Opts = {
  dataKey: 'item',
  line: null,
  tickLine: null,
  grid: {
    lineStyle: {
      lineDash: null
    },
    hideFirstLine: false
  }
}
const axis2Opts = {
  dataKey: 'score',
  line: null,
  tickLine: null,
  grid: {
    type: 'polygon',
    lineStyle: {
      lineDash: null
    }
  }
}

const scale = [
  {
    dataKey: 'score',
    min: 0,
    max: 80
  }, {
    dataKey: 'user',
    alias: '类型'
  }
]

export default {
  name: 'Radar',
  props: {
    data: {
      type: Array,
      default: null
    }
  },
  data () {
    return {
      axis1Opts,
      axis2Opts,
      scale
    }
  }
}
</script>

<style scoped>

</style>


================================================
FILE: src/components/Charts/RankList.vue
================================================
<template>
  <div class="rank">
    <h4 class="title">{{ title }}</h4>
    <ul class="list">
      <li :key="index" v-for="(item, index) in list">
        <span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
        <span>{{ item.name }}</span>
        <span>{{ item.total }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'RankList',
  // ['title', 'list']
  props: {
    title: {
      type: String,
      default: ''
    },
    list: {
      type: Array,
      default: null
    }
  }
}
</script>

<style lang="less" scoped>

  .rank {
    padding: 0 32px 32px 72px;

    .list {
      margin: 25px 0 0;
      padding: 0;
      list-style: none;

      li {
        margin-top: 16px;

        span {
          color: rgba(0, 0, 0, .65);
          font-size: 14px;
          line-height: 22px;

          &:first-child {
            background-color: #f5f5f5;
            border-radius: 20px;
            display: inline-block;
            font-size: 12px;
            font-weight: 600;
            margin-right: 24px;
            height: 20px;
            line-height: 20px;
            width: 20px;
            text-align: center;
          }
          &.active {
            background-color: #314659;
            color: #fff;
          }
          &:last-child {
            float: right;
          }
        }
      }
    }
  }

  .mobile .rank {
    padding: 0 32px 32px 32px;
  }

</style>


================================================
FILE: src/components/Charts/TagCloud.vue
================================================
<template>
  <v-chart :width="width" :height="height" :padding="[0]" :data="data" :scale="scale">
    <v-tooltip :show-title="false" />
    <v-coord type="rect" direction="TL" />
    <v-point position="x*y" color="category" shape="cloud" tooltip="value*category" />
  </v-chart>
</template>

<script>
import { registerShape } from 'viser-vue'
const DataSet = require('@antv/data-set')

const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png'

const scale = [
  { dataKey: 'x', nice: false },
  { dataKey: 'y', nice: false }
]

registerShape('point', 'cloud', {
  draw (cfg, container) {
    return container.addShape('text', {
      attrs: {
        fillOpacity: cfg.opacity,
        fontSize: cfg.origin._origin.size,
        rotate: cfg.origin._origin.rotate,
        text: cfg.origin._origin.text,
        textAlign: 'center',
        fontFamily: cfg.origin._origin.font,
        fill: cfg.color,
        textBaseline: 'Alphabetic',
        ...cfg.style,
        x: cfg.x,
        y: cfg.y
      }
    })
  }
})

export default {
  name: 'TagCloud',
  props: {
    tagList: {
      type: Array,
      required: true
    },
    height: {
      type: Number,
      default: 400
    },
    width: {
      type: Number,
      default: 640
    }
  },
  data () {
    return {
      data: [],
      scale
    }
  },
  watch: {
    tagList: function (val) {
      if (val.length > 0) {
        this.initTagCloud(val)
      }
    }
  },
  mounted () {
    if (this.tagList.length > 0) {
      this.initTagCloud(this.tagList)
    }
  },
  methods: {
    initTagCloud (dataSource) {
      const { height, width } = this

      const dv = new DataSet.View().source(dataSource)
      const range = dv.range('value')
      const min = range[0]
      const max = range[1]
      const imageMask = new Image()
      imageMask.crossOrigin = ''
      imageMask.src = imgUrl
      imageMask.onload = () => {
        dv.transform({
          type: 'tag-cloud',
          fields: ['name', 'value'],
          size: [width, height],
          imageMask,
          font: 'Verdana',
          padding: 0,
          timeInterval: 5000, // max execute time
          rotate () {
            let random = ~~(Math.random() * 4) % 4
            if (random === 2) {
              random = 0
            }
            return random * 90 // 0, 90, 270
          },
          fontSize (d) {
            if (d.value) {
              return ((d.value - min) / (max - min)) * (32 - 8) + 8
            }
            return 0
          }
        })
        this.data = dv.rows
      }
    }
  }
}
</script>


================================================
FILE: src/components/Charts/TransferBar.vue
================================================
<template>
  <div :style="{ padding: '0 0 32px 32px' }">
    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
    <v-chart
      height="254"
      :data="data"
      :scale="scale"
      :forceFit="true"
      :padding="['auto', 'auto', '40', '50']">
      <v-tooltip />
      <v-axis />
      <v-bar position="x*y"/>
    </v-chart>
  </div>
</template>

<script>
const tooltip = [
  'x*y',
  (x, y) => ({
    name: x,
    value: y
  })
]
const scale = [{
  dataKey: 'x',
  title: '日期(天)',
  alias: '日期(天)',
  min: 2
}, {
  dataKey: 'y',
  title: '流量(Gb)',
  alias: '流量(Gb)',
  min: 1
}]

export default {
  name: 'Bar',
  props: {
    title: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      data: [],
      scale,
      tooltip
    }
  },
  created () {
    this.getMonthBar()
  },
  methods: {
    getMonthBar () {
      this.$http.get('/analysis/month-bar')
        .then(res => {
          this.data = res.result
        })
    }
  }
}
</script>


================================================
FILE: src/components/Charts/Trend.vue
================================================
<template>
  <div class="chart-trend">
    {{ term }}
    <span>{{ rate }}%</span>
    <span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
  </div>
</template>

<script>
export default {
  name: 'Trend',
  props: {
    term: {
      type: String,
      default: '',
      required: true
    },
    percentage: {
      type: Number,
      default: null
    },
    type: {
      type: Boolean,
      default: null
    },
    target: {
      type: Number,
      default: 0
    },
    value: {
      type: Number,
      default: 0
    },
    fixed: {
      type: Number,
      default: 2
    }
  },
  data () {
    return {
      trend: this.type && 'up' || 'down',
      rate: this.percentage
    }
  },
  created () {
    const type = this.type === null ? this.value >= this.target : this.type
    this.trend = type ? 'up' : 'down'
    this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
  }
}
</script>

<style lang="less" scoped>
  .chart-trend {
    display: inline-block;
    font-size: 14px;
    line-height: 22px;

    .trend-icon {
      font-size: 12px;

      &.up, &.down {
        margin-left: 4px;
        position: relative;
        top: 1px;

        i {
          font-size: 12px;
          transform: scale(.83);
        }
      }

      &.up {
        color: #f5222d;
      }
      &.down {
        color: #52c41a;
        top: -1px;
      }
    }
  }
</style>


================================================
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
================================================
<template>
  <div :class="prefixCls">
    <quill-editor
      v-model="content"
      ref="myQuillEditor"
      :options="editorOption"
      @blur="onEditorBlur($event)"
      @focus="onEditorFocus($event)"
      @ready="onEditorReady($event)"
      @change="onEditorChange($event)">
    </quill-editor>

  </div>
</template>

<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

import { quillEditor } from 'vue-quill-editor'

export default {
  name: 'QuillEditor',
  components: {
    quillEditor
  },
  props: {
    prefixCls: {
      type: String,
      default: 'ant-editor-quill'
    },
    // 表单校验用字段
    // eslint-disable-next-line
    value: {
      type: String
    }
  },
  data () {
    return {
      content: null,
      editorOption: {
        // some quill options
      }
    }
  },
  methods: {
    onEditorBlur (quill) {
      console.log('editor blur!', quill)
    },
    onEditorFocus (quill) {
      console.log('editor focus!', quill)
    },
    onEditorReady (quill) {
      console.log('editor ready!', quill)
    },
    onEditorChange ({ quill, html, text }) {
      console.log('editor change!', quill, html, text)
      this.$emit('change', html)
    }
  },
  watch: {
    value (val) {
      this.content = val
    }
  }
}
</script>

<style lang="less" scoped>
@import url('../index.less');

/* 覆盖 quill 默认边框圆角为 ant 默认圆角,用于统一 ant 组件风格 */
.ant-editor-quill {
  line-height: initial;
  :deep(.ql-toolbar.ql-snow) {
    border-radius: @border-radius-base @border-radius-base 0 0;
  }
  :deep(.ql-container.ql-snow) {
    border-radius: 0 0 @border-radius-base @border-radius-base;
  }
}
</style>


================================================
FILE: src/components/Editor/WangEditor.vue
================================================
<template>
  <div :class="prefixCls">
    <div ref="editor" class="editor-wrapper"></div>
  </div>
</template>

<script>
import WEditor from 'wangeditor'

export default {
  name: 'WangEditor',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-editor-wang'
    },
    // eslint-disable-next-line
    value: {
      type: String
    }
  },
  data () {
    return {
      editor: null,
      editorContent: null
    }
  },
  watch: {
    value (val) {
      this.editorContent = val
      this.editor.txt.html(val)
    }
  },
  mounted () {
    this.initEditor()
  },
  methods: {
    initEditor () {
      this.editor = new WEditor(this.$refs.editor)
      // this.editor.onchangeTimeout = 200
      this.editor.customConfig.onchange = (html) => {
        this.editorContent = html
        this.$emit('change', this.editorContent)
      }
      this.editor.create()
    }
  }
}
</script>

<style lang="less" scoped>
.ant-editor-wang {
  .editor-wrapper {
    text-align: left;
  }
}
</style>


================================================
FILE: src/components/Ellipsis/Ellipsis.vue
================================================
<script>
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/util'
/*
    const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;

    const TooltipOverlayStyle = {
      overflowWrap: 'break-word',
      wordWrap: 'break-word',
    };
  */

export default {
  name: 'Ellipsis',
  components: {
    Tooltip
  },
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-ellipsis'
    },
    tooltip: {
      type: Boolean
    },
    length: {
      type: Number,
      required: true
    },
    lines: {
      type: Number,
      default: 1
    },
    fullWidthRecognition: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    getStrDom (str, fullLength) {
      return (
        <span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span>
      )
    },
    getTooltip (fullStr, fullLength) {
      return (
        <Tooltip>
          <template slot="title">{ fullStr }</template>
          { this.getStrDom(fullStr, fullLength) }
        </Tooltip>
      )
    }
  },
  render () {
    const { tooltip, length } = this.$props
    const str = this.$slots.default.map(vNode => vNode.text).join('')
    const fullLength = getStrFullLength(str)
    const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
    return (
      strDom
    )
  }
}
</script>


================================================
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
<ellipsis :length="100" tooltip>
        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.
</ellipsis>
```



## API


参数 | 说明 | 类型 | 默认值
----|------|-----|------
tooltip | 移动到文本展示完整内容的提示 | boolean | -
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -

================================================
FILE: src/components/FooterToolbar/FooterToolBar.vue
================================================
<template>
  <div :class="prefixCls" :style="{ width: barWidth, transition: '0.3s all' }">
    <div style="float: left">
      <slot name="extra">{{ extra }}</slot>
    </div>
    <div style="float: right">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  name: 'FooterToolBar',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-footer-toolbar'
    },
    collapsed: {
      type: Boolean,
      default: false
    },
    isMobile: {
      type: Boolean,
      default: false
    },
    siderWidth: {
      type: Number,
      default: undefined
    },
    extra: {
      type: [String, Object],
      default: ''
    }
  },
  computed: {
    barWidth () {
      return this.isMobile ? undefined : `calc(100% - ${this.collapsed ? 80 : this.siderWidth || 256}px)`
    }
  }
}
</script>

<style lang="less" scoped>

</style>


================================================
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
<footer-tool-bar>
    <a-button type="primary" @click="validate" :loading="loading">提交</a-button>
</footer-tool-bar>
```
或
```html
<footer-tool-bar extra="扩展信息提示">
    <a-button type="primary" @click="validate" :loading="loading">提交</a-button>
</footer-tool-bar>
```


## API

参数 | 说明 | 类型 | 默认值
----|------|-----|------
children (slot) | 工具栏内容,向右对齐 | - | -
extra | 额外信息,向左对齐 | String, Object | -



================================================
FILE: src/components/GlobalFooter/index.vue
================================================
<template>
  <global-footer class="footer custom-render">
    <template v-slot:links>
      <a href="https://www.github.com/vueComponent/pro-layout" target="_blank">Pro Layout</a>
      <a href="https://www.github.com/vueComponent/ant-design-vue-pro" target="_blank">Github</a>
      <a href="https://www.github.com/sendya/" target="_blank">@Sendya</a>
    </template>
    <template v-slot:copyright>
      <a href="https://github.com/vueComponent" target="_blank">vueComponent</a>
    </template>
  </global-footer>
</template>

<script>
import { GlobalFooter } from '@ant-design-vue/pro-layout'

export default {
  name: 'ProGlobalFooter',
  components: {
    GlobalFooter
  }
}
</script>


================================================
FILE: src/components/GlobalHeader/AvatarDropdown.vue
================================================
<template>
  <a-dropdown v-if="currentUser && currentUser.name" placement="bottomRight">
    <span class="ant-pro-account-avatar">
      <a-avatar size="small" src="https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png" class="antd-pro-global-header-index-avatar" />
      <span>{{ currentUser.name }}</span>
    </span>
    <template v-slot:overlay>
      <a-menu class="ant-pro-drop-down menu" :selected-keys="[]">
        <a-menu-item v-if="menu" key="center" @click="handleToCenter">
          <a-icon type="user" />
          {{ $t('menu.account.center') }}
        </a-menu-item>
        <a-menu-item v-if="menu" key="settings" @click="handleToSettings">
          <a-icon type="setting" />
          {{ $t('menu.account.settings') }}
        </a-menu-item>
        <a-menu-divider v-if="menu" />
        <a-menu-item key="logout" @click="handleLogout">
          <a-icon type="logout" />
          {{ $t('menu.account.logout') }}
        </a-menu-item>
      </a-menu>
    </template>
  </a-dropdown>
  <span v-else>
    <a-spin size="small" :style="{ marginLeft: 8, marginRight: 8 }" />
  </span>
</template>

<script>
import { Modal } from 'ant-design-vue'

export default {
  name: 'AvatarDropdown',
  props: {
    currentUser: {
      type: Object,
      default: () => null
    },
    menu: {
      type: Boolean,
      default: true
    }
  },
  methods: {
    handleToCenter () {
      this.$router.push({ path: '/account/center' })
    },
    handleToSettings () {
      this.$router.push({ path: '/account/settings' })
    },
    handleLogout (e) {
      Modal.confirm({
        title: this.$t('layouts.usermenu.dialog.title'),
        content: this.$t('layouts.usermenu.dialog.content'),
        onOk: () => {
          // return new Promise((resolve, reject) => {
          //   setTimeout(Math.random() > 0.5 ? resolve : reject, 1500)
          // }).catch(() => console.log('Oops errors!'))
          return this.$store.dispatch('Logout').then(() => {
            this.$router.push({ name: 'login' })
          })
        },
        onCancel () {}
      })
    }
  }
}
</script>

<style lang="less" scoped>
.ant-pro-drop-down {
  :deep(.action) {
    margin-right: 8px;
  }
  :deep(.ant-dropdown-menu-item) {
    min-width: 160px;
  }
}
</style>


================================================
FILE: src/components/GlobalHeader/RightContent.vue
================================================
<template>
  <div :class="wrpCls">
    <avatar-dropdown :menu="showMenu" :current-user="currentUser" :class="prefixCls" />
    <select-lang :class="prefixCls" />
  </div>
</template>

<script>
import AvatarDropdown from './AvatarDropdown'
import SelectLang from '@/components/SelectLang'

export default {
  name: 'RightContent',
  components: {
    AvatarDropdown,
    SelectLang
  },
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-global-header-index-action'
    },
    isMobile: {
      type: Boolean,
      default: () => false
    },
    topMenu: {
      type: Boolean,
      required: true
    },
    theme: {
      type: String,
      required: true
    }
  },
  data () {
    return {
      showMenu: true,
      currentUser: {}
    }
  },
  computed: {
    wrpCls () {
      return {
        'ant-pro-global-header-index-right': true,
        [`ant-pro-global-header-index-${(this.isMobile || !this.topMenu) ? 'light' : this.theme}`]: true
      }
    }
  },
  mounted () {
    setTimeout(() => {
      this.currentUser = {
        name: 'Serati Ma'
      }
    }, 1500)
  }
}
</script>


================================================
FILE: src/components/IconSelector/IconSelector.vue
================================================
<template>
  <div :class="prefixCls">
    <a-tabs v-model="currentTab" @change="handleTabChange">
      <a-tab-pane v-for="v in icons" :tab="v.title" :key="v.key">
        <ul>
          <li v-for="(icon, key) in v.icons" :key="`${v.key}-${key}`" :class="{ 'active': selectedIcon==icon }" @click="handleSelectedIcon(icon)" >
            <a-icon :type="icon" :style="{ fontSize: '36px' }" />
          </li>
        </ul>
      </a-tab-pane>
    </a-tabs>
  </div>
</template>

<script>
import icons from './icons'

export default {
  name: 'IconSelect',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-icon-selector'
    },
    // eslint-disable-next-line
    value: {
      type: String
    }
  },
  data () {
    return {
      selectedIcon: this.value || '',
      currentTab: 'directional',
      icons
    }
  },
  watch: {
    value (val) {
      this.selectedIcon = val
      this.autoSwitchTab()
    }
  },
  created () {
    if (this.value) {
      this.autoSwitchTab()
    }
  },
  methods: {
    handleSelectedIcon (icon) {
      this.selectedIcon = icon
      this.$emit('change', icon)
    },
    handleTabChange (activeKey) {
      this.currentTab = activeKey
    },
    autoSwitchTab () {
      icons.some(item => item.icons.some(icon => icon === this.value) && (this.currentTab = item.key))
    }
  }
}
</script>

<style lang="less" scoped>
  @import "../index.less";

  ul{
    list-style: none;
    padding: 0;
    overflow-y: scroll;
    height: 250px;

    li{
      display: inline-block;
      padding: @padding-sm;
      margin: 3px 0;
      border-radius: @border-radius-base;

      &:hover, &.active{
        // box-shadow: 0px 0px 5px 2px @primary-color;
        cursor: pointer;
        color: @white;
        background-color: @primary-color;
      }
    }
  }
</style>


================================================
FILE: src/components/IconSelector/README.md
================================================
IconSelector
====

> 图标选择组件,常用于为某一个数据设定一个图标时使用
> eg: 设定菜单列表时,为每个菜单设定一个图标

该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装



### 使用方式

```vue
<template>
	<div>
       <icon-selector @change="handleIconChange"/>
    </div>
</template>

<script>
import IconSelector from '@/components/IconSelector'

export default {
  name: 'YourView',
  components: {
    IconSelector
  },
  data () {
    return {
    }
  },
  methods: {
    handleIconChange (icon) {
      console.log('change Icon', icon)
    }
  }
}
</script>
```



### 事件


| 名称   | 说明                       | 类型   | 默认值 |
| ------ | -------------------------- | ------ | ------ |
| 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
================================================
<script>
import events from './events'

export default {
  name: 'MultiTab',
  data () {
    return {
      fullPathList: [],
      pages: [],
      activeKey: '',
      newTabIndex: 0
    }
  },
  created () {
    // bind event
    events.$on('open', val => {
      if (!val) {
        throw new Error(`multi-tab: open tab ${val} err`)
      }
      this.activeKey = val
    }).$on('close', val => {
      if (!val) {
        this.closeThat(this.activeKey)
        return
      }
      this.closeThat(val)
    }).$on('rename', ({ key, name }) => {
      console.log('rename', key, name)
      try {
        const item = this.pages.find(item => item.path === key)
        item.meta.customTitle = name
        this.$forceUpdate()
      } catch (e) {
      }
    })

    this.pages.push(this.$route)
    this.fullPathList.push(this.$route.fullPath)
    this.selectedLastPath()
  },
  methods: {
    onEdit (targetKey, action) {
      this[action](targetKey)
    },
    remove (targetKey) {
      this.pages = this.pages.filter(page => page.fullPath !== targetKey)
      this.fullPathList = this.fullPathList.filter(path => path !== targetKey)
      // 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页
      if (!this.fullPathList.includes(this.activeKey)) {
        this.selectedLastPath()
      }
    },
    selectedLastPath () {
      this.activeKey = this.fullPathList[this.fullPathList.length - 1]
    },

    // content menu
    closeThat (e) {
      // 判断是否为最后一个标签页,如果是最后一个,则无法被关闭
      if (this.fullPathList.length > 1) {
        this.remove(e)
      } else {
        this.$message.info('这是最后一个标签了, 无法被关闭')
      }
    },
    closeLeft (e) {
      const currentIndex = this.fullPathList.indexOf(e)
      if (currentIndex > 0) {
        this.fullPathList.forEach((item, index) => {
          if (index < currentIndex) {
            this.remove(item)
          }
        })
      } else {
        this.$message.info('左侧没有标签')
      }
    },
    closeRight (e) {
      const currentIndex = this.fullPathList.indexOf(e)
      if (currentIndex < (this.fullPathList.length - 1)) {
        this.fullPathList.forEach((item, index) => {
          if (index > currentIndex) {
            this.remove(item)
          }
        })
      } else {
        this.$message.info('右侧没有标签')
      }
    },
    closeAll (e) {
      const currentIndex = this.fullPathList.indexOf(e)
      this.fullPathList.forEach((item, index) => {
        if (index !== currentIndex) {
          this.remove(item)
        }
      })
    },
    closeMenuClick (key, route) {
      this[key](route)
    },
    renderTabPaneMenu (e) {
      return (
        <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
          <a-menu-item key="closeThat">关闭当前标签</a-menu-item>
          <a-menu-item key="closeRight">关闭右侧</a-menu-item>
          <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
          <a-menu-item key="closeAll">关闭全部</a-menu-item>
        </a-menu>
      )
    },
    // render
    renderTabPane (title, keyPath) {
      const menu = this.renderTabPaneMenu(keyPath)

      return (
        <a-dropdown overlay={menu} trigger={['contextmenu']}>
          <span style={{ userSelect: 'none' }}>{ title }</span>
        </a-dropdown>
      )
    }
  },
  watch: {
    '$route': function (newVal) {
      this.activeKey = newVal.fullPath
      if (this.fullPathList.indexOf(newVal.fullPath) < 0) {
        this.fullPathList.push(newVal.fullPath)
        this.pages.push(newVal)
      }
    },
    activeKey: function (newPathKey) {
      this.$router.push({ path: newPathKey })
    }
  },
  render () {
    const { onEdit, $data: { pages } } = this
    const panes = pages.map(page => {
      return (
        <a-tab-pane
          style={{ height: 0 }}
          tab={this.renderTabPane(page.meta.customTitle || page.meta.title, page.fullPath)}
          key={page.fullPath} closable={pages.length > 1}
        >
        </a-tab-pane>)
    })

    return (
      <div class="ant-pro-multi-tab">
        <div class="ant-pro-multi-tab-wrapper">
          <a-tabs
            hideAdd
            type={'editable-card'}
            v-model={this.activeKey}
            tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }}
            {...{ on: { edit: onEdit } }}>
            {panes}
          </a-tabs>
        </div>
      </div>
    )
  }
}
</script>


================================================
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
================================================
<template>
  <a-popover
    v-model="visible"
    trigger="click"
    placement="bottomRight"
    overlayClassName="header-notice-wrapper"
    :getPopupContainer="() => $refs.noticeRef.parentElement"
    :autoAdjustOverflow="true"
    :arrowPointAtCenter="true"
    :overlayStyle="{ width: '300px', top: '50px' }"
  >
    <template slot="content">
      <a-spin :spinning="loading">
        <a-tabs>
          <a-tab-pane tab="通知" key="1">
            <a-list>
              <a-list-item>
                <a-list-item-meta title="你收到了 14 份新周报" description="一年前">
                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item>
                <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item>
                <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
                  <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
                </a-list-item-meta>
              </a-list-item>
            </a-list>
          </a-tab-pane>
          <a-tab-pane tab="消息" key="2">
            123
          </a-tab-pane>
          <a-tab-pane tab="待办" key="3">
            123
          </a-tab-pane>
        </a-tabs>
      </a-spin>
    </template>
    <span @click="fetchNotice" class="header-notice" ref="noticeRef" style="padding: 0 18px">
      <a-badge count="12">
        <a-icon style="font-size: 16px; padding: 4px" type="bell" />
      </a-badge>
    </span>
  </a-popover>
</template>

<script>
export default {
  name: 'HeaderNotice',
  data () {
    return {
      loading: false,
      visible: false
    }
  },
  methods: {
    fetchNotice () {
      if (!this.visible) {
        this.loading = true
        setTimeout(() => {
          this.loading = false
        }, 2000)
      } else {
        this.loading = false
      }
      this.visible = !this.visible
    }
  }
}
</script>

<style lang="css">
  .header-notice-wrapper {
    top: 50px !important;
  }
</style>
<style lang="less" scoped>
  .header-notice{
    display: inline-block;
    transition: all 0.3s;

    span {
      vertical-align: initial;
    }
  }
</style>


================================================
FILE: src/components/NoticeIcon/index.js
================================================
import NoticeIcon from './NoticeIcon'
export default NoticeIcon


================================================
FILE: src/components/NumberInfo/NumberInfo.vue
================================================
<template>
  <div :class="[prefixCls]">
    <slot name="subtitle">
      <div :class="[`${prefixCls}-subtitle`]">{{ typeof subTitle === 'string' ? subTitle : subTitle() }}</div>
    </slot>
    <div class="number-info-value">
      <span>{{ total }}</span>
      <span class="sub-total">
        {{ subTotal }}
        <icon :type="`caret-${status}`" />
      </span>
    </div>
  </div>
</template>

<script>
import Icon from 'ant-design-vue/es/icon'

export default {
  name: 'NumberInfo',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-number-info'
    },
    total: {
      type: Number,
      required: true
    },
    subTotal: {
      type: Number,
      required: true
    },
    subTitle: {
      type: [String, Function],
      default: ''
    },
    status: {
      type: String,
      default: 'up'
    }
  },
  components: {
    Icon
  },
  data () {
    return {}
  }
}
</script>

<style lang="less" scoped>
  @import "index";
</style>


================================================
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
<number-info
    :sub-title="() => { return 'Visits this week' }"
    :total="12321"
    status="up"
    :sub-total="17.1"></number-info>
```



## 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
================================================
<script>
const googleAdsUrl = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'
export default {
  props: {
    isMobile: Boolean
  },
  // watch: {
  //   $route (e, t) {
  //     const adId = '#adsbygoogle'
  //     // if(isGitee) {
  //     //   adId = '#cf';
  //     // }
  //     if (e.path !== t.path && this.$el.querySelector(adId)) {
  //       this.$el.innerHTML = ''
  //       this.load()
  //     }
  //     this.adInterval && clearInterval(this.adInterval)
  //     this.adInterval = setInterval(() => {
  //       if (!this.$el.querySelector(adId)) {
  //         this.$el.innerHTML = ''
  //         this.load()
  //       }
  //     }, 20000)
  //   }
  // },
  mounted () {
    // this.load()
  },
  methods: {
    load () {
      if (googleAdsUrl) {
        /* eslint-disable */
        let adsbygoogle = []
        const e = document.createElement('script')
        e.id = '_adsbygoogle_js'
        e.src = googleAdsUrl
        this.$el.appendChild(e)
        setTimeout(() => {
          (adsbygoogle = window.adsbygoogle || []).push({})
        }, 2000)
      }
    }
  },
  render () {
    // return <ins class="adsbygoogle" style="display:inline-block;width:728px;height:90px" data-ad-client="ca-pub-4801326429087140" data-ad-slot="6929057621" />
    return <div class="business-pro-ad"><a href="https://store.antdv.com/pro/" target="_blank">(推荐) 企业级商用版 Admin Pro 现已发售,采用 Vue3 + TS 欢迎购买。</a></div>;
  }
}
</script>

<style lang="less" scoped>
.business-pro-ad {
  position: fixed;
  background: rgba(255,255,255,0.25);
  left: 0;
  bottom: 0;
  padding: 0 12px;
  height: 48px;
  width: 258px;
  z-index: 99;
}
</style>


================================================
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 (<div style={style}>
      <Spin size={this.size} style={spinStyle} tip={this.tip} />
    </div>)
  }
}

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 <PageLoading { ...{ props } } />
      }
      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 (
      <div class={'global-search global-search-wrapper'}>
        <div class={'global-search-box'}>
          <Select
            size={'large'}
            showSearch
            placeholder="Input search text.."
            style={{ width: '100%' }}
            defaultActiveFirstOption={false}
            showArrow={false}
            filterOption={false}
            onSearch={handleSearch}
            onChange={handleChange}
            notFoundContent={null}
          >
          </Select>
          <div class={'global-search-tips'}>Open with Ctrl/⌘ + K</div>
        </div>
      </div>
    )
  }
}

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 = (
      <Menu class={['menu', 'ant-pro-header-menu']} selectedKeys={[this.currentLang]} onClick={changeLang}>
        {locales.map(locale => (
          <Menu.Item key={locale}>
            <span role="img" aria-label={languageLabels[locale]}>
              {languageIcons[locale]}
            </span>{' '}
            {languageLabels[locale]}
          </Menu.Item>
        ))}
      </Menu>
    )
    return (
      <Dropdown overlay={langMenu} placement="bottomRight">
        <span class={prefixCls}>
          <Icon type="global" title={i18nRender('navBar.lang')} />
        </span>
      </Dropdown>
    )
  }
}

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
================================================
<template>
  <div class="setting-drawer">
    <a-drawer
      width="300"
      placement="right"
      @close="onClose"
      :closable="false"
      :visible="visible"
      :drawer-style="{ position: 'absolute' }"
      style="position: absolute"
    >
      <div class="setting-drawer-index-content">

        <div :style="{ marginBottom: '24px' }">
          <h3 class="setting-drawer-index-title">整体风格设置</h3>

          <div class="setting-drawer-index-blockChecbox">
            <a-tooltip>
              <template slot="title">
                暗色菜单风格
              </template>
              <div class="setting-drawer-index-item" @click="handleMenuTheme('dark')">
                <img src="https://gw.alipayobjects.com/zos/rmsportal/LCkqqYNmvBEbokSDscrm.svg" alt="dark">
                <div class="setting-drawer-index-selectIcon" v-if="navTheme === 'dark'">
                  <a-icon type="check"/>
                </div>
              </div>
            </a-tooltip>

            <a-tooltip>
              <template slot="title">
                亮色菜单风格
              </template>
              <div class="setting-drawer-index-item" @click="handleMenuTheme('light')">
                <img src="https://gw.alipayobjects.com/zos/rmsportal/jpRkZQMyYRryryPNtyIC.svg" alt="light">
                <div class="setting-drawer-index-selectIcon" v-if="navTheme !== 'dark'">
                  <a-icon type="check"/>
                </div>
              </div>
            </a-tooltip>
          </div>
        </div>

        <div :style="{ marginBottom: '24px' }">
          <h3 class="setting-drawer-index-title">主题色</h3>

          <div style="height: 20px">
            <a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index">
              <template slot="title">
                {{ item.key }}
              </template>
              <a-tag :color="item.color" @click="changeColor(item.color)">
                <a-icon type="check" v-if="item.color === primaryColor"></a-icon>
              </a-tag>
            </a-tooltip>

          </div>
        </div>
        <a-divider />

        <div :style="{ marginBottom: '24px' }">
          <h3 class="setting-drawer-index-title">导航模式</h3>

          <div class="setting-drawer-index-blockChecbox">
            <a-tooltip>
              <template slot="title">
                侧边栏导航
              </template>
              <div class="setting-drawer-index-item" @click="handleLayout('sidemenu')">
                <img src="https://gw.alipayobjects.com/zos/rmsportal/JopDzEhOqwOjeNTXkoje.svg" alt="sidemenu">
                <div class="setting-drawer-index-selectIcon" v-if="layoutMode === 'sidemenu'">
                  <a-icon type="check"/>
                </div>
              </div>
            </a-tooltip>

            <a-tooltip>
              <template slot="title">
                顶部栏导航
              </template>
              <div class="setting-drawer-index-item" @click="handleLayout('topmenu')">
                <img src="https://gw.alipayobjects.com/zos/rmsportal/KDNDBbriJhLwuqMoxcAr.svg" alt="topmenu">
                <div class="setting-drawer-index-selectIcon" v-if="layoutMode !== 'sidemenu'">
                  <a-icon type="check"/>
                </div>
              </div>
            </a-tooltip>
          </div>
          <div :style="{ marginTop: '24px' }">
            <a-list :split="false">
              <a-list-item>
                <a-tooltip slot="actions">
                  <template slot="title">
                    该设定仅 [顶部栏导航] 时有效
                  </template>
                  <a-select size="small" style="width: 80px;" :defaultValue="contentWidth" @change="handleContentWidthChange">
                    <a-select-option value="Fixed">固定</a-select-option>
                    <a-select-option value="Fluid" v-if="layoutMode !== 'sidemenu'">流式</a-select-option>
                  </a-select>
                </a-tooltip>
                <a-list-item-meta>
                  <div slot="title">内容区域宽度</div>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item>
                <a-switch slot="actions" size="small" :defaultChecked="fixedHeader" @change="handleFixedHeader" />
                <a-list-item-meta>
                  <div slot="title">固定 Header</div>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item>
                <a-switch slot="actions" size="small" :disabled="!fixedHeader" :defaultChecked="autoHideHeader" @change="handleFixedHeaderHidden" />
                <a-list-item-meta>
                  <a-tooltip slot="title" placement="left">
                    <template slot="title">固定 Header 时可配置</template>
                    <div :style="{ opacity: !fixedHeader ? '0.5' : '1' }">下滑时隐藏 Header</div>
                  </a-tooltip>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item >
                <a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :defaultChecked="fixSiderbar" @change="handleFixSiderbar" />
                <a-list-item-meta>
                  <div slot="title" :style="{ textDecoration: layoutMode === 'topmenu' ? 'line-through' : 'unset' }">固定侧边菜单</div>
                </a-list-item-meta>
              </a-list-item>
            </a-list>
          </div>
        </div>
        <a-divider />

        <div :style="{ marginBottom: '24px' }">
          <h3 class="setting-drawer-index-title">其他设置</h3>
          <div>
            <a-list :split="false">
              <a-list-item>
                <a-switch slot="actions" size="small" :defaultChecked="colorWeak" @change="onColorWeak" />
                <a-list-item-meta>
                  <div slot="title">色弱模式</div>
                </a-list-item-meta>
              </a-list-item>
              <a-list-item>
                <a-switch slot="actions" size="small" :defaultChecked="multiTab" @change="onMultiTab" />
                <a-list-item-meta>
                  <div slot="title">多页签模式</div>
                </a-list-item-meta>
              </a-list-item>
            </a-list>
          </div>
        </div>
        <a-divider />
        <div :style="{ marginBottom: '24px' }">
          <a-button
            @click="doCopy"
            icon="copy"
            block
          >拷贝设置</a-button>
          <a-alert type="warning" :style="{ marginTop: '24px' }">
            <span slot="message">
              配置栏只在开发环境用于预览,生产环境不会展现,请手动修改配置文件。修改配置文件后,需要清空本地缓存和LocalStorage
              <a href="https://github.com/sendya/ant-design-pro-vue/blob/master/src/config/defaultSettings.js" target="_blank">src/config/defaultSettings.js</a>
            </span>
          </a-alert>
        </div>
      </div>
      <div class="setting-drawer-index-handle" @click="toggle" slot="handle">
        <a-icon type="setting" v-if="!visible"/>
        <a-icon type="close" v-else/>
      </div>
    </a-drawer>
  </div>
</template>

<script>
import SettingItem from './SettingItem'
import config from '@/config/defaultSettings'
import { updateTheme, updateColorWeak, colorList } from './settingConfig'

export default {
  components: {
    SettingItem
  },
  mixins: [],
  data () {
    return {
      visible: false,
      colorList
    }
  },
  watch: {

  },
  mounted () {
    updateTheme(this.primaryColor)
    if (this.colorWeak !== config.colorWeak) {
      updateColorWeak(this.colorWeak)
    }
  },
  methods: {
    showDrawer () {
      this.visible = true
    },
    onClose () {
      this.visible = false
    },
    toggle () {
      this.visible = !this.visible
    },
    onColorWeak (checked) {
      this.$store.dispatch('ToggleWeak', checked)
      updateColorWeak(checked)
    },
    onMultiTab (checked) {
      this.$store.dispatch('ToggleMultiTab', checked)
    },
    handleMenuTheme (theme) {
      this.$store.dispatch('ToggleTheme', theme)
    },
    doCopy () {
      // get current settings from mixin or this.$store.state.app, pay attention to the property name
      const text = `export default {
  primaryColor: '${this.primaryColor}', // primary color of ant design
  navTheme: '${this.navTheme}', // theme for nav menu
  layout: '${this.layoutMode}', // nav menu position: sidemenu or topmenu
  contentWidth: '${this.contentWidth}', // layout of content: Fluid or Fixed, only works when layout is topmenu
  fixedHeader: ${this.fixedHeader}, // sticky header
  fixSiderbar: ${this.fixSiderbar}, // sticky siderbar
  autoHideHeader: ${this.autoHideHeader}, //  auto hide header
  colorWeak: ${this.colorWeak},
  multiTab: ${this.multiTab},
  production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true'
}`
      this.$copyText(text).then(message => {
        console.log('copy', message)
        this.$message.success('复制完毕')
      }).catch(err => {
        console.log('copy.err', err)
        this.$message.error('复制失败')
      })
    },
    handleLayout (mode) {
      this.$store.dispatch('ToggleLayoutMode', mode)
      // 因为顶部菜单不能固定左侧菜单栏,所以强制关闭
      this.handleFixSiderbar(false)
    },
    handleContentWidthChange (type) {
      this.$store.dispatch('ToggleContentWidth', type)
    },
    changeColor (color) {
      if (this.primaryColor !== color) {
        this.$store.dispatch('ToggleColor', color)
        updateTheme(color)
      }
    },
    handleFixedHeader (fixed) {
      this.$store.dispatch('ToggleFixedHeader', fixed)
    },
    handleFixedHeaderHidden (autoHidden) {
      this.$store.dispatch('ToggleFixedHeaderHidden', autoHidden)
    },
    handleFixSiderbar (fixed) {
      if (this.layoutMode === 'topmenu') {
        this.$store.dispatch('ToggleFixSiderbar', false)
        return
      }
      this.$store.dispatch('ToggleFixSiderbar', fixed)
    }
  }
}
</script>

<style lang="less" scoped>

  .setting-drawer-index-content {

    .setting-drawer-index-blockChecbox {
      display: flex;

      .setting-drawer-index-item {
        margin-right: 16px;
        position: relative;
        border-radius: 4px;
        cursor: pointer;

        img {
          width: 48px;
        }

        .setting-drawer-index-selectIcon {
          position: absolute;
          top: 0;
          right: 0;
          width: 100%;
          padding-top: 15px;
          padding-left: 24px;
          height: 100%;
          color: #1890ff;
          font-size: 14px;
          font-weight: 700;
        }
      }
    }
    .setting-drawer-theme-color-colorBlock {
      width: 20px;
      height: 20px;
      border-radius: 2px;
      float: left;
      cursor: pointer;
      margin-right: 8px;
      padding-left: 0px;
      padding-right: 0px;
      text-align: center;
      color: #fff;
      font-weight: 700;

      i {
        font-size: 14px;
      }
    }
  }

  .setting-drawer-index-handle {
    position: absolute;
    top: 240px;
    background: #1890ff;
    width: 48px;
    height: 48px;
    right: 300px;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    pointer-events: auto;
    z-index: 1001;
    text-align: center;
    font-size: 16px;
    border-radius: 4px 0 0 4px;

    i {
      color: rgb(255, 255, 255);
      font-size: 20px;
    }
  }
</style>


================================================
FILE: src/components/SettingDrawer/SettingItem.vue
================================================
<template>
  <div class="setting-drawer-index-item">
    <h3 class="setting-drawer-index-title">{{ title }}</h3>
    <slot></slot>
    <a-divider v-if="divider"/>
  </div>
</template>

<script>
export default {
  name: 'SettingItem',
  props: {
    title: {
      type: String,
      default: ''
    },
    divider: {
      type: Boolean,
      default: false
    }
  }
}
</script>

<style lang="less" scoped>

  .setting-drawer-index-item {
    margin-bottom: 24px;

    .setting-drawer-index-title {
      font-size: 14px;
      color: rgba(0, 0, 0, .85);
      line-height: 22px;
      margin-bottom: 12px;
    }

  }
</style>


================================================
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
================================================
<template>
  <div :class="[prefixCls, lastCls, blockCls, gridCls]">
    <div v-if="title" class="antd-pro-components-standard-form-row-index-label">
      <span>{{ title }}</span>
    </div>
    <div class="antd-pro-components-standard-form-row-index-content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
const classes = [
  'antd-pro-components-standard-form-row-index-standardFormRowBlock',
  'antd-pro-components-standard-form-row-index-standardFormRowGrid',
  'antd-pro-components-standard-form-row-index-standardFormRowLast'
]
export default {
  name: 'StandardFormRow',
  props: {
    prefixCls: {
      type: String,
      default: 'antd-pro-components-standard-form-row-index-standardFormRow'
    },
    title: {
      type: String,
      default: undefined
    },
    last: {
      type: Boolean
    },
    block: {
      type: Boolean
    },
    grid: {
      type: Boolean
    }
  },
  computed: {
    lastCls () {
      return this.last ? classes[2] : null
    },
    blockCls () {
      return this.block ? classes[0] : null
    },
    gridCls () {
      return this.grid ? classes[1] : null
    }
  }
}
</script>

<style lang="less" scoped>
@import '../index.less';

.antd-pro-components-standard-form-row-index-standardFormRow {
  display: flex;
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px dashed @border-color-split;

  :deep(.ant-form-item ) {
    margin-right: 24px;
  }
  :deep(.ant-form-item-label label) {
    margin-right: 0;
    color: @text-color;
  }
  :deep(.ant-form-item-label,
  .ant-form-item-control) {
    padding: 0;
    line-height: 32px;
  }

  .antd-pro-components-standard-form-row-index-label {
    flex: 0 0 auto;
    margin-right: 24px;
    color: @heading-color;
    font-size: @font-size-base;
    text-align: right;
    & > span {
      display: inline-block;
      height: 32px;
      line-height: 32px;
      &::after {
        content: ':';
      }
    }
  }

  .antd-pro-components-standard-form-row-index-content {
    flex: 1 1 0;
    :deep(.ant-form-item:last-child) {
      margin-right: 0;
    }
  }

  &.antd-pro-components-standard-form-row-index-standardFormRowLast {
    margin-bottom: 0;
    padding-bottom: 0;
    border: none;
  }

  &.antd-pro-components-standard-form-row-index-standardFormRowBlock {
    :deep(.ant-form-item,
    div.ant-form-item-control-wrapper) {
      display: block;
    }
  }

  &.antd-pro-components-standard-form-row-index-standardFormRowGrid {
      :deep(.ant-form-item,
      div.ant-form-item-control-wrapper) {
        display: block;
      }
      :deep(.ant-form-item-label) {
        float: left;
      }
  }
}

</style>


================================================
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

<template>
  <s-table
    ref="table"
    size="default"
    :rowKey="(record) => record.data.id"
    :columns="columns"
    :data="loadData"
    :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
  >
  </s-table>
</template>

<script>
  import STable from '@/components'

  export default {
    components: {
      STable
    },
    data() {
      return {
        columns: [
          {
            title: '规则编号',
            dataIndex: 'no'
          },
          {
            title: '描述',
            dataIndex: 'description'
          },
          {
            title: '服务调用次数',
            dataIndex: 'callNo',
            sorter: true,
            needTotal: true,
            customRender: (text) => text + ' 次'
          },
          {
            title: '状态',
            dataIndex: 'status',
            needTotal: true
          },
          {
            title: '更新时间',
            dataIndex: 'updatedAt',
            sorter: true
          }
        ],
        // 查询条件参数
        queryParam: {},
        // 加载数据方法 必须为 Promise 对象
        loadData: parameter => {
          return this.$http.get('/service', {
            params: Object.assign(parameter, this.queryParam)
          }).then(res => {
            return res.result
          })
        },
        selectedRowKeys: [],
        selectedRows: []
      }
    },
    methods: {
      onSelectChange (selectedRowKeys, selectedRows) {
         this.selectedRowKeys = selectedRowKeys
         this.selectedRows = selectedRows
       }
    }
  }
</script>

```



例子2
----

(简单的表格,最后一列是各种操作)

```vue
<template>
  <s-table
    ref="table"
    size="default"
    :columns="columns"
    :data="loadData"
  >
    <span slot="action" slot-scope="text, record">
      <a>编辑</a>
      <a-divider type="vertical"/>
      <a-dropdown>
        <a class="ant-dropdown-link">
          更多 <a-icon type="down"/>
        </a>
        <a-menu slot="overlay">
          <a-menu-item>
            <a href="javascript:;">1st menu item</a>
          </a-menu-item>
          <a-menu-item>
            <a href="javascript:;">2nd menu item</a>
          </a-menu-item>
          <a-menu-item>
            <a href="javascript:;">3rd menu item</a>
          </a-menu-item>
        </a-menu>
      </a-dropdown>
    </span>
  </s-table>
</template>

<script>
  import STable from '@/components/table/'

  export default {
    components: {
      STable
    },
    data() {
      return {
        columns: [
          {
            title: '规则编号',
            dataIndex: 'no'
          },
          {
            title: '描述',
            dataIndex: 'description'
          },
          {
            title: '服务调用次数',
            dataIndex: 'callNo',
          },
          {
            title: '状态',
            dataIndex: 'status',
          },
          {
            title: '更新时间',
            dataIndex: 'updatedAt',
          },
          {
            table: '操作',
            dataIndex: 'action',
            scopedSlots: {customRender: 'action'},
          }
        ],
        // 查询条件参数
        queryParam: {},
        // 加载数据方法 必须为 Promise 对象
        loadData: parameter => {
          return this.$http.get('/service', {
            params: Object.assign(parameter, this.queryParam)
          }).then(res => {
            return res.result
          })
        },
      }
    },
    methods: {
      edit(row) {
        // axios 发送数据到后端 修改数据成功后
        // 调用 refresh() 重新加载列表数据
        // 这里 setTimeout 模拟发起请求的网络延迟..
        setTimeout(() => {
          this.$refs.table.refresh() // refresh() 不传参默认值 false 不刷新到分页第一页
        }, 1500)

      }
    }
  }
</script>
```



内置方法
----

通过 `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 (
        <a style="margin-left: 24px" onClick={() => {
          callback()
          this.clearSelected()
        }}>清空</a>
      )
    },
    renderAlert () {
      // 绘制统计列数据
      const needTotalItems = this.needTotalList.map((item) => {
        return (<span style="margin-right: 12px">
          {item.title}总计 <a style="font-weight: 600">{!item.customRender ? item.total : item.customRender(item.total)}</a>
        </span>)
      })

      // 绘制 清空 按钮
      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 (
        <a-alert showIcon={true} style="margin-bottom: 16px">
          <template slot="message">
            <span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span>
            {needTotalItems}
            {clearItem}
          </template>
        </a-alert>
      )
    }
  },

  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 = (
      <a-table {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.loadData} onExpand={ (expanded, record) => { this.$emit('expand', expanded, record) } }>
        { Object.keys(this.$slots).map(name => (<template slot={name}>{this.$slots[name]}</template>)) }
      </a-table>
    )

    return (
      <div class="table-wrapper">
        { showAlert ? this.renderAlert() : null }
        { table }
      </div>
    )
  }
}


================================================
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 (<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}>
      {$slots.default}
    </CheckableTag>)
  }
}


================================================
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 = <Option key={'total'} checked={this.localCheckAll} {...props}>All</Option>
      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 (
      <div class={classString}>
        {this.renderCheckAll()}
        {this.renderTags(tagItems)}
      </div>
    )
  }
}


================================================
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 (
      <div class={this.prefixCls}>
        <TextArea {...{ props }} value={this.value} onChange={this.handleChange}>
        </TextArea>
        <span class="limit">{this.currentLimit}/{this.limit}</span>
      </div>
    )
  }
}


================================================
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 (
        <Search
          placeholder="input search text"
          style="width: 100%; margin-bottom: 1rem"
        />
      )
    },
    renderIcon (icon) {
      return icon && (<Icon type={icon} />) || null
    },
    renderMenuItem (item) {
      return (
        <Item key={item.key}>
          { this.renderIcon(item.icon) }
          { item.title }
          <a class="btn" style="width: 20px;z-index:1300" {...{ on: { click: () => this.handlePlus(item) } }}><a-icon type="plus"/></a>
        </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 (
        <ItemGroup key={item.key}>
          <template slot="title">
            <span>{ item.title }</span>
            <a-dropdown>
              <a class="btn"><a-icon type="ellipsis" /></a>
              <a-menu slot="overlay">
                <a-menu-item key="1">新增</a-menu-item>
                <a-menu-item key="2">合并</a-menu-item>
                <a-menu-item key="3">移除</a-menu-item>
              </a-menu>
            </a-dropdown>
          </template>
          { childrenItems }
        </ItemGroup>
      )
    },
    renderSubItem (item, key) {
      const childrenItems = item.children && item.children.map(o => {
        return this.renderItem(o, o.key)
      })

      const title = (
        <span slot="title">
          { this.renderIcon(item.icon) }
          <span>{ item.title }</span>
        </span>
      )

      if (item.group) {
        return this.renderItemGroup(item)
      }
      // titleClick={this.handleTitleClick(item)}
      return (
        <SubMenu key={key}>
          { title }
          { childrenItems }
        </SubMenu>
      )
    }
  },
  render () {
    const { dataSource, search } = this.$props

    // this.localOpenKeys = openKeys.slice(0)
    const list = dataSource.map(item => {
      return this.renderItem(item)
    })

    return (
      <div class="tree-wrapper">
        { search ? this.renderSearch() : null }
        <Menu mode="inline" class="custom-tree" {...{ on: { click: item => this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}>
          { list }
        </Menu>
      </div>
    )
  }
}


================================================
FILE: src/components/Trend/Trend.vue
================================================
<template>
  <div :class="[prefixCls, reverseColor && 'reverse-color' ]">
    <span>
      <slot name="term"></slot>
      <span class="item-text">
        <slot></slot>
      </span>
    </span>
    <span :class="[flag]"><a-icon :type="`caret-${flag}`"/></span>
  </div>
</template>

<script>
export default {
  name: 'Trend',
  props: {
    prefixCls: {
      type: String,
      default: 'ant-pro-trend'
    },
    /**
       * 上升下降标识:up|down
       */
    flag: {
      type: String,
      required: true
    },
    /**
       * 颜色反转
       */
    reverseColor: {
      type: Boolean,
      default: false
    }
  }
}
</script>

<style lang="less" scoped>
  @import "index";
</style>


================================================
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
<trend flag="up">5%</trend>
```
或
```html
<trend flag="up">
    <span slot="term">工资</span>
    5%
</trend>
```
或
```html
<trend flag="up" term="工资">5%</trend>
```


## 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
================================================
<template>
  <!-- 两步验证 -->
  <a-modal
    centered
    v-model="visible"
    @cancel="handleCancel"
    :maskClosable="false"
  >
    <div slot="title" :style="{ textAlign: 'center' }">两步验证</div>
    <template slot="footer">
      <div :style="{ textAlign: 'center' }">
        <a-button key="back" @click="handleCancel">返回</a-button>
        <a-button key="submit" type="primary" :loading="stepLoading" @click="handleStepOk">
          继续
        </a-button>
      </div>
    </template>

    <a-spin :spinning="stepLoading">
      <a-form layout="vertical" :auto-form-create="(form)=>{this.form = form}">
        <div class="step-form-wrapper">
          <p style="text-align: center" v-if="!stepLoading">请在手机中打开 Google Authenticator 或两步验证 APP<br />输入 6 位动态码</p>
          <p style="text-align: center" v-else>正在验证..<br/>请稍后</p>
          <a-form-item
            :style="{ textAlign: 'center' }"
            hasFeedback
            fieldDecoratorId="stepCode"
            :fieldDecoratorOptions="{rules: [{ required: true, message: '请输入 6 位动态码!', pattern: /^\d{6}$/, len: 6 }]}"
          >
            <a-input :style="{ textAlign: 'center' }" @keyup.enter.native="handleStepOk" placeholder="000000" />
          </a-form-item>
          <p style="text-align: center">
            <a @click="onForgeStepCode">遗失手机?</a>
          </p>
        </div>
      </a-form>
    </a-spin>
  </a-modal>
</template>

<script>
export default {
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      stepLoading: false,

      form: null
    }
  },
  methods: {
    handleStepOk () {
      const vm = this
      this.stepLoading = true
      this.form.validateFields((err, values) => {
        if (!err) {
          console.log('values', values)
          setTimeout(() => {
            vm.stepLoading = false
            vm.$emit('success', { values })
          }, 2000)
          return
        }
        this.stepLoading = false
        this.$emit('error', { err })
      })
    },
    handleCancel () {
      this.visible = false
      this.$emit('cancel')
    },
    onForgeStepCode () {

    }
  }
}
</script>
<style lang="less" scoped>
  .step-form-wrapper {
    margin: 0 auto;
    width: 80%;
    max-width: 400px;
  }
</style>


================================================
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] , 如下:
 *    <i-button v-action:add >添加用户</a-button>
 *    <a-button v-action:delete>删除用户</a-button>
 *    <a v-action:edit @click="edit(record)">修改</a>
 *
 *  - 当前用户没有权限时,组件上使用了该指令则会被隐藏
 *  - 当后台权限跟 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: '导出' }
}

/**
 * <a-button v-if="$auth('form.edit')">Button</a-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
================================================
<template>
  <pro-layout
    :menus="menus"
    :collapsed="collapsed"
    :mediaQuery="query"
    :isMobile="isMobile"
    :handleMediaQuery="handleMediaQuery"
    :ha
Download .txt
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
Download .txt
SYMBOL INDEX (119 symbols across 33 files)

FILE: babel.config.js
  constant IS_PROD (line 1) | const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
  constant IS_PREVIEW (line 2) | const IS_PREVIEW = process.env.VUE_APP_PREVIEW === 'true'

FILE: config/plugin.config.js
  method changeSelector (line 18) | changeSelector (selector) {

FILE: src/api/login.js
  function login (line 27) | function login (parameter) {
  function getSmsCaptcha (line 35) | function getSmsCaptcha (parameter) {
  function getInfo (line 43) | function getInfo () {
  function getCurrentUserNav (line 53) | function getCurrentUserNav () {
  function logout (line 60) | function logout () {
  function get2step (line 74) | function get2step (parameter) {

FILE: src/api/manage.js
  function getUserList (line 14) | function getUserList (parameter) {
  function getRoleList (line 22) | function getRoleList (parameter) {
  function getServiceList (line 30) | function getServiceList (parameter) {
  function getPermissions (line 38) | function getPermissions (parameter) {
  function getOrgTree (line 46) | function getOrgTree (parameter) {
  function saveService (line 56) | function saveService (parameter) {
  function saveSub (line 64) | function saveSub (sub) {

FILE: src/components/AvatarList/Item.jsx
  method created (line 15) | created () {
  method render (line 18) | render () {

FILE: src/components/AvatarList/List.jsx
  method render (line 33) | render (h) {
  method getItems (line 49) | getItems (items) {

FILE: src/components/Dialog.js
  function dialog (line 3) | function dialog (component, componentProps, modalProps) {

FILE: src/components/PageLoading/index.jsx
  method render (line 15) | render () {
  method data (line 53) | data () {
  method render (line 58) | render () {
  function update (line 69) | function update (config) {

FILE: src/components/Search/GlobalSearch.jsx
  method data (line 6) | data () {
  method mounted (line 11) | mounted () {
  method render (line 24) | render () {

FILE: src/components/SelectLang/index.jsx
  method render (line 27) | render () {

FILE: src/components/SettingDrawer/themeColor.js
  method getAntdSerials (line 5) | getAntdSerials (color) {
  method changeColor (line 15) | changeColor (newColor) {

FILE: src/components/Table/index.js
  method data (line 5) | data () {
  method 'localPagination.current' (line 84) | 'localPagination.current' (val) {
  method pageNum (line 97) | pageNum (val) {
  method pageSize (line 102) | pageSize (val) {
  method showSizeChanger (line 107) | showSizeChanger (val) {
  method created (line 113) | created () {
  method refresh (line 130) | refresh (bool = false) {
  method loadData (line 142) | loadData (pagination, filters = this.filters, sorter = this.sorter) {
  method initTotalList (line 197) | initTotalList (columns) {
  method updateSelect (line 214) | updateSelect (selectedRowKeys, selectedRows) {
  method clearSelected (line 231) | clearSelected () {
  method renderClear (line 242) | renderClear (callback) {
  method renderAlert (line 251) | renderAlert () {
  method render (line 279) | render () {

FILE: src/components/TagSelect/TagSelectOption.jsx
  method data (line 20) | data () {
  method 'checked' (line 26) | 'checked' (val) {
  method render (line 36) | render () {

FILE: src/components/TagSelect/index.jsx
  method data (line 34) | data () {
  method onChange (line 43) | onChange (checked) {
  method onCheckAll (line 53) | onCheckAll (checked) {
  method getItemsKey (line 59) | getItemsKey (items) {
  method renderCheckAll (line 67) | renderCheckAll () {
  method renderExpandable (line 81) | renderExpandable () {
  method renderTags (line 85) | renderTags (items) {
  method render (line 100) | render () {

FILE: src/components/TextArea/index.jsx
  method data (line 26) | data () {
  method value (line 32) | value (val) {
  method created (line 36) | created () {
  method handleChange (line 40) | handleChange (e) {
  method calcLimitNum (line 54) | calcLimitNum (val) {
  method render (line 59) | render () {

FILE: src/components/Tree/Tree.jsx
  method created (line 22) | created () {
  method data (line 25) | data () {
  method handlePlus (line 31) | handlePlus (item) {
  method handleTitleClick (line 34) | handleTitleClick (...args) {
  method renderSearch (line 38) | renderSearch () {
  method renderIcon (line 46) | renderIcon (icon) {
  method renderMenuItem (line 49) | renderMenuItem (item) {
  method renderItem (line 58) | renderItem (item) {
  method renderItemGroup (line 61) | renderItemGroup (item) {
  method renderSubItem (line 83) | renderSubItem (item, key) {

FILE: src/components/_util/util.js
  function filterEmpty (line 10) | function filterEmpty (children = []) {

FILE: src/core/bootstrap.js
  function Initializer (line 15) | function Initializer () {

FILE: src/core/permission/permission.js
  constant PERMISSION_ENUM (line 1) | const PERMISSION_ENUM = {
  function plugin (line 17) | function plugin (Vue) {

FILE: src/locales/index.js
  function setI18nLanguage (line 28) | function setI18nLanguage (lang) {
  function loadLanguageAsync (line 35) | function loadLanguageAsync (lang = defaultLang) {
  function i18nRender (line 55) | function i18nRender (key) {

FILE: src/router/index.js
  function resetRouter (line 23) | function resetRouter () {

FILE: src/store/app-mixin.js
  method isTopMenu (line 19) | isTopMenu () {
  method isSideMenu (line 24) | isSideMenu () {

FILE: src/store/i18n-mixin.js
  method setLang (line 10) | setLang (lang) {

FILE: src/store/modules/app.js
  method setLang (line 86) | setLang ({ commit }, lang) {

FILE: src/store/modules/async-router.js
  method GenerateRoutes (line 19) | GenerateRoutes ({ commit }, data) {

FILE: src/store/modules/static-router.js
  function hasPermission (line 11) | function hasPermission (permission, route) {
  function hasRole (line 37) | function hasRole(roles, route) {
  function filterAsyncRouter (line 45) | function filterAsyncRouter (routerMap, role) {
  method GenerateRoutes (line 70) | GenerateRoutes ({ commit }, data) {

FILE: src/store/modules/user.js
  method Login (line 39) | Login ({ commit }, userInfo) {
  method GetInfo (line 53) | GetInfo ({ commit }) {
  method Logout (line 87) | Logout ({ commit, state }) {

FILE: src/store/mutation-types.js
  constant ACCESS_TOKEN (line 1) | const ACCESS_TOKEN = 'Access-Token'
  constant SIDEBAR_TYPE (line 3) | const SIDEBAR_TYPE = 'sidebar_type'
  constant TOGGLE_MOBILE_TYPE (line 4) | const TOGGLE_MOBILE_TYPE = 'is_mobile'
  constant TOGGLE_NAV_THEME (line 5) | const TOGGLE_NAV_THEME = 'nav_theme'
  constant TOGGLE_LAYOUT (line 6) | const TOGGLE_LAYOUT = 'layout'
  constant TOGGLE_FIXED_HEADER (line 7) | const TOGGLE_FIXED_HEADER = 'fixed_header'
  constant TOGGLE_FIXED_SIDEBAR (line 8) | const TOGGLE_FIXED_SIDEBAR = 'fixed_sidebar'
  constant TOGGLE_CONTENT_WIDTH (line 9) | const TOGGLE_CONTENT_WIDTH = 'content_width'
  constant TOGGLE_HIDE_HEADER (line 10) | const TOGGLE_HIDE_HEADER = 'auto_hide_header'
  constant TOGGLE_COLOR (line 11) | const TOGGLE_COLOR = 'color'
  constant TOGGLE_WEAK (line 12) | const TOGGLE_WEAK = 'weak'
  constant TOGGLE_MULTI_TAB (line 13) | const TOGGLE_MULTI_TAB = 'multi_tab'
  constant APP_LANGUAGE (line 14) | const APP_LANGUAGE = 'app_language'
  constant CONTENT_WIDTH_TYPE (line 16) | const CONTENT_WIDTH_TYPE = {
  constant NAV_THEME (line 21) | const NAV_THEME = {

FILE: src/utils/axios.js
  method install (line 4) | install (Vue, instance) {

FILE: src/utils/request.js
  method install (line 62) | install (Vue) {

FILE: src/utils/routeConvert.js
  function convertRoutes (line 3) | function convertRoutes (nodes) {

FILE: src/utils/util.js
  function timeFix (line 1) | function timeFix () {
  function welcome (line 7) | function welcome () {
  function triggerWindowResizeEvent (line 16) | function triggerWindowResizeEvent () {
  function handleScrollHeader (line 23) | function handleScrollHeader (callback) {
  function isIE (line 48) | function isIE () {
  function removeLoadingAnimate (line 60) | function removeLoadingAnimate (id = '', timeout = 1500) {
  function scorePassword (line 68) | function scorePassword (pass) {

FILE: vue.config.js
  function resolve (line 9) | function resolve (dir) {
  function getGitHash (line 14) | function getGitHash () {
Condensed preview — 255 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (626K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 34,
    "preview": "> 1%\nlast 2 versions\nnot ie <= 10\n"
  },
  {
    "path": ".editorconfig",
    "chars": 659,
    "preview": "[*]\ncharset=utf-8\nend_of_line=lf\ninsert_final_newline=false\nindent_style=space\nindent_size=2\n\n[{*.ng,*.sht,*.html,*.shtm"
  },
  {
    "path": ".eslintrc.js",
    "chars": 1584,
    "preview": "module.exports = {\n  root: true,\n  env: {\n    node: true\n  },\n  'extends': [\n    'plugin:vue/strongly-recommended',\n    "
  },
  {
    "path": ".eslintrc.json",
    "chars": 58,
    "preview": "{\n  \"rules\": {\n    \"space-before-function-paren\": 0\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "chars": 381,
    "preview": "public/* linguist-vendored\n\n# Automatically normalize line endings (to LF) for all text-based files.\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 893,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve(Bug 反馈)\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Descri"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/need-help-issue.md",
    "chars": 369,
    "preview": "---\nname: Need help issue\nabout: Question for use(问题求助)\ntitle: ''\nlabels: question\nassignees: ''\n\n---\n\n**Question (问题描述)"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 754,
    "preview": "First of all, thank you for your contribution! 😄\n\nPull request will be merged after one of collaborators approve.\nPlease"
  },
  {
    "path": ".github/workflows/cloudflare.yml",
    "chars": 1524,
    "preview": "name: Build and Deploy to Cloudflare\non:\n  push:\n    branches: [ master ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n   "
  },
  {
    "path": ".gitignore",
    "chars": 232,
    "preview": ".DS_Store\nnode_modules\n/dist\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn"
  },
  {
    "path": ".husky/.gitignore",
    "chars": 2,
    "preview": "_\n"
  },
  {
    "path": ".husky/commit-msg",
    "chars": 81,
    "preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx --no-install commitlint --edit \"$1\""
  },
  {
    "path": ".husky/pre-commit",
    "chars": 54,
    "preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpm run lint"
  },
  {
    "path": ".lintstagedrc.json",
    "chars": 65,
    "preview": "{\n  \"*.js\": \"eslint --fix\",\n  \"*.{css,less}\": \"stylelint --fix\"\n}"
  },
  {
    "path": ".prettierrc",
    "chars": 109,
    "preview": "{\n  \"printWidth\": 120,\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"prettier.spaceBeforeFunctionParen\": true\n}\n"
  },
  {
    "path": ".stylelintrc.js",
    "chars": 2950,
    "preview": "module.exports = {\n    processors: [],\n    plugins: ['stylelint-order'],\n    extends: [\n        'stylelint-config-standa"
  },
  {
    "path": ".travis.yml",
    "chars": 113,
    "preview": "language: node_js\nnode_js:\n  - 10.15.0\ncache: yarn\nscript:\n  - yarn\n  - yarn run lint --no-fix && yarn run build\n"
  },
  {
    "path": "Dockerfile",
    "chars": 138,
    "preview": "FROM nginx\n\nRUN rm /etc/nginx/conf.d/default.conf\n\nADD deploy/nginx.conf /etc/nginx/conf.d/default.conf\nCOPY dist/ /usr/"
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2018 Anan Yang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 6669,
    "preview": "English | [简体中文](./README.zh-CN.md)\n\n<h1 align=\"center\">Ant Design Vue Pro for Vue 2</h1>\n<div align=\"center\">\nAn out-of"
  },
  {
    "path": "README.zh-CN.md",
    "chars": 6569,
    "preview": "[English](./README.md) | 简体中文\n\n<h1 align=\"center\">Ant Design Vue Pro for Vue 2</h1>\n<div align=\"center\">\nAn out-of-box U"
  },
  {
    "path": "babel.config.js",
    "chars": 644,
    "preview": "const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)\nconst IS_PREVIEW = process.env.VUE_APP_PREVIEW ==="
  },
  {
    "path": "commitlint.config.js",
    "chars": 637,
    "preview": "/**\n * feat:新增功能\n * fix:bug 修复\n * docs:文档更新\n * style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)\n * refactor:重构代码(既没有新增功"
  },
  {
    "path": "config/plugin.config.js",
    "chars": 3455,
    "preview": "const ThemeColorReplacer = require('webpack-theme-color-replacer')\nconst generate = require('@ant-design/colors/lib/gene"
  },
  {
    "path": "config/themePluginConfig.js",
    "chars": 2116,
    "preview": "export default {\n  theme: [\n    {\n      key: 'dark',\n      fileName: 'dark.css',\n      theme: 'dark'\n    },\n    {\n      "
  },
  {
    "path": "deploy/caddy.conf",
    "chars": 92,
    "preview": "0.0.0.0:80 {\n  gzip\n  root /usr/share/nginx/html\n\n  rewrite {\n    r .*\n    to {path} /\n  }\n}"
  },
  {
    "path": "deploy/nginx.conf",
    "chars": 641,
    "preview": "server {\n    listen 80;\n    server_name  _;\n    # gzip config\n    gzip on;\n    gzip_min_length 1k;\n    gzip_comp_level 6"
  },
  {
    "path": "docs/add-page-loading-animate.md",
    "chars": 592,
    "preview": "为首屏增加 加载动画\n====\n\n\n\n## 需求\n\n> 为了缓解用户第一次访问时,加载 JS 过大所导致用户等待白屏时间过长导致的用户体验不好,进行的一个优化动效。\n\n\n\n## 实现方案\n\n1. 将 动画加载 dom 元素放在 #app 内"
  },
  {
    "path": "docs/webpack-bundle-analyzer.md",
    "chars": 738,
    "preview": "先增加依赖\n\n```bash\n// npm\n$ npm install --save-dev webpack-bundle-analyzer\n\n// or yarn\n$ yarn add webpack-bundle-analyzer -D"
  },
  {
    "path": "jest.config.js",
    "chars": 512,
    "preview": "module.exports = {\n  moduleFileExtensions: [\n    'js',\n    'jsx',\n    'json',\n    'vue'\n  ],\n  transform: {\n    '^.+\\\\.v"
  },
  {
    "path": "jsconfig.json",
    "chars": 182,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"src/*\"]\n    }\n  },\n  \"ex"
  },
  {
    "path": "package.json",
    "chars": 3025,
    "preview": "{\n  \"name\": \"vue-antd-pro\",\n  \"version\": \"3.0.4\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\""
  },
  {
    "path": "postcss.config.js",
    "chars": 59,
    "preview": "module.exports = {\n  plugins: {\n    autoprefixer: {}\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "chars": 2532,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-cmn-Hans\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" con"
  },
  {
    "path": "src/App.vue",
    "chars": 567,
    "preview": "<template>\n  <a-config-provider :locale=\"locale\">\n    <div id=\"app\">\n      <router-view/>\n    </div>\n  </a-config-provid"
  },
  {
    "path": "src/api/login.js",
    "chars": 1440,
    "preview": "import request from '@/utils/request'\n\nconst userApi = {\n  Login: '/auth/login',\n  Logout: '/auth/logout',\n  ForgePasswo"
  },
  {
    "path": "src/api/manage.js",
    "chars": 1238,
    "preview": "import request from '@/utils/request'\n\nconst api = {\n  user: '/user',\n  role: '/role',\n  service: '/service',\n  permissi"
  },
  {
    "path": "src/components/ArticleListContent/ArticleListContent.vue",
    "chars": 1693,
    "preview": "<template>\n  <div class=\"antd-pro-components-article-list-content-index-listContent\">\n    <div class=\"description\">\n    "
  },
  {
    "path": "src/components/ArticleListContent/index.js",
    "chars": 89,
    "preview": "import ArticleListContent from './ArticleListContent'\n\nexport default ArticleListContent\n"
  },
  {
    "path": "src/components/AvatarList/Item.jsx",
    "chars": 853,
    "preview": "import PropTypes from 'ant-design-vue/es/_util/vue-types'\nimport { Tooltip, Avatar } from 'ant-design-vue'\nimport { getS"
  },
  {
    "path": "src/components/AvatarList/List.jsx",
    "chars": 1899,
    "preview": "import './index.less'\n\nimport PropTypes from 'ant-design-vue/es/_util/vue-types'\nimport Avatar from 'ant-design-vue/es/a"
  },
  {
    "path": "src/components/AvatarList/index.js",
    "chars": 136,
    "preview": "import AvatarList from './List'\nimport Item from './Item'\n\nexport {\n  AvatarList,\n  Item as AvatarListItem\n}\n\nexport def"
  },
  {
    "path": "src/components/AvatarList/index.less",
    "chars": 1002,
    "preview": "@import '../index';\n\n@avatar-list-prefix-cls: ~\"@{ant-pro-prefix}-avatar-list\";\n@avatar-list-item-prefix-cls: ~\"@{ant-pr"
  },
  {
    "path": "src/components/AvatarList/index.md",
    "chars": 2140,
    "preview": "# AvatarList 用户头像列表\n\n\n一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。\n\n\n\n引用方式:\n\n```javascript\nimport AvatarList from '@/comp"
  },
  {
    "path": "src/components/Charts/Bar.vue",
    "chars": 1019,
    "preview": "<template>\n  <div :style=\"{ padding: '0 0 32px 32px' }\">\n    <h4 :style=\"{ marginBottom: '20px' }\">{{ title }}</h4>\n    "
  },
  {
    "path": "src/components/Charts/ChartCard.vue",
    "chars": 2250,
    "preview": "<template>\n  <a-card :loading=\"loading\" :body-style=\"{ padding: '20px 24px 8px' }\" :bordered=\"false\">\n    <div class=\"ch"
  },
  {
    "path": "src/components/Charts/Liquid.vue",
    "chars": 1181,
    "preview": "<template>\n  <div>\n    <v-chart\n      :forceFit=\"true\"\n      :height=\"height\"\n      :width=\"width\"\n      :data=\"data\"\n  "
  },
  {
    "path": "src/components/Charts/MiniArea.vue",
    "chars": 942,
    "preview": "<template>\n  <div class=\"antv-chart-mini\">\n    <div class=\"chart-wrapper\" :style=\"{ height: 46 }\">\n      <v-chart :force"
  },
  {
    "path": "src/components/Charts/MiniBar.vue",
    "chars": 934,
    "preview": "<template>\n  <div class=\"antv-chart-mini\">\n    <div class=\"chart-wrapper\" :style=\"{ height: 46 }\">\n      <v-chart :force"
  },
  {
    "path": "src/components/Charts/MiniProgress.vue",
    "chars": 1441,
    "preview": "<template>\n  <div class=\"chart-mini-progress\">\n    <div class=\"target\" :style=\"{ left: target + '%'}\">\n      <span :styl"
  },
  {
    "path": "src/components/Charts/MiniSmoothArea.vue",
    "chars": 781,
    "preview": "<template>\n  <div :class=\"prefixCls\">\n    <div class=\"chart-wrapper\" :style=\"{ height: 46 }\">\n      <v-chart :force-fit="
  },
  {
    "path": "src/components/Charts/Radar.vue",
    "chars": 1307,
    "preview": "<template>\n  <v-chart :forceFit=\"true\" height=\"400\" :data=\"data\" :padding=\"[20, 20, 95, 20]\" :scale=\"scale\">\n    <v-tool"
  },
  {
    "path": "src/components/Charts/RankList.vue",
    "chars": 1450,
    "preview": "<template>\n  <div class=\"rank\">\n    <h4 class=\"title\">{{ title }}</h4>\n    <ul class=\"list\">\n      <li :key=\"index\" v-fo"
  },
  {
    "path": "src/components/Charts/TagCloud.vue",
    "chars": 2601,
    "preview": "<template>\n  <v-chart :width=\"width\" :height=\"height\" :padding=\"[0]\" :data=\"data\" :scale=\"scale\">\n    <v-tooltip :show-t"
  },
  {
    "path": "src/components/Charts/TransferBar.vue",
    "chars": 990,
    "preview": "<template>\n  <div :style=\"{ padding: '0 0 32px 32px' }\">\n    <h4 :style=\"{ marginBottom: '20px' }\">{{ title }}</h4>\n    "
  },
  {
    "path": "src/components/Charts/Trend.vue",
    "chars": 1485,
    "preview": "<template>\n  <div class=\"chart-trend\">\n    {{ term }}\n    <span>{{ rate }}%</span>\n    <span :class=\"['trend-icon', tren"
  },
  {
    "path": "src/components/Charts/chart.less",
    "chars": 194,
    "preview": ".antv-chart-mini {\n  position: relative;\n  width: 100%;\n\n  .chart-wrapper {\n    position: absolute;\n    bottom: -28px;\n "
  },
  {
    "path": "src/components/Charts/smooth.area.less",
    "chars": 231,
    "preview": "@import '../index';\n\n@smoothArea-prefix-cls: ~\"@{ant-pro-prefix}-smooth-area\";\n\n.@{smoothArea-prefix-cls} {\n  position: "
  },
  {
    "path": "src/components/Dialog.js",
    "chars": 3236,
    "preview": "import Modal from 'ant-design-vue/es/modal'\nexport default (Vue) => {\n  function dialog (component, componentProps, moda"
  },
  {
    "path": "src/components/Editor/QuillEditor.vue",
    "chars": 1688,
    "preview": "<template>\n  <div :class=\"prefixCls\">\n    <quill-editor\n      v-model=\"content\"\n      ref=\"myQuillEditor\"\n      :options"
  },
  {
    "path": "src/components/Editor/WangEditor.vue",
    "chars": 1009,
    "preview": "<template>\n  <div :class=\"prefixCls\">\n    <div ref=\"editor\" class=\"editor-wrapper\"></div>\n  </div>\n</template>\n\n<script>"
  },
  {
    "path": "src/components/Ellipsis/Ellipsis.vue",
    "chars": 1471,
    "preview": "<script>\nimport Tooltip from 'ant-design-vue/es/tooltip'\nimport { cutStrByFullLength, getStrFullLength } from '@/compone"
  },
  {
    "path": "src/components/Ellipsis/index.js",
    "chars": 59,
    "preview": "import Ellipsis from './Ellipsis'\n\nexport default Ellipsis\n"
  },
  {
    "path": "src/components/Ellipsis/index.md",
    "chars": 672,
    "preview": "# Ellipsis 文本自动省略号\n\n文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。\n\n\n\n引用方式:\n\n```javascript\nimport Ellipsis from '@/components/Ellipsis"
  },
  {
    "path": "src/components/FooterToolbar/FooterToolBar.vue",
    "chars": 877,
    "preview": "<template>\n  <div :class=\"prefixCls\" :style=\"{ width: barWidth, transition: '0.3s all' }\">\n    <div style=\"float: left\">"
  },
  {
    "path": "src/components/FooterToolbar/index.js",
    "chars": 96,
    "preview": "import FooterToolBar from './FooterToolBar'\nimport './index.less'\n\nexport default FooterToolBar\n"
  },
  {
    "path": "src/components/FooterToolbar/index.less",
    "chars": 417,
    "preview": "@import '../index';\n\n@footer-toolbar-prefix-cls: ~\"@{ant-pro-prefix}-footer-toolbar\";\n\n.@{footer-toolbar-prefix-cls} {\n "
  },
  {
    "path": "src/components/FooterToolbar/index.md",
    "chars": 650,
    "preview": "# FooterToolbar 底部工具栏\n\n固定在底部的工具栏。\n\n\n\n## 何时使用\n\n固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。\n\n\n\n引用方式:\n\n```javascript\nimport Footer"
  },
  {
    "path": "src/components/GlobalFooter/index.vue",
    "chars": 691,
    "preview": "<template>\n  <global-footer class=\"footer custom-render\">\n    <template v-slot:links>\n      <a href=\"https://www.github."
  },
  {
    "path": "src/components/GlobalHeader/AvatarDropdown.vue",
    "chars": 2291,
    "preview": "<template>\n  <a-dropdown v-if=\"currentUser && currentUser.name\" placement=\"bottomRight\">\n    <span class=\"ant-pro-accoun"
  },
  {
    "path": "src/components/GlobalHeader/RightContent.vue",
    "chars": 1121,
    "preview": "<template>\n  <div :class=\"wrpCls\">\n    <avatar-dropdown :menu=\"showMenu\" :current-user=\"currentUser\" :class=\"prefixCls\" "
  },
  {
    "path": "src/components/IconSelector/IconSelector.vue",
    "chars": 1823,
    "preview": "<template>\n  <div :class=\"prefixCls\">\n    <a-tabs v-model=\"currentTab\" @change=\"handleTabChange\">\n      <a-tab-pane v-fo"
  },
  {
    "path": "src/components/IconSelector/README.md",
    "chars": 689,
    "preview": "IconSelector\n====\n\n> 图标选择组件,常用于为某一个数据设定一个图标时使用\n> eg: 设定菜单列表时,为每个菜单设定一个图标\n\n该组件由 [@Saraka](https://github.com/saraka-tsuka"
  },
  {
    "path": "src/components/IconSelector/icons.js",
    "chars": 4510,
    "preview": "/**\n * 增加新的图标时,请遵循以下数据结构\n * Adding new icon please follow the data structure below\n */\nexport default [\n  {\n    key: 'di"
  },
  {
    "path": "src/components/IconSelector/index.js",
    "chars": 70,
    "preview": "import IconSelector from './IconSelector'\nexport default IconSelector\n"
  },
  {
    "path": "src/components/MultiTab/MultiTab.vue",
    "chars": 4384,
    "preview": "<script>\nimport events from './events'\n\nexport default {\n  name: 'MultiTab',\n  data () {\n    return {\n      fullPathList"
  },
  {
    "path": "src/components/MultiTab/events.js",
    "chars": 47,
    "preview": "import Vue from 'vue'\nexport default new Vue()\n"
  },
  {
    "path": "src/components/MultiTab/index.js",
    "chars": 754,
    "preview": "import events from './events'\nimport MultiTab from './MultiTab'\nimport './index.less'\n\nconst api = {\n  /**\n   * open new"
  },
  {
    "path": "src/components/MultiTab/index.less",
    "chars": 512,
    "preview": "@import '../index';\n\n@multi-tab-prefix-cls: ~\"@{ant-pro-prefix}-multi-tab\";\n@multi-tab-wrapper-prefix-cls: ~\"@{ant-pro-p"
  },
  {
    "path": "src/components/NProgress/nprogress.less",
    "chars": 1455,
    "preview": "@import url('../index.less');\n\n/* Make clicks pass-through */\n#nprogress {\n  pointer-events: none;\n}\n\n#nprogress .bar {\n"
  },
  {
    "path": "src/components/NoticeIcon/NoticeIcon.vue",
    "chars": 2570,
    "preview": "<template>\n  <a-popover\n    v-model=\"visible\"\n    trigger=\"click\"\n    placement=\"bottomRight\"\n    overlayClassName=\"head"
  },
  {
    "path": "src/components/NoticeIcon/index.js",
    "chars": 64,
    "preview": "import NoticeIcon from './NoticeIcon'\nexport default NoticeIcon\n"
  },
  {
    "path": "src/components/NumberInfo/NumberInfo.vue",
    "chars": 975,
    "preview": "<template>\n  <div :class=\"[prefixCls]\">\n    <slot name=\"subtitle\">\n      <div :class=\"[`${prefixCls}-subtitle`]\">{{ type"
  },
  {
    "path": "src/components/NumberInfo/index.js",
    "chars": 65,
    "preview": "import NumberInfo from './NumberInfo'\n\nexport default NumberInfo\n"
  },
  {
    "path": "src/components/NumberInfo/index.less",
    "chars": 1077,
    "preview": "@import '../index';\n\n@numberInfo-prefix-cls: ~\"@{ant-pro-prefix}-number-info\";\n\n.@{numberInfo-prefix-cls} {\n  .ant-pro-n"
  },
  {
    "path": "src/components/NumberInfo/index.md",
    "chars": 688,
    "preview": "# NumberInfo 数据文本\n\n常用在数据卡片中,用于突出展示某个业务数据。\n\n\n\n引用方式:\n\n```javascript\nimport NumberInfo from '@/components/NumberInfo'\n\nexpo"
  },
  {
    "path": "src/components/Other/CarbonAds.vue",
    "chars": 1659,
    "preview": "<script>\nconst googleAdsUrl = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'\nexport default {\n  props:"
  },
  {
    "path": "src/components/PageLoading/index.jsx",
    "chars": 2227,
    "preview": "import { Spin } from 'ant-design-vue'\n\nexport const PageLoading = {\n  name: 'PageLoading',\n  props: {\n    tip: {\n      t"
  },
  {
    "path": "src/components/Search/GlobalSearch.jsx",
    "chars": 1550,
    "preview": "import { Select } from 'ant-design-vue'\nimport './index.less'\n\nconst GlobalSearch = {\n  name: 'GlobalSearch',\n  data () "
  },
  {
    "path": "src/components/Search/index.less",
    "chars": 453,
    "preview": "@import '~ant-design-vue/es/style/themes/default';\n\n.global-search-wrapper {\n  position: fixed;\n  top: 0;\n  right: 0;\n  "
  },
  {
    "path": "src/components/SelectLang/index.jsx",
    "chars": 1289,
    "preview": "import './index.less'\n\nimport { Icon, Menu, Dropdown } from 'ant-design-vue'\nimport { i18nRender } from '@/locales'\nimpo"
  },
  {
    "path": "src/components/SelectLang/index.less",
    "chars": 554,
    "preview": "@import '~ant-design-vue/es/style/themes/default';\n\n@header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu';\n@header-d"
  },
  {
    "path": "src/components/SettingDrawer/SettingDrawer.vue",
    "chars": 11292,
    "preview": "<template>\n  <div class=\"setting-drawer\">\n    <a-drawer\n      width=\"300\"\n      placement=\"right\"\n      @close=\"onClose\""
  },
  {
    "path": "src/components/SettingDrawer/SettingItem.vue",
    "chars": 630,
    "preview": "<template>\n  <div class=\"setting-drawer-index-item\">\n    <h3 class=\"setting-drawer-index-title\">{{ title }}</h3>\n    <sl"
  },
  {
    "path": "src/components/SettingDrawer/index.js",
    "chars": 73,
    "preview": "import SettingDrawer from './SettingDrawer'\nexport default SettingDrawer\n"
  },
  {
    "path": "src/components/SettingDrawer/settingConfig.js",
    "chars": 1023,
    "preview": "import message from 'ant-design-vue/es/message'\n// import defaultSettings from '../defaultSettings';\nimport themeColor f"
  },
  {
    "path": "src/components/SettingDrawer/themeColor.js",
    "chars": 846,
    "preview": "import client from 'webpack-theme-color-replacer/client'\nimport generate from '@ant-design/colors/lib/generate'\n\nexport "
  },
  {
    "path": "src/components/StandardFormRow/StandardFormRow.vue",
    "chars": 2658,
    "preview": "<template>\n  <div :class=\"[prefixCls, lastCls, blockCls, gridCls]\">\n    <div v-if=\"title\" class=\"antd-pro-components-sta"
  },
  {
    "path": "src/components/StandardFormRow/index.js",
    "chars": 80,
    "preview": "import StandardFormRow from './StandardFormRow'\n\nexport default StandardFormRow\n"
  },
  {
    "path": "src/components/Table/README.md",
    "chars": 8024,
    "preview": "Table 重封装组件说明\n====\n\n\n封装说明\n----\n\n>  基础的使用方式与 API 与 [官方版(Table)](https://vuecomponent.github.io/ant-design-vue/components/"
  },
  {
    "path": "src/components/Table/index.js",
    "chars": 9315,
    "preview": "import T from 'ant-design-vue/es/table/Table'\nimport get from 'lodash.get'\n\nexport default {\n  data () {\n    return {\n  "
  },
  {
    "path": "src/components/TagSelect/TagSelectOption.jsx",
    "chars": 961,
    "preview": "import { Tag } from 'ant-design-vue'\nconst { CheckableTag } = Tag\n\nexport default {\n  name: 'TagSelectOption',\n  props: "
  },
  {
    "path": "src/components/TagSelect/index.jsx",
    "chars": 2723,
    "preview": "import PropTypes from 'ant-design-vue/es/_util/vue-types'\nimport Option from './TagSelectOption.jsx'\nimport { filterEmpt"
  },
  {
    "path": "src/components/TextArea/index.jsx",
    "chars": 1583,
    "preview": "import './style.less'\nimport { getStrFullLength, cutStrByFullLength } from '../_util/util'\nimport Input from 'ant-design"
  },
  {
    "path": "src/components/TextArea/style.less",
    "chars": 183,
    "preview": ".ant-textarea-limit {\n  position: relative;\n\n  .limit {\n    position: absolute;\n    right: 10px;\n    bottom: 5px;\n    fo"
  },
  {
    "path": "src/components/Tree/Tree.jsx",
    "chars": 3118,
    "preview": "import { Menu, Icon, Input } from 'ant-design-vue'\n\nconst { Item, ItemGroup, SubMenu } = Menu\nconst { Search } = Input\n\n"
  },
  {
    "path": "src/components/Trend/Trend.vue",
    "chars": 688,
    "preview": "<template>\n  <div :class=\"[prefixCls, reverseColor && 'reverse-color' ]\">\n    <span>\n      <slot name=\"term\"></slot>\n   "
  },
  {
    "path": "src/components/Trend/index.js",
    "chars": 54,
    "preview": "import Trend from './Trend.vue'\n\nexport default Trend\n"
  },
  {
    "path": "src/components/Trend/index.less",
    "chars": 600,
    "preview": "@import '../index';\n\n@trend-prefix-cls: ~\"@{ant-pro-prefix}-trend\";\n\n.@{trend-prefix-cls} {\n  display: inline-block;\n  f"
  },
  {
    "path": "src/components/Trend/index.md",
    "chars": 654,
    "preview": "# Trend 趋势标记\n\n趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。\n\n\n\n引用方式:\n\n```javascript\nimport Trend from '@/components/Trend"
  },
  {
    "path": "src/components/_util/util.js",
    "chars": 925,
    "preview": "/**\n * components util\n */\n\n/**\n * 清理空值,对象\n * @param children\n * @returns {*[]}\n */\nexport function filterEmpty (childre"
  },
  {
    "path": "src/components/index.js",
    "chars": 1559,
    "preview": "// chart\nimport Bar from '@/components/Charts/Bar'\nimport ChartCard from '@/components/Charts/ChartCard'\nimport Liquid f"
  },
  {
    "path": "src/components/index.less",
    "chars": 190,
    "preview": "@import '~ant-design-vue/lib/style/index';\n\n// The prefix to use on all css classes from ant-pro.\n@ant-pro-prefix : ant-"
  },
  {
    "path": "src/components/tools/TwoStepCaptcha.vue",
    "chars": 2283,
    "preview": "<template>\n  <!-- 两步验证 -->\n  <a-modal\n    centered\n    v-model=\"visible\"\n    @cancel=\"handleCancel\"\n    :maskClosable=\"f"
  },
  {
    "path": "src/config/defaultSettings.js",
    "chars": 958,
    "preview": "/**\n * 项目默认配置项\n * primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage\n * navTheme - sidebar theme ['dark', 'light'] 两种主题\n *"
  },
  {
    "path": "src/config/router.config.js",
    "chars": 13378,
    "preview": "// eslint-disable-next-line\nimport { UserLayout, BasicLayout, BlankLayout } from '@/layouts'\nimport { bxAnaalyse } from "
  },
  {
    "path": "src/core/bootstrap.js",
    "chars": 1464,
    "preview": "import store from '@/store'\nimport storage from 'store'\nimport {\n  ACCESS_TOKEN,\n  APP_LANGUAGE,\n  TOGGLE_CONTENT_WIDTH,"
  },
  {
    "path": "src/core/directives/action.js",
    "chars": 1058,
    "preview": "import Vue from 'vue'\nimport store from '@/store'\n\n/**\n * Action 权限指令\n * 指令用法:\n *  - 在需要控制 action 级别权限的组件上使用 v-action:[m"
  },
  {
    "path": "src/core/icons.js",
    "chars": 298,
    "preview": "/**\n * Custom icon list\n * All icons are loaded here for easy management\n * @see https://vue.ant.design/components/icon/"
  },
  {
    "path": "src/core/lazy_use.js",
    "chars": 2041,
    "preview": "import Vue from 'vue'\n\n// base library\nimport {\n  ConfigProvider,\n  Layout,\n  Input,\n  InputNumber,\n  Button,\n  Switch,\n"
  },
  {
    "path": "src/core/permission/permission.js",
    "chars": 1454,
    "preview": "export const PERMISSION_ENUM = {\n  'add': { key: 'add', label: '新增' },\n  'delete': { key: 'delete', label: '删除' },\n  'ed"
  },
  {
    "path": "src/core/use.js",
    "chars": 741,
    "preview": "import Vue from 'vue'\n\n// base library\nimport Antd from 'ant-design-vue'\nimport Viser from 'viser-vue'\nimport VueCropper"
  },
  {
    "path": "src/global.less",
    "chars": 1405,
    "preview": "@import '~ant-design-vue/es/style/themes/default.less';\n\nhtml,\nbody,\n#app,\n#root {\n  height: 100%;\n}\n\n.colorWeak {\n  fil"
  },
  {
    "path": "src/layouts/BasicLayout.less",
    "chars": 691,
    "preview": "@import '~ant-design-vue/es/style/themes/default.less';\n\n.ant-pro-global-header-index-right {\n  margin-right: 8px;\n\n  &."
  },
  {
    "path": "src/layouts/BasicLayout.vue",
    "chars": 5225,
    "preview": "<template>\n  <pro-layout\n    :menus=\"menus\"\n    :collapsed=\"collapsed\"\n    :mediaQuery=\"query\"\n    :isMobile=\"isMobile\"\n"
  },
  {
    "path": "src/layouts/BlankLayout.vue",
    "chars": 148,
    "preview": "<template>\n  <div>\n    <router-view />\n  </div>\n</template>\n\n<script>\n\nexport default {\n  name: 'BlankLayout'\n}\n</script"
  },
  {
    "path": "src/layouts/PageView.vue",
    "chars": 151,
    "preview": "<template>\n  <page-header-wrapper>\n    <router-view />\n  </page-header-wrapper>\n</template>\n\n<script>\n\nexport default {\n"
  },
  {
    "path": "src/layouts/RouteView.vue",
    "chars": 683,
    "preview": "<script>\nexport default {\n  name: 'RouteView',\n  props: {\n    keepAlive: {\n      type: Boolean,\n      default: true\n    "
  },
  {
    "path": "src/layouts/UserLayout.vue",
    "chars": 3819,
    "preview": "<template>\n\n  <div id=\"userLayout\" :class=\"['user-layout-wrapper', isMobile && 'mobile']\">\n    <div class=\"container\">\n "
  },
  {
    "path": "src/layouts/index.js",
    "chars": 258,
    "preview": "import UserLayout from './UserLayout'\nimport BlankLayout from './BlankLayout'\nimport BasicLayout from './BasicLayout'\nim"
  },
  {
    "path": "src/locales/index.js",
    "chars": 1331,
    "preview": "import Vue from 'vue'\nimport VueI18n from 'vue-i18n'\nimport storage from 'store'\nimport moment from 'moment'\n\n// default"
  },
  {
    "path": "src/locales/lang/en-US/account/settings.js",
    "chars": 3726,
    "preview": "export default {\n  'account.settings.menuMap.basic': 'Basic Settings',\n  'account.settings.menuMap.security': 'Security "
  },
  {
    "path": "src/locales/lang/en-US/account.js",
    "chars": 80,
    "preview": "import settings from './account/settings'\n\nexport default {\n    ...settings\n  }\n"
  },
  {
    "path": "src/locales/lang/en-US/dashboard/analysis.js",
    "chars": 1771,
    "preview": "export default {\n  'dashboard.analysis.test': 'Gongzhuan No.{no} shop',\n  'dashboard.analysis.introduce': 'Introduce',\n "
  },
  {
    "path": "src/locales/lang/en-US/dashboard.js",
    "chars": 82,
    "preview": "import analysis from './dashboard/analysis'\n\nexport default {\n    ...analysis\n  }\n"
  },
  {
    "path": "src/locales/lang/en-US/form/basicForm.js",
    "chars": 3616,
    "preview": "export default {\n  'form.basic-form.basic.title': 'Basic form',\n  'form.basic-form.basic.description':\n   'Form pages ar"
  },
  {
    "path": "src/locales/lang/en-US/form.js",
    "chars": 80,
    "preview": "import basicForm from './form/basicForm'\n\nexport default {\n    ...basicForm\n  }\n"
  },
  {
    "path": "src/locales/lang/en-US/global.js",
    "chars": 127,
    "preview": "export default {\n  submit: 'Submit',\n  save: 'Save',\n  'submit.ok': 'Submit successfully',\n  'save.ok': 'Saved successfu"
  },
  {
    "path": "src/locales/lang/en-US/menu.js",
    "chars": 1622,
    "preview": "export default {\n    'menu.welcome': 'Welcome',\n    'menu.home': 'Home',\n    'menu.dashboard': 'Dashboard',\n    'menu.da"
  },
  {
    "path": "src/locales/lang/en-US/result/fail.js",
    "chars": 575,
    "preview": "export default {\n  'result.fail.error.title': 'Submission Failed',\n  'result.fail.error.description':\n    'Please check "
  },
  {
    "path": "src/locales/lang/en-US/result/success.js",
    "chars": 1120,
    "preview": "export default {\n  'result.success.title': 'Submission Success',\n  'result.success.description':\n    'The submission res"
  },
  {
    "path": "src/locales/lang/en-US/result.js",
    "chars": 122,
    "preview": "import success from './result/success'\nimport fail from './result/fail'\n\nexport default {\n    ...success,\n    ...fail\n  "
  },
  {
    "path": "src/locales/lang/en-US/setting.js",
    "chars": 1407,
    "preview": "export default {\n  'app.setting.pagestyle': 'Page style setting',\n  'app.setting.pagestyle.light': 'Light style',\n  'app"
  },
  {
    "path": "src/locales/lang/en-US/user.js",
    "chars": 2591,
    "preview": "export default {\n  'user.login.userName': 'userName',\n  'user.login.password': 'password',\n  'user.login.username.placeh"
  },
  {
    "path": "src/locales/lang/en-US.js",
    "chars": 874,
    "preview": "import antdEnUS from 'ant-design-vue/es/locale-provider/en_US'\nimport momentEU from 'moment/locale/eu'\nimport global fro"
  },
  {
    "path": "src/locales/lang/zh-CN/account/settings.js",
    "chars": 2845,
    "preview": "export default {\n  'account.settings.menuMap.basic': '基本设置',\n  'account.settings.menuMap.security': '安全设置',\n  'account.s"
  },
  {
    "path": "src/locales/lang/zh-CN/account.js",
    "chars": 80,
    "preview": "import settings from './account/settings'\n\nexport default {\n    ...settings\n  }\n"
  },
  {
    "path": "src/locales/lang/zh-CN/dashboard/analysis.js",
    "chars": 1539,
    "preview": "export default {\n  'dashboard.analysis.test': '工专路 {no} 号店',\n  'dashboard.analysis.introduce': '指标说明',\n  'dashboard.anal"
  },
  {
    "path": "src/locales/lang/zh-CN/dashboard.js",
    "chars": 82,
    "preview": "import analysis from './dashboard/analysis'\n\nexport default {\n    ...analysis\n  }\n"
  },
  {
    "path": "src/locales/lang/zh-CN/form/basicForm.js",
    "chars": 2680,
    "preview": "export default {\n  'form.basic-form.basic.title': '基础表单',\n  'form.basic-form.basic.description':\n    '表单页用于向用户收集或验证信息,基础"
  },
  {
    "path": "src/locales/lang/zh-CN/form.js",
    "chars": 80,
    "preview": "import basicForm from './form/basicForm'\n\nexport default {\n    ...basicForm\n  }\n"
  },
  {
    "path": "src/locales/lang/zh-CN/global.js",
    "chars": 92,
    "preview": "export default {\n  submit: '提交',\n  save: '保存',\n  'submit.ok': '提交成功',\n  'save.ok': '保存成功'\n}\n"
  },
  {
    "path": "src/locales/lang/zh-CN/menu.js",
    "chars": 1363,
    "preview": "export default {\n    'menu.welcome': '欢迎',\n    'menu.home': '主页',\n    'menu.dashboard': '仪表盘',\n    'menu.dashboard.analy"
  },
  {
    "path": "src/locales/lang/zh-CN/result/fail.js",
    "chars": 380,
    "preview": "export default {\n  'result.fail.error.title': '提交失败',\n  'result.fail.error.description': '请核对并修改以下信息后,再重新提交。',\n  'result"
  },
  {
    "path": "src/locales/lang/zh-CN/result/success.js",
    "chars": 754,
    "preview": "export default {\n  'result.success.title': '提交成功',\n  'result.success.description':\n    '提交结果页用于反馈一系列操作任务的处理结果, 如果仅是简单操作,"
  },
  {
    "path": "src/locales/lang/zh-CN/result.js",
    "chars": 122,
    "preview": "import success from './result/success'\nimport fail from './result/fail'\n\nexport default {\n    ...success,\n    ...fail\n  "
  },
  {
    "path": "src/locales/lang/zh-CN/setting.js",
    "chars": 1150,
    "preview": "export default {\n  'app.setting.pagestyle': '整体风格设置',\n  'app.setting.pagestyle.light': '亮色菜单风格',\n  'app.setting.pagestyl"
  },
  {
    "path": "src/locales/lang/zh-CN/user.js",
    "chars": 1901,
    "preview": "export default {\n  'user.login.userName': '用户名',\n  'user.login.password': '密码',\n  'user.login.username.placeholder': '账户"
  },
  {
    "path": "src/locales/lang/zh-CN.js",
    "chars": 790,
    "preview": "import antd from 'ant-design-vue/es/locale-provider/zh_CN'\nimport momentCN from 'moment/locale/zh-cn'\nimport global from"
  },
  {
    "path": "src/main.js",
    "chars": 1192,
    "preview": "// with polyfills\nimport 'core-js/stable'\nimport 'regenerator-runtime/runtime'\n\nimport Vue from 'vue'\nimport App from '."
  },
  {
    "path": "src/mock/index.js",
    "chars": 722,
    "preview": "import { isIE } from '@/utils/util'\n\n// 判断环境不是 prod 或者 preview 是 true 时,加载 mock 服务\nif (process.env.NODE_ENV !== 'product"
  },
  {
    "path": "src/mock/services/article.js",
    "chars": 2778,
    "preview": "import Mock from 'mockjs2'\nimport { builder, getQueryParameters } from '../util'\n\nconst titles = [\n  'Alipay',\n  'Angula"
  },
  {
    "path": "src/mock/services/auth.js",
    "chars": 1496,
    "preview": "import Mock from 'mockjs2'\nimport { builder, getBody } from '../util'\n\nconst username = ['admin', 'super']\n// 强硬要求 ant.d"
  },
  {
    "path": "src/mock/services/manage.js",
    "chars": 5310,
    "preview": "import Mock from 'mockjs2'\nimport { builder, getQueryParameters } from '../util'\n\nconst totalCount = 5701\n\nconst serverL"
  },
  {
    "path": "src/mock/services/other.js",
    "chars": 28141,
    "preview": "import Mock from 'mockjs2'\nimport { builder } from '../util'\n\nconst orgTree = () => {\n  return builder([{\n    'key': 'ke"
  },
  {
    "path": "src/mock/services/tagCloud.js",
    "chars": 6063,
    "preview": "import Mock from 'mockjs2'\nimport { builder } from '../util'\n\n//\nconst tagCloudData = () => {\n  return builder([{ 'value"
  },
  {
    "path": "src/mock/services/user.js",
    "chars": 21159,
    "preview": "import Mock from 'mockjs2'\nimport { builder } from '../util'\n\nconst info = options => {\n  console.log('options', options"
  },
  {
    "path": "src/mock/util.js",
    "chars": 960,
    "preview": "const responseBody = {\n  message: '',\n  timestamp: 0,\n  result: null,\n  code: 0\n}\n\nexport const builder = (data, message"
  },
  {
    "path": "src/permission.js",
    "chars": 2845,
    "preview": "import router, { resetRouter } from './router'\nimport store from './store'\nimport storage from 'store'\nimport NProgress "
  },
  {
    "path": "src/router/README.md",
    "chars": 4468,
    "preview": "路由/菜单说明\n====\n\n\n格式和说明\n----\n\n```ecmascript 6\nconst routerObject = {\n  redirect: noredirect,\n  name: 'router-name',\n  hidde"
  },
  {
    "path": "src/router/generator-routers.js",
    "chars": 5791,
    "preview": "// eslint-disable-next-line\nimport * as loginService from '@/api/login'\n// eslint-disable-next-line\nimport { BasicLayout"
  },
  {
    "path": "src/router/index.js",
    "chars": 741,
    "preview": "import Vue from 'vue'\nimport Router from 'vue-router'\nimport { constantRouterMap } from '@/config/router.config'\n\n// hac"
  },
  {
    "path": "src/store/app-mixin.js",
    "chars": 790,
    "preview": "import { mapState } from 'vuex'\n\nconst baseMixin = {\n  computed: {\n    ...mapState({\n      layout: state => state.app.la"
  },
  {
    "path": "src/store/device-mixin.js",
    "chars": 168,
    "preview": "import { mapState } from 'vuex'\n\nconst deviceMixin = {\n  computed: {\n    ...mapState({\n      isMobile: state => state.ap"
  },
  {
    "path": "src/store/getters.js",
    "chars": 506,
    "preview": "const getters = {\n  isMobile: state => state.app.isMobile,\n  lang: state => state.app.lang,\n  theme: state => state.app."
  },
  {
    "path": "src/store/i18n-mixin.js",
    "chars": 256,
    "preview": "import { mapState } from 'vuex'\n\nconst i18nMixin = {\n  computed: {\n    ...mapState({\n      currentLang: state => state.a"
  },
  {
    "path": "src/store/index.js",
    "chars": 550,
    "preview": "import Vue from 'vue'\nimport Vuex from 'vuex'\n\nimport app from './modules/app'\nimport user from './modules/user'\n\n// def"
  },
  {
    "path": "src/store/modules/app.js",
    "chars": 2418,
    "preview": "import storage from 'store'\nimport {\n  SIDEBAR_TYPE,\n  TOGGLE_MOBILE_TYPE,\n  TOGGLE_NAV_THEME,\n  TOGGLE_LAYOUT,\n  TOGGLE"
  },
  {
    "path": "src/store/modules/async-router.js",
    "chars": 751,
    "preview": "/**\n * 向后端请求用户的菜单,动态生成路由\n */\nimport { constantRouterMap } from '@/config/router.config'\nimport { generatorDynamicRouter "
  },
  {
    "path": "src/store/modules/static-router.js",
    "chars": 1861,
    "preview": "import { asyncRouterMap, constantRouterMap } from '@/config/router.config'\nimport cloneDeep from 'lodash.clonedeep'\n\n/**"
  },
  {
    "path": "src/store/modules/user.js",
    "chars": 2827,
    "preview": "import storage from 'store'\nimport expirePlugin from 'store/plugins/expire'\nimport { login, getInfo, logout } from '@/ap"
  },
  {
    "path": "src/store/mutation-types.js",
    "chars": 716,
    "preview": "export const ACCESS_TOKEN = 'Access-Token'\n\nexport const SIDEBAR_TYPE = 'sidebar_type'\nexport const TOGGLE_MOBILE_TYPE ="
  },
  {
    "path": "src/utils/axios.js",
    "chars": 597,
    "preview": "const VueAxios = {\n  vm: {},\n  // eslint-disable-next-line no-unused-vars\n  install (Vue, instance) {\n    if (this.insta"
  },
  {
    "path": "src/utils/domUtil.js",
    "chars": 563,
    "preview": "import config from '@/config/defaultSettings'\n\nexport const setDocumentTitle = function (title) {\n  document.title = tit"
  },
  {
    "path": "src/utils/filter.js",
    "chars": 542,
    "preview": "import Vue from 'vue'\nimport moment from 'moment'\nimport 'moment/locale/zh-cn'\nmoment.locale('zh-cn')\n\nVue.filter('Numbe"
  },
  {
    "path": "src/utils/request.js",
    "chars": 1652,
    "preview": "import axios from 'axios'\nimport store from '@/store'\nimport storage from 'store'\nimport notification from 'ant-design-v"
  },
  {
    "path": "src/utils/routeConvert.js",
    "chars": 691,
    "preview": "import cloneDeep from 'lodash.clonedeep'\n\nexport function convertRoutes (nodes) {\n  if (!nodes) return null\n\n  nodes = c"
  },
  {
    "path": "src/utils/screenLog.js",
    "chars": 1580,
    "preview": "/* eslint-disable */\nexport const printANSI = () => {\n  // console.clear()\n  console.log('[antd pro] created()')\n  // AS"
  },
  {
    "path": "src/utils/util.js",
    "chars": 2357,
    "preview": "export function timeFix () {\n  const time = new Date()\n  const hour = time.getHours()\n  return hour < 9 ? '早上好' : hour <"
  },
  {
    "path": "src/utils/utils.less",
    "chars": 850,
    "preview": ".textOverflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  word-break: break-all;\n  white-space: nowrap;\n}\n\n.tex"
  },
  {
    "path": "src/views/404.vue",
    "chars": 132,
    "preview": "<template>\n  <div>\n    404 page\n  </div>\n</template>\n\n<script>\nexport default {\n  name: '404'\n}\n</script>\n\n<style scoped"
  },
  {
    "path": "src/views/account/center/index.vue",
    "chars": 6863,
    "preview": "<template>\n  <div class=\"page-header-index-wide page-header-wrapper-grid-content-main\">\n    <a-row :gutter=\"24\">\n      <"
  },
  {
    "path": "src/views/account/center/page/App.vue",
    "chars": 2729,
    "preview": "<template>\n  <div class=\"app-list\">\n    <a-list\n      :grid=\"{ gutter: 24, lg: 3, md: 2, sm: 1, xs: 1 }\"\n      :dataSour"
  },
  {
    "path": "src/views/account/center/page/Article.vue",
    "chars": 1916,
    "preview": "<template>\n  <a-list\n    size=\"large\"\n    rowKey=\"id\"\n    :loading=\"loading\"\n    itemLayout=\"vertical\"\n    :dataSource=\""
  },
  {
    "path": "src/views/account/center/page/Project.vue",
    "chars": 2638,
    "preview": "<template>\n  <div class=\"ant-pro-pages-account-projects-cardList\">\n    <a-list :loading=\"loading\" :data-source=\"data\" :g"
  },
  {
    "path": "src/views/account/center/page/index.js",
    "chars": 146,
    "preview": "import AppPage from './App'\nimport ArticlePage from './Article'\nimport ProjectPage from './Project'\n\nexport { AppPage, A"
  },
  {
    "path": "src/views/account/settings/AvatarModal.vue",
    "chars": 4845,
    "preview": "<template>\n\n  <a-modal\n    title=\"修改头像\"\n    :visible=\"visible\"\n    :maskClosable=\"false\"\n    :confirmLoading=\"confirmLoa"
  },
  {
    "path": "src/views/account/settings/BasicSetting.vue",
    "chars": 3151,
    "preview": "<template>\n  <div class=\"account-settings-info-view\">\n    <a-row :gutter=\"16\" type=\"flex\" justify=\"center\">\n      <a-col"
  },
  {
    "path": "src/views/account/settings/Binding.vue",
    "chars": 235,
    "preview": "<template>\n  <a-list\n    itemLayout=\"horizontal\"\n    :dataSource=\"data\"\n  >\n\n  </a-list>\n</template>\n\n<script>\nexport de"
  }
]

// ... and 55 more files (download for full content)

About this extraction

This page contains the full source code of the vueComponent/ant-design-vue-pro GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 255 files (566.6 KB), approximately 165.3k tokens, and a symbol index with 119 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!