├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .postcssrc.js
├── LICENSE
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
├── webpack.prod.conf.js
└── webpack.test.conf.js
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── index.html
├── mock
├── mock.js
├── modules
│ ├── chart.js
│ ├── profile.js
│ └── rule.js
├── server.js
└── utils
│ └── roadhog.js
├── package-lock.json
├── package.json
├── src
├── App.vue
├── assets
│ └── logo.png
├── common
│ ├── menu.ts
│ └── router.ts
├── components
│ ├── ActiveChart
│ │ ├── ActiveChart.vue
│ │ └── index.ts
│ ├── AntIcon
│ │ ├── AntIcon.vue
│ │ └── index.ts
│ ├── Authorized
│ │ ├── Authorized.vue
│ │ ├── CheckPermissions.ts
│ │ └── index.ts
│ ├── Avatar
│ │ └── index.vue
│ ├── Charts
│ │ ├── Bar
│ │ │ ├── Bar.vue
│ │ │ └── index.ts
│ │ ├── ChartCard
│ │ │ ├── ChartCard.vue
│ │ │ └── index.ts
│ │ ├── Field
│ │ │ ├── Field.vue
│ │ │ └── index.ts
│ │ ├── Gauge
│ │ │ ├── Gauge.vue
│ │ │ └── index.ts
│ │ ├── MiniArea
│ │ │ ├── MiniArea.vue
│ │ │ └── index.ts
│ │ ├── MiniBar
│ │ │ ├── MiniBar.vue
│ │ │ └── index.ts
│ │ ├── MiniProgress
│ │ │ ├── MiniProgress.vue
│ │ │ └── index.ts
│ │ ├── Pie
│ │ │ ├── Pie.vue
│ │ │ └── index.ts
│ │ ├── TagCloud
│ │ │ ├── TagCloud.vue
│ │ │ └── index.ts
│ │ ├── TimelineChart
│ │ │ ├── TimelineChart.vue
│ │ │ └── index.ts
│ │ ├── Trend
│ │ │ ├── Trend.vue
│ │ │ └── index.ts
│ │ ├── WaterWave
│ │ │ ├── WaterWave.vue
│ │ │ └── index.ts
│ │ ├── autoHeight.d.ts
│ │ ├── autoHeight.js
│ │ ├── default.ts
│ │ └── index.ts
│ ├── CountDown
│ │ ├── CountDown.vue
│ │ └── index.ts
│ ├── Description
│ │ └── index.ts
│ ├── DescriptionList
│ │ ├── Description.vue
│ │ ├── DescriptionList.vue
│ │ ├── index.ts
│ │ └── responsive.ts
│ ├── Divider
│ │ ├── Divider.vue
│ │ └── index.ts
│ ├── EChart
│ │ └── index.vue
│ ├── Exception
│ │ ├── Exception.vue
│ │ ├── index.ts
│ │ └── typeConfig.ts
│ ├── GlobalFooter
│ │ ├── GlobalFooter.vue
│ │ └── index.ts
│ ├── GlobalHeader
│ │ └── index.vue
│ ├── HeaderSearch
│ │ └── index.vue
│ ├── HelloWorld.vue
│ ├── List
│ │ ├── ListItem.vue
│ │ ├── ListItemMeta.vue
│ │ ├── index.vue
│ │ └── style
│ │ │ └── index.scss
│ ├── Login
│ │ ├── Login.vue
│ │ ├── LoginItem.ts
│ │ ├── LoginSubmit.vue
│ │ ├── LoginTab.vue
│ │ ├── index.ts
│ │ └── map.ts
│ ├── LoginSubmit
│ │ └── index.ts
│ ├── LoginTab
│ │ └── index.ts
│ ├── NoticeIcon
│ │ ├── NoticeList.vue
│ │ └── index.vue
│ ├── NumberInfo
│ │ ├── NumberInfo.vue
│ │ └── index.ts
│ ├── PageHeader
│ │ ├── PageHeader.vue
│ │ └── index.ts
│ ├── Result
│ │ ├── Result.vue
│ │ └── index.ts
│ ├── SiderMenu
│ │ └── index.vue
│ ├── StandardTable
│ │ ├── StandardTable.vue
│ │ └── index.ts
│ └── utils
│ │ └── pathTools.ts
├── layouts
│ ├── BasicLayout
│ │ └── index.vue
│ ├── BlankLayout
│ │ └── index.vue
│ ├── PageHeaderLayout
│ │ └── index.vue
│ └── UserLayout
│ │ └── index.vue
├── main.scss
├── main.ts
├── router
│ ├── authority.ts
│ └── index.ts
├── services
│ ├── api.ts
│ └── user.ts
├── store
│ ├── index.ts
│ └── modules
│ │ ├── chart.ts
│ │ ├── form.ts
│ │ ├── index.ts
│ │ ├── login.ts
│ │ ├── monitor.ts
│ │ ├── profile.ts
│ │ ├── rule.ts
│ │ └── user.ts
├── theme
│ ├── anticon.scss
│ ├── element-theme.scss
│ └── theme.scss
├── utils
│ ├── Authorized.ts
│ ├── authority.ts
│ ├── request.ts
│ ├── utils.scss
│ └── utils.ts
├── views
│ ├── Dashboard
│ │ ├── Analysis
│ │ │ └── index.vue
│ │ ├── Monitor
│ │ │ └── index.vue
│ │ └── Workplace
│ │ │ └── index.vue
│ ├── Exception
│ │ ├── 403.vue
│ │ ├── 404.vue
│ │ └── 500.vue
│ ├── Forms
│ │ ├── BasicForm
│ │ │ └── index.vue
│ │ └── StepForm
│ │ │ ├── Step1.vue
│ │ │ ├── Step2.vue
│ │ │ ├── Step3.vue
│ │ │ ├── index.vue
│ │ │ └── style.scss
│ ├── List
│ │ ├── BasicList
│ │ │ └── index.vue
│ │ └── TableList
│ │ │ └── index.vue
│ ├── Profile
│ │ ├── AdvancedProfile
│ │ │ └── index.vue
│ │ └── BasicProfile
│ │ │ └── index.vue
│ ├── Result
│ │ ├── Error
│ │ │ └── index.vue
│ │ └── Success
│ │ │ └── index.vue
│ └── User
│ │ └── Login
│ │ ├── Message.vue
│ │ └── index.vue
└── vue-shim.d.ts
├── static
└── .gitkeep
├── test
├── e2e
│ ├── custom-assertions
│ │ └── elementCount.js
│ ├── nightwatch.conf.js
│ ├── runner.js
│ └── specs
│ │ └── test.js
└── unit
│ ├── .eslintrc
│ ├── jest.conf.js
│ ├── setup.js
│ └── specs
│ └── HelloWorld.spec.js
├── tsconfig.json
└── tslint.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "modules": false
7 | }
8 | ],
9 | "stage-2"
10 | ],
11 | "plugins": [
12 | "transform-runtime",
13 | [
14 | "component",
15 | [
16 | {
17 | "libraryName": "element-ui",
18 | "styleLibraryName": "theme-chalk"
19 | }
20 | ]
21 | ],
22 | [
23 | "import",
24 | [
25 | {
26 | "libraryName": "vue-antd-ui",
27 | "style": "css"
28 | }
29 | ]
30 | ]
31 | ],
32 | "env": {
33 | "test": {
34 | "presets": ["env", "stage-2"],
35 | "plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /test/unit/coverage/
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md
13 | extends: [
14 | 'standard',
15 | 'plugin:vue/recommended'
16 | ],
17 | // required to lint *.vue files
18 | plugins: [
19 | 'html'
20 | ],
21 | // add your custom rules here
22 | 'rules': {
23 | // allow paren-less arrow functions
24 | 'arrow-parens': 0,
25 | // allow async-await
26 | 'generator-star-spacing': 0,
27 | // allow debugger during development
28 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
29 | 'space-before-function-paren': ['error', 'never']
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | scr/assets/* linguist-vendored=true
2 | src/theme/* linguist-vendored=true
3 | *.scss linguist-vendored=true
4 | *.css linguist-vendored=true
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | /test/unit/coverage/
8 | /test/e2e/reports/
9 | selenium-debug.log
10 |
11 | # Editor directories and files
12 | .idea
13 | .vscode
14 | *.suo
15 | *.ntvs*
16 | *.njsproj
17 | *.sln
18 | *.code-workspace
19 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "postcss-import": {},
7 | "autoprefixer": {}
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 element-pro
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Element UI Pro
2 |
3 | Vue 版 Ant Design Pro(基于 Element-ui 实现)
4 |
5 | 
6 |
7 | - 预览:https://qidaizhe11.github.io/element-pro
8 |
9 | ## 特性
10 | - **漂亮**: 高度还原Ant Design Pro设计效果
11 | - **最新技术栈**:使用 Vue/Element UI/Typescript/ECharts等前端前沿技术开发
12 | - **响应式**:针对不同屏幕大小设计
13 | - **代码级还原**:代码组织与实现均在Vue推荐用法基础上尽可能与Ant Design Pro高度一致
14 | - **Mock 数据**:基于[JSON Server](https://github.com/typicode/json-server)及nodemon实现与Ant Design Pro一致用法的本地数据调试方案
15 |
16 | ## 使用
17 |
18 | ``` bash
19 | $ git clone https://github.com/qidaizhe11/element-pro.git --depth=1
20 | $ cd element-pro
21 | $ npm install
22 | $ npm run mock
23 | $ npm run dev
24 | ```
25 |
26 | ## 兼容性
27 |
28 | Modern browsers and IE11.
29 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, function (err, stats) {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 | function exec (cmd) {
7 | return require('child_process').execSync(cmd).toString().trim()
8 | }
9 |
10 | const versionRequirements = [
11 | {
12 | name: 'node',
13 | currentVersion: semver.clean(process.version),
14 | versionRequirement: packageConfig.engines.node
15 | }
16 | ]
17 |
18 | if (shell.which('npm')) {
19 | versionRequirements.push({
20 | name: 'npm',
21 | currentVersion: exec('npm --version'),
22 | versionRequirement: packageConfig.engines.npm
23 | })
24 | }
25 |
26 | module.exports = function () {
27 | const warnings = []
28 | for (let i = 0; i < versionRequirements.length; i++) {
29 | const mod = versionRequirements[i]
30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
31 | warnings.push(mod.name + ': ' +
32 | chalk.red(mod.currentVersion) + ' should be ' +
33 | chalk.green(mod.versionRequirement)
34 | )
35 | }
36 | }
37 |
38 | if (warnings.length) {
39 | console.log('')
40 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
41 | console.log()
42 | for (let i = 0; i < warnings.length; i++) {
43 | const warning = warnings[i]
44 | console.log(' ' + warning)
45 | }
46 | console.log()
47 | process.exit(1)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qidaizhe11/element-pro/588bc474ba82f130fc521f997785558242a648d2/build/logo.png
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const pkg = require('../package.json')
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 | return path.posix.join(assetsSubDirectory, _path)
12 | }
13 |
14 | exports.cssLoaders = function (options) {
15 | options = options || {}
16 |
17 | const cssLoader = {
18 | loader: 'css-loader',
19 | options: {
20 | sourceMap: options.sourceMap
21 | }
22 | }
23 |
24 | var postcssLoader = {
25 | loader: 'postcss-loader',
26 | options: {
27 | sourceMap: options.sourceMap
28 | }
29 | }
30 |
31 | // generate loader string to be used with extract text plugin
32 | function generateLoaders (loader, loaderOptions) {
33 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
34 | if (loader) {
35 | if (loader === 'sass') {
36 | loaders.push('resolve-url-loader')
37 | }
38 | loaders.push({
39 | loader: loader + '-loader',
40 | options: Object.assign({}, loaderOptions, {
41 | sourceMap: options.sourceMap
42 | })
43 | })
44 | }
45 |
46 | // Extract CSS when that option is specified
47 | // (which is the case during production build)
48 | if (options.extract) {
49 | return ExtractTextPlugin.extract({
50 | use: loaders,
51 | fallback: 'vue-style-loader'
52 | })
53 | } else {
54 | return ['vue-style-loader'].concat(loaders)
55 | }
56 | }
57 |
58 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
59 | return {
60 | css: generateLoaders(),
61 | postcss: generateLoaders(),
62 | less: generateLoaders('less'),
63 | sass: generateLoaders('sass', { indentedSyntax: true }),
64 | scss: generateLoaders('sass'),
65 | stylus: generateLoaders('stylus'),
66 | styl: generateLoaders('stylus')
67 | }
68 | }
69 |
70 | // Generate loaders for standalone style files (outside of .vue)
71 | exports.styleLoaders = function (options) {
72 | const output = []
73 | const loaders = exports.cssLoaders(options)
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 | return output
82 | }
83 |
84 | exports.createNotifierCallback = function () {
85 | const notifier = require('node-notifier')
86 |
87 | return (severity, errors) => {
88 | if (severity !== 'error') {
89 | return
90 | }
91 | const error = errors[0]
92 |
93 | const filename = error.file && error.file.split('!').pop()
94 | notifier.notify({
95 | title: pkg.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const isProduction = process.env.NODE_ENV === 'production'
6 | const sourceMapEnabled = isProduction
7 | ? config.build.productionSourceMap
8 | : config.dev.cssSourceMap
9 |
10 |
11 | module.exports = {
12 | loaders: merge(utils.cssLoaders({
13 | sourceMap: sourceMapEnabled,
14 | extract: isProduction
15 | }), {
16 | ts: ['babel-loader', 'ts-loader', 'tslint-loader']
17 | }),
18 | cssSourceMap: sourceMapEnabled,
19 | cacheBusting: config.dev.cacheBusting,
20 | transformToRequire: {
21 | video: 'src',
22 | source: 'src',
23 | img: 'src',
24 | image: 'xlink:href'
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | module.exports = {
12 | context: path.resolve(__dirname, '../'),
13 | entry: {
14 | app: './src/main.ts'
15 | },
16 | output: {
17 | path: config.build.assetsRoot,
18 | filename: '[name].js',
19 | publicPath: process.env.NODE_ENV === 'production'
20 | ? config.build.assetsPublicPath
21 | : config.dev.assetsPublicPath
22 | },
23 | resolve: {
24 | extensions: ['.js', '.ts', '.vue', '.json'],
25 | alias: {
26 | 'vue$': 'vue/dist/vue.esm.js',
27 | '@': resolve('src'),
28 | 'views': resolve('src/views'),
29 | 'assets': resolve('src/assets'),
30 | 'components': resolve('src/components'),
31 | 'api': resolve('src/api'),
32 | 'utils': resolve('src/utils'),
33 | 'config': resolve('src/config'),
34 | 'store': resolve('src/store'),
35 | 'router': resolve('src/router'),
36 | 'vendor': resolve('src/vendor'),
37 | 'theme': resolve('src/theme'),
38 | 'layouts': resolve('src/layouts'),
39 | 'services': resolve('src/services'),
40 | 'common': resolve('src/common')
41 | }
42 | },
43 | module: {
44 | rules: [
45 | // ...(config.dev.useEslint? [{
46 | // test: /\.(js|vue)$/,
47 | // loader: 'eslint-loader',
48 | // enforce: 'pre',
49 | // include: [resolve('src'), resolve('test')],
50 | // options: {
51 | // formatter: require('eslint-friendly-formatter'),
52 | // emitWarning: !config.dev.showEslintErrorsInOverlay
53 | // }
54 | // }] : []),
55 | {
56 | test: /\.ts$/,
57 | exclude: /node_modules/,
58 | enforce: 'pre',
59 | loader: 'tslint-loader'
60 | },
61 | {
62 | test: /\.vue$/,
63 | loader: 'vue-loader',
64 | options: vueLoaderConfig
65 | },
66 | {
67 | test: /\.js$/,
68 | loader: 'babel-loader',
69 | include: [resolve('src'), resolve('test')]
70 | },
71 | {
72 | test: /\.tsx?$/,
73 | use: [
74 | {
75 | loader: 'babel-loader'
76 | },
77 | {
78 | loader: 'ts-loader',
79 | options: {
80 | appendTsSuffixTo: [/\.vue$/]
81 | }
82 | }
83 | ],
84 | exclude: /node_modules/
85 | },
86 | {
87 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
88 | loader: 'url-loader',
89 | options: {
90 | limit: 10000,
91 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
92 | }
93 | },
94 | {
95 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
96 | loader: 'url-loader',
97 | options: {
98 | limit: 10000,
99 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
100 | }
101 | },
102 | {
103 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
104 | loader: 'url-loader',
105 | options: {
106 | limit: 10000,
107 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
108 | }
109 | }
110 | ]
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.conf')
7 | const HtmlWebpackPlugin = require('html-webpack-plugin')
8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
9 | const portfinder = require('portfinder')
10 |
11 | const devWebpackConfig = merge(baseWebpackConfig, {
12 | module: {
13 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
14 | },
15 | // cheap-module-eval-source-map is faster for development
16 | devtool: config.dev.devtool,
17 |
18 | // these devServer options should be customized in /config/index.js
19 | devServer: {
20 | clientLogLevel: 'warning',
21 | historyApiFallback: true,
22 | hot: true,
23 | compress: true,
24 | host: process.env.HOST || config.dev.host,
25 | port: process.env.PORT || config.dev.port,
26 | open: config.dev.autoOpenBrowser,
27 | overlay: config.dev.errorOverlay ? {
28 | warnings: false,
29 | errors: true,
30 | } : false,
31 | publicPath: config.dev.assetsPublicPath,
32 | proxy: config.dev.proxyTable,
33 | quiet: true, // necessary for FriendlyErrorsPlugin
34 | watchOptions: {
35 | poll: config.dev.poll,
36 | }
37 | },
38 | plugins: [
39 | new webpack.DefinePlugin({
40 | 'process.env': require('../config/dev.env')
41 | }),
42 | new webpack.HotModuleReplacementPlugin(),
43 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
44 | new webpack.NoEmitOnErrorsPlugin(),
45 | // https://github.com/ampedandwired/html-webpack-plugin
46 | new HtmlWebpackPlugin({
47 | filename: 'index.html',
48 | template: 'index.html',
49 | inject: true
50 | }),
51 | ]
52 | })
53 |
54 | module.exports = new Promise((resolve, reject) => {
55 | portfinder.basePort = process.env.PORT || config.dev.port
56 | portfinder.getPort((err, port) => {
57 | if (err) {
58 | reject(err)
59 | } else {
60 | // publish the new Port, necessary for e2e tests
61 | process.env.PORT = port
62 | // add port to devServer config
63 | devWebpackConfig.devServer.port = port
64 |
65 | // Add FriendlyErrorsPlugin
66 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
67 | compilationSuccessInfo: {
68 | messages: [`Your application is running here: http://${config.dev.host}:${port}`],
69 | },
70 | onErrors: config.dev.notifyOnErrors
71 | ? utils.createNotifierCallback()
72 | : undefined
73 | }))
74 |
75 | resolve(devWebpackConfig)
76 | }
77 | })
78 | })
79 |
--------------------------------------------------------------------------------
/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // This is the webpack config used for unit tests.
3 |
4 | const utils = require('./utils')
5 | const webpack = require('webpack')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 |
9 | const webpackConfig = merge(baseWebpackConfig, {
10 | // use inline sourcemap for karma-sourcemap-loader
11 | module: {
12 | rules: utils.styleLoaders()
13 | },
14 | devtool: '#inline-source-map',
15 | resolveLoader: {
16 | alias: {
17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
19 | 'scss-loader': 'sass-loader'
20 | }
21 | },
22 | plugins: [
23 | new webpack.DefinePlugin({
24 | 'process.env': require('../config/test.env')
25 | })
26 | ]
27 | })
28 |
29 | // no need for app entry during tests
30 | delete webpackConfig.entry
31 |
32 | module.exports = webpackConfig
33 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"',
7 | BASE_URL: '"http://localhost:8082"'
8 | })
9 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.2.4
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: true,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: true,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | // CSS Sourcemaps off by default because relative paths are "buggy"
44 | // with this option, according to the CSS-Loader README
45 | // (https://github.com/webpack/css-loader#sourcemaps)
46 | // In our experience, they generally work as expected,
47 | // just be aware of this issue when enabling this option.
48 | cssSourceMap: true,
49 | },
50 |
51 | build: {
52 | // Template for index.html
53 | index: path.resolve(__dirname, '../dist/index.html'),
54 |
55 | // Paths
56 | assetsRoot: path.resolve(__dirname, '../dist'),
57 | assetsSubDirectory: 'static',
58 | assetsPublicPath: '/element-pro/',
59 |
60 | /**
61 | * Source Maps
62 | */
63 |
64 | productionSourceMap: true,
65 | // https://webpack.js.org/configuration/devtool/#production
66 | devtool: '#source-map',
67 |
68 | // Gzip off by default as many popular static hosts such as
69 | // Surge or Netlify already gzip all static assets for you.
70 | // Before setting to `true`, make sure to:
71 | // npm install --save-dev compression-webpack-plugin
72 | productionGzip: false,
73 | productionGzipExtensions: ['js', 'css'],
74 |
75 | // Run the build command with an extra argument to
76 | // View the bundle analyzer report after build finishes:
77 | // `npm run build --report`
78 | // Set to `true` or `false` to always turn it on or off
79 | bundleAnalyzerReport: process.env.npm_config_report
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"',
4 | BASE_URL: '"https://demo.liupanfeng.me"'
5 | }
6 |
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const devEnv = require('./dev.env')
4 |
5 | module.exports = merge(devEnv, {
6 | NODE_ENV: '"testing"'
7 | })
8 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | element-pro
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/mock/mock.js:
--------------------------------------------------------------------------------
1 | const mockjs = require('mockjs')
2 |
3 | const {
4 | getProfileAdvancedData,
5 | getProfileBasicData
6 | } = require('./modules/profile')
7 | const { getRule, postRule } = require('./modules/rule')
8 | const { getFakeChartData } = require('./modules/chart')
9 |
10 | module.exports = {
11 | 'GET /api/currentUser': {
12 | name: 'Serati Ma',
13 | avatar:
14 | 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
15 | userid: '00000001',
16 | notifyCount: 12
17 | },
18 | 'GET /api/rule': getRule,
19 | 'POST /api/rule': postRule,
20 | 'POST /api/forms': (req, res) => {
21 | res.send({
22 | message: 'ok'
23 | })
24 | },
25 | 'GET /api/tags': mockjs.mock({
26 | 'list|100': [
27 | {
28 | name: '@city',
29 | 'value|1-100': 150,
30 | 'type|0-2': 1
31 | }
32 | ]
33 | }),
34 | 'GET /api/fake_chart_data': getFakeChartData,
35 | 'GET /api/profile/basic': getProfileBasicData,
36 | 'GET /api/profile/advanced': getProfileAdvancedData,
37 | 'POST /api/login/account': (req, res) => {
38 | const { password, userName, type } = req.body
39 | if (password === '888888' && userName === 'admin') {
40 | res.send({
41 | status: 'ok',
42 | type,
43 | currentAuthority: 'admin'
44 | })
45 | return
46 | }
47 | if (password === '123456' && userName === 'user') {
48 | res.send({
49 | status: 'ok',
50 | type,
51 | currentAuthority: 'user'
52 | })
53 | return
54 | }
55 | res.send({
56 | status: 'error',
57 | type,
58 | currentAuthority: 'guset'
59 | })
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/mock/modules/chart.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment')
2 |
3 | // mock data
4 | const visitData = []
5 | const beginDay = new Date().getTime()
6 |
7 | const fakeY = [7, 5, 4, 2, 4, 7, 5, 6, 5, 9, 6, 3, 1, 5, 3, 6, 5]
8 | for (let i = 0; i < fakeY.length; i += 1) {
9 | visitData.push({
10 | x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format(
11 | 'YYYY-MM-DD'
12 | ),
13 | y: fakeY[i]
14 | })
15 | }
16 |
17 | const visitData2 = []
18 | const fakeY2 = [1, 6, 4, 8, 3, 7, 2]
19 | for (let i = 0; i < fakeY2.length; i += 1) {
20 | visitData2.push({
21 | x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format(
22 | 'YYYY-MM-DD'
23 | ),
24 | y: fakeY2[i]
25 | })
26 | }
27 |
28 | const salesData = []
29 | for (let i = 0; i < 12; i += 1) {
30 | salesData.push({
31 | x: `${i + 1}月`,
32 | y: Math.floor(Math.random() * 1000) + 200
33 | })
34 | }
35 | const searchData = []
36 | for (let i = 0; i < 50; i += 1) {
37 | searchData.push({
38 | index: i + 1,
39 | keyword: `搜索关键词-${i}`,
40 | count: Math.floor(Math.random() * 1000),
41 | range: Math.floor(Math.random() * 100),
42 | status: Math.floor((Math.random() * 10) % 2)
43 | })
44 | }
45 | const salesTypeData = [
46 | {
47 | x: '家用电器',
48 | y: 4544
49 | },
50 | {
51 | x: '食用酒水',
52 | y: 3321
53 | },
54 | {
55 | x: '个护健康',
56 | y: 3113
57 | },
58 | {
59 | x: '服饰箱包',
60 | y: 2341
61 | },
62 | {
63 | x: '母婴产品',
64 | y: 1231
65 | },
66 | {
67 | x: '其他',
68 | y: 1231
69 | }
70 | ]
71 |
72 | const salesTypeDataOnline = [
73 | {
74 | x: '家用电器',
75 | y: 244
76 | },
77 | {
78 | x: '食用酒水',
79 | y: 321
80 | },
81 | {
82 | x: '个护健康',
83 | y: 311
84 | },
85 | {
86 | x: '服饰箱包',
87 | y: 41
88 | },
89 | {
90 | x: '母婴产品',
91 | y: 121
92 | },
93 | {
94 | x: '其他',
95 | y: 111
96 | }
97 | ]
98 |
99 | const salesTypeDataOffline = [
100 | {
101 | x: '家用电器',
102 | y: 99
103 | },
104 | {
105 | x: '个护健康',
106 | y: 188
107 | },
108 | {
109 | x: '服饰箱包',
110 | y: 344
111 | },
112 | {
113 | x: '母婴产品',
114 | y: 255
115 | },
116 | {
117 | x: '其他',
118 | y: 65
119 | }
120 | ]
121 |
122 | const offlineData = []
123 | for (let i = 0; i < 10; i += 1) {
124 | offlineData.push({
125 | name: `门店${i}`,
126 | cvr: Math.ceil(Math.random() * 9) / 10
127 | })
128 | }
129 | const offlineChartData = []
130 | for (let i = 0; i < 20; i += 1) {
131 | offlineChartData.push({
132 | x: new Date().getTime() + 1000 * 60 * 30 * i,
133 | y1: Math.floor(Math.random() * 100) + 10,
134 | y2: Math.floor(Math.random() * 100) + 10
135 | })
136 | }
137 |
138 | const radarOriginData = [
139 | {
140 | name: '个人',
141 | ref: 10,
142 | koubei: 8,
143 | output: 4,
144 | contribute: 5,
145 | hot: 7
146 | },
147 | {
148 | name: '团队',
149 | ref: 3,
150 | koubei: 9,
151 | output: 6,
152 | contribute: 3,
153 | hot: 1
154 | },
155 | {
156 | name: '部门',
157 | ref: 4,
158 | koubei: 1,
159 | output: 6,
160 | contribute: 5,
161 | hot: 7
162 | }
163 | ]
164 |
165 | //
166 | const radarData = []
167 | const radarTitleMap = {
168 | ref: '引用',
169 | koubei: '口碑',
170 | output: '产量',
171 | contribute: '贡献',
172 | hot: '热度'
173 | }
174 | radarOriginData.forEach(item => {
175 | Object.keys(item).forEach(key => {
176 | if (key !== 'name') {
177 | radarData.push({
178 | name: item.name,
179 | label: radarTitleMap[key],
180 | value: item[key]
181 | })
182 | }
183 | })
184 | })
185 |
186 | const getFakeChartData = {
187 | visitData,
188 | visitData2,
189 | salesData,
190 | searchData,
191 | offlineData,
192 | offlineChartData,
193 | salesTypeData,
194 | salesTypeDataOnline,
195 | salesTypeDataOffline,
196 | radarData
197 | }
198 |
199 | module.exports = {
200 | getFakeChartData
201 | }
202 |
--------------------------------------------------------------------------------
/mock/modules/profile.js:
--------------------------------------------------------------------------------
1 | const basicGoods = [
2 | {
3 | id: '1234561',
4 | name: '矿泉水 550ml',
5 | barcode: '12421432143214321',
6 | price: '2.00',
7 | num: '1',
8 | amount: '2.00'
9 | },
10 | {
11 | id: '1234562',
12 | name: '凉茶 300ml',
13 | barcode: '12421432143214322',
14 | price: '3.00',
15 | num: '2',
16 | amount: '6.00'
17 | },
18 | {
19 | id: '1234563',
20 | name: '好吃的薯片',
21 | barcode: '12421432143214323',
22 | price: '7.00',
23 | num: '4',
24 | amount: '28.00'
25 | },
26 | {
27 | id: '1234564',
28 | name: '特别好吃的蛋卷',
29 | barcode: '12421432143214324',
30 | price: '8.50',
31 | num: '3',
32 | amount: '25.50'
33 | }
34 | ]
35 |
36 | const basicProgress = [
37 | {
38 | key: '1',
39 | time: '2017-10-01 14:10',
40 | rate: '联系客户',
41 | status: 'processing',
42 | operator: '取货员 ID1234',
43 | cost: '5mins'
44 | },
45 | {
46 | key: '2',
47 | time: '2017-10-01 14:05',
48 | rate: '取货员出发',
49 | status: 'success',
50 | operator: '取货员 ID1234',
51 | cost: '1h'
52 | },
53 | {
54 | key: '3',
55 | time: '2017-10-01 13:05',
56 | rate: '取货员接单',
57 | status: 'success',
58 | operator: '取货员 ID1234',
59 | cost: '5mins'
60 | },
61 | {
62 | key: '4',
63 | time: '2017-10-01 13:00',
64 | rate: '申请审批通过',
65 | status: 'success',
66 | operator: '系统',
67 | cost: '1h'
68 | },
69 | {
70 | key: '5',
71 | time: '2017-10-01 12:00',
72 | rate: '发起退货申请',
73 | status: 'success',
74 | operator: '用户',
75 | cost: '5mins'
76 | }
77 | ]
78 |
79 | const advancedOperation1 = [
80 | {
81 | key: 'op1',
82 | type: '订购关系生效',
83 | name: '曲丽丽',
84 | status: 'agree',
85 | updatedAt: '2017-10-03 19:23:12',
86 | memo: '-'
87 | },
88 | {
89 | key: 'op2',
90 | type: '财务复审',
91 | name: '付小小',
92 | status: 'reject',
93 | updatedAt: '2017-10-03 19:23:12',
94 | memo: '不通过原因'
95 | },
96 | {
97 | key: 'op3',
98 | type: '部门初审',
99 | name: '周毛毛',
100 | status: 'agree',
101 | updatedAt: '2017-10-03 19:23:12',
102 | memo: '-'
103 | },
104 | {
105 | key: 'op4',
106 | type: '提交订单',
107 | name: '林东东',
108 | status: 'agree',
109 | updatedAt: '2017-10-03 19:23:12',
110 | memo: '很棒'
111 | },
112 | {
113 | key: 'op5',
114 | type: '创建订单',
115 | name: '汗牙牙',
116 | status: 'agree',
117 | updatedAt: '2017-10-03 19:23:12',
118 | memo: '-'
119 | }
120 | ]
121 |
122 | const advancedOperation2 = [
123 | {
124 | key: 'op1',
125 | type: '订购关系生效',
126 | name: '曲丽丽',
127 | status: 'agree',
128 | updatedAt: '2017-10-03 19:23:12',
129 | memo: '-'
130 | }
131 | ]
132 |
133 | const advancedOperation3 = [
134 | {
135 | key: 'op1',
136 | type: '创建订单',
137 | name: '汗牙牙',
138 | status: 'agree',
139 | updatedAt: '2017-10-03 19:23:12',
140 | memo: '-'
141 | }
142 | ]
143 |
144 | const getProfileBasicData = {
145 | basicGoods,
146 | basicProgress
147 | }
148 |
149 | const getProfileAdvancedData = {
150 | advancedOperation1,
151 | advancedOperation2,
152 | advancedOperation3
153 | }
154 |
155 | module.exports = {
156 | getProfileBasicData,
157 | getProfileAdvancedData
158 | }
159 |
--------------------------------------------------------------------------------
/mock/modules/rule.js:
--------------------------------------------------------------------------------
1 | const { parse } = require('url')
2 |
3 | // mock tableListDataSource
4 | let tableListDataSource = []
5 | for (let i = 0; i < 46; i += 1) {
6 | tableListDataSource.push({
7 | key: i,
8 | disabled: i % 6 === 0,
9 | href: 'https://ant.design',
10 | avatar: [
11 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
12 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'
13 | ][i % 2],
14 | no: `TradeCode ${i}`,
15 | title: `一个任务名称 ${i}`,
16 | owner: '曲丽丽',
17 | description: '这是一段描述',
18 | callNo: Math.floor(Math.random() * 1000),
19 | status: Math.floor(Math.random() * 10) % 4,
20 | updatedAt: new Date(`2017-07-${Math.floor(i / 2) + 1}`),
21 | createdAt: new Date(`2017-07-${Math.floor(i / 2) + 1}`),
22 | progress: Math.ceil(Math.random() * 100)
23 | })
24 | }
25 |
26 | function getRule(req, res, u) {
27 | let url = u
28 | if (!url || Object.prototype.toString.call(url) !== '[object String]') {
29 | url = req.url // eslint-disable-line
30 | }
31 |
32 | const params = parse(url, true).query
33 |
34 | let dataSource = [...tableListDataSource]
35 |
36 | if (params.sorter) {
37 | const s = params.sorter.split('_')
38 | dataSource = dataSource.sort((prev, next) => {
39 | if (s[1] === 'descend') {
40 | return next[s[0]] - prev[s[0]]
41 | }
42 | return prev[s[0]] - next[s[0]]
43 | })
44 | }
45 |
46 | if (params.status) {
47 | const status = params.status.split(',')
48 | let filterDataSource = []
49 | status.forEach(s => {
50 | filterDataSource = filterDataSource.concat(
51 | [...dataSource].filter(
52 | data => parseInt(data.status, 10) === parseInt(s[0], 10)
53 | )
54 | )
55 | })
56 | dataSource = filterDataSource
57 | }
58 |
59 | if (params.no) {
60 | dataSource = dataSource.filter(data => data.no.indexOf(params.no) > -1)
61 | }
62 |
63 | let pageSize = 10
64 | if (params.pageSize) {
65 | pageSize = params.pageSize * 1
66 | }
67 |
68 | const result = {
69 | list: dataSource,
70 | pagination: {
71 | total: dataSource.length,
72 | pageSize,
73 | current: parseInt(params.currentPage, 10) || 1
74 | }
75 | }
76 |
77 | if (res && res.json) {
78 | res.json(result)
79 | } else {
80 | return result
81 | }
82 | }
83 |
84 | function postRule(req, res, u, b) {
85 | let url = u
86 | if (!url || Object.prototype.toString.call(url) !== '[object String]') {
87 | url = req.url // eslint-disable-line
88 | }
89 |
90 | const body = (b && b.body) || req.body
91 | const { method, no, description } = body
92 |
93 | switch (method) {
94 | /* eslint no-case-declarations:0 */
95 | case 'delete':
96 | tableListDataSource = tableListDataSource.filter(
97 | item => no.indexOf(item.no) === -1
98 | )
99 | break
100 | case 'post':
101 | const i = Math.ceil(Math.random() * 10000)
102 | tableListDataSource.unshift({
103 | key: i,
104 | href: 'https://ant.design',
105 | avatar: [
106 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
107 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png'
108 | ][i % 2],
109 | no: `TradeCode ${i}`,
110 | title: `一个任务名称 ${i}`,
111 | owner: '曲丽丽',
112 | description,
113 | callNo: Math.floor(Math.random() * 1000),
114 | status: Math.floor(Math.random() * 10) % 2,
115 | updatedAt: new Date(),
116 | createdAt: new Date(),
117 | progress: Math.ceil(Math.random() * 100)
118 | })
119 | break
120 | default:
121 | break
122 | }
123 |
124 | const result = {
125 | list: tableListDataSource,
126 | pagination: {
127 | total: tableListDataSource.length
128 | }
129 | }
130 |
131 | if (res && res.json) {
132 | res.json(result)
133 | } else {
134 | return result
135 | }
136 | }
137 |
138 | module.exports = {
139 | getRule,
140 | postRule
141 | }
142 |
--------------------------------------------------------------------------------
/mock/server.js:
--------------------------------------------------------------------------------
1 | const jsonServer = require('json-server')
2 | const roadhog = require('./utils/roadhog')
3 |
4 | const server = jsonServer.create()
5 | const middlewares = jsonServer.defaults()
6 |
7 | server.use(middlewares)
8 | server.use(jsonServer.bodyParser)
9 |
10 | // roadhog mock模式
11 |
12 | roadhog.init(server)
13 |
14 | // 传统json-server模式
15 |
16 | const router = jsonServer.router()
17 |
18 | server.use(router)
19 |
20 | const port = process.env.PORT || 8082
21 |
22 | server.listen(port, () => {
23 | console.log('json-server running at port:', port)
24 | })
25 |
--------------------------------------------------------------------------------
/mock/utils/roadhog.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert')
2 |
3 | const config = require('../mock.js')
4 |
5 | function parseKey(key) {
6 | let method = 'get'
7 | let path = key
8 |
9 | if (key.indexOf(' ') > -1) {
10 | const splited = key.split(' ')
11 | method = splited[0].toLowerCase()
12 | path = splited[1]
13 | }
14 |
15 | return { method, path }
16 | }
17 |
18 | function createMockHandler(method, path, value) {
19 | return function mockHandler(...args) {
20 | const res = args[1]
21 | if (typeof value === 'function') {
22 | value(...args)
23 | } else {
24 | res.json(value)
25 | }
26 | }
27 | }
28 |
29 | function init(server) {
30 | Object.keys(config).forEach(key => {
31 | const keyParsed = parseKey(key)
32 | assert(!!server[keyParsed.method], `method of ${key} is not valid`)
33 | assert(
34 | typeof config[key] === 'function' || typeof config[key] === 'object',
35 | `mock value of ${key} should be function or object, but got ${typeof config[
36 | key
37 | ]}`
38 | )
39 |
40 | server[keyParsed.method](
41 | keyParsed.path,
42 | createMockHandler(keyParsed.method, keyParsed.path, config[key])
43 | )
44 | })
45 | }
46 |
47 | module.exports = {
48 | init
49 | }
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "element-pro",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "Panfeng qidaizhe11@gmail.com",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "unit": "jest --config test/unit/jest.conf.js --coverage",
11 | "e2e": "node test/e2e/runner.js",
12 | "test": "npm run unit && npm run e2e",
13 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
14 | "build": "node build/build.js",
15 | "mock": "nodemon --watch mock mock/server.js"
16 | },
17 | "dependencies": {
18 | "axios": "^0.17.1",
19 | "bootstrap": "^4.0.0",
20 | "echarts": "^4.0.2",
21 | "echarts-liquidfill": "^2.0.0",
22 | "echarts-wordcloud": "^1.1.3",
23 | "el-table-wrapper": "^2.0.4",
24 | "element-ui": "^2.0.11",
25 | "font-awesome": "^4.7.0",
26 | "lodash": "^4.17.4",
27 | "moment": "^2.20.1",
28 | "nprogress": "^0.2.0",
29 | "numeral": "^2.0.6",
30 | "path-to-regexp": "^2.1.0",
31 | "vue": "^2.5.2",
32 | "vue-antd-ui": "^0.3.1",
33 | "vue-router": "^3.0.1",
34 | "vuex": "^3.0.1"
35 | },
36 | "devDependencies": {
37 | "@types/echarts": "0.0.9",
38 | "@types/lodash": "^4.14.85",
39 | "@types/node": "^8.0.53",
40 | "@types/nprogress": "0.0.29",
41 | "@types/numeral": "0.0.22",
42 | "@types/webpack": "^3.8.5",
43 | "@types/webpack-env": "^1.13.5",
44 | "autoprefixer": "^7.1.2",
45 | "babel-core": "^6.22.1",
46 | "babel-eslint": "^7.1.1",
47 | "babel-jest": "^21.0.2",
48 | "babel-loader": "^7.1.1",
49 | "babel-plugin-component": "^0.10.1",
50 | "babel-plugin-dynamic-import-node": "^1.2.0",
51 | "babel-plugin-import": "^1.7.0",
52 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
53 | "babel-plugin-transform-runtime": "^6.22.0",
54 | "babel-preset-env": "^1.3.2",
55 | "babel-preset-stage-2": "^6.22.0",
56 | "babel-register": "^6.22.0",
57 | "chalk": "^2.0.1",
58 | "copy-webpack-plugin": "^4.0.1",
59 | "cross-spawn": "^5.0.1",
60 | "css-loader": "^0.28.0",
61 | "eslint": "^3.19.0",
62 | "eslint-config-standard": "^10.2.1",
63 | "eslint-friendly-formatter": "^3.0.0",
64 | "eslint-loader": "^1.7.1",
65 | "eslint-plugin-html": "^3.0.0",
66 | "eslint-plugin-import": "^2.7.0",
67 | "eslint-plugin-node": "^5.2.0",
68 | "eslint-plugin-promise": "^3.4.0",
69 | "eslint-plugin-standard": "^3.0.1",
70 | "eslint-plugin-vue": "^4.2.2",
71 | "eventsource-polyfill": "^0.9.6",
72 | "extract-text-webpack-plugin": "^3.0.0",
73 | "faker": "^4.1.0",
74 | "file-loader": "^1.1.4",
75 | "friendly-errors-webpack-plugin": "^1.6.1",
76 | "html-webpack-plugin": "^2.30.1",
77 | "jest": "^21.2.0",
78 | "jest-serializer-vue": "^0.3.0",
79 | "json-server": "^0.12.1",
80 | "lodash": "^4.17.4",
81 | "mockjs": "^1.0.1-beta3",
82 | "nightwatch": "^0.9.12",
83 | "node-notifier": "^5.1.2",
84 | "node-sass": "^4.7.1",
85 | "nodemon": "^1.14.12",
86 | "optimize-css-assets-webpack-plugin": "^3.2.0",
87 | "ora": "^1.2.0",
88 | "portfinder": "^1.0.13",
89 | "postcss-import": "^11.0.0",
90 | "postcss-loader": "^2.0.8",
91 | "resolve-url-loader": "^2.2.0",
92 | "rimraf": "^2.6.0",
93 | "sass-loader": "^6.0.6",
94 | "selenium-server": "^3.0.1",
95 | "semver": "^5.3.0",
96 | "shelljs": "^0.7.6",
97 | "ts-loader": "^3.1.1",
98 | "tslint": "^5.8.0",
99 | "tslint-config-standard": "^7.0.0",
100 | "tslint-loader": "^3.5.3",
101 | "typescript": "^2.6.1",
102 | "url-loader": "^0.5.8",
103 | "vue-class-component": "^6.2.0",
104 | "vue-jest": "^1.0.2",
105 | "vue-loader": "^13.3.0",
106 | "vue-property-decorator": "^6.0.0",
107 | "vue-style-loader": "^3.0.1",
108 | "vue-template-compiler": "^2.5.2",
109 | "webpack": "^3.6.0",
110 | "webpack-bundle-analyzer": "^2.9.0",
111 | "webpack-dev-server": "^2.9.1",
112 | "webpack-merge": "^4.1.0"
113 | },
114 | "engines": {
115 | "node": ">= 4.0.0",
116 | "npm": ">= 3.0.0"
117 | },
118 | "browserslist": [
119 | "> 1%",
120 | "last 2 versions",
121 | "not ie <= 8"
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
24 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qidaizhe11/element-pro/588bc474ba82f130fc521f997785558242a648d2/src/assets/logo.png
--------------------------------------------------------------------------------
/src/common/menu.ts:
--------------------------------------------------------------------------------
1 | import { isUrl } from '../utils/utils'
2 |
3 | const anticon = (name: string) => {
4 | return `anticon anticon-${name}`
5 | }
6 |
7 | const menuData = [
8 | {
9 | name: 'dashboard',
10 | icon: anticon('dashboard'),
11 | path: 'dashboard',
12 | children: [
13 | {
14 | name: '分析页',
15 | path: 'analysis'
16 | },
17 | {
18 | name: '监控页',
19 | path: 'monitor'
20 | },
21 | {
22 | name: '工作台',
23 | path: 'workplace'
24 | // hideInBreadcrumb: true,
25 | // hideInMenu: true,
26 | // authority: 'admin'
27 | }
28 | ]
29 | },
30 | {
31 | name: '表单页',
32 | icon: anticon('form'),
33 | path: 'form',
34 | children: [
35 | {
36 | name: '基础表单',
37 | path: 'basic-form'
38 | },
39 | {
40 | name: '分步表单',
41 | path: 'step-form'
42 | }
43 | // {
44 | // name: '高级表单',
45 | // authority: 'admin',
46 | // path: 'advanced-form'
47 | // }
48 | ]
49 | },
50 | {
51 | name: '列表页',
52 | icon: anticon('table'),
53 | path: 'list',
54 | children: [
55 | {
56 | name: '查询表格',
57 | path: 'table-list'
58 | }
59 | // {
60 | // name: '标准列表',
61 | // path: 'basic-list'
62 | // },
63 | // {
64 | // name: '卡片列表',
65 | // path: 'card-list'
66 | // },
67 | // {
68 | // name: '搜索列表',
69 | // path: 'search',
70 | // children: [
71 | // {
72 | // name: '搜索列表(文章)',
73 | // path: 'articles'
74 | // },
75 | // {
76 | // name: '搜索列表(项目)',
77 | // path: 'projects'
78 | // },
79 | // {
80 | // name: '搜索列表(应用)',
81 | // path: 'applications'
82 | // }
83 | // ]
84 | // }
85 | ]
86 | },
87 | {
88 | name: '详情页',
89 | icon: anticon('profile'),
90 | path: 'profile',
91 | children: [
92 | {
93 | name: '基础详情页',
94 | path: 'basic'
95 | },
96 | {
97 | name: '高级详情页',
98 | path: 'advanced',
99 | authority: 'admin'
100 | }
101 | ]
102 | },
103 | {
104 | name: '结果页',
105 | icon: anticon('check-circle-o'),
106 | path: 'result',
107 | children: [
108 | {
109 | name: '成功',
110 | path: 'success'
111 | },
112 | {
113 | name: '失败',
114 | path: 'fail'
115 | }
116 | ]
117 | },
118 | {
119 | name: '异常页',
120 | icon: anticon('warning'),
121 | path: 'exception',
122 | children: [
123 | {
124 | name: '403',
125 | path: '403'
126 | },
127 | {
128 | name: '404',
129 | path: '404'
130 | },
131 | {
132 | name: '500',
133 | path: '500'
134 | },
135 | {
136 | name: '触发异常',
137 | path: 'trigger',
138 | hideInMenu: true
139 | }
140 | ]
141 | },
142 | {
143 | name: '账户',
144 | icon: anticon('user'),
145 | path: 'user',
146 | authority: 'guest',
147 | children: [
148 | {
149 | name: '登录',
150 | path: 'login'
151 | },
152 | {
153 | name: '注册',
154 | path: 'register'
155 | },
156 | {
157 | name: '注册结果',
158 | path: 'register-result'
159 | }
160 | ]
161 | }
162 | ]
163 |
164 | function formatter(data: any[], parentPath = '/', parentAuthority?: boolean) {
165 | return data.map(item => {
166 | let { path } = item
167 | if (!isUrl(path)) {
168 | path = parentPath + item.path
169 | }
170 | const result = {
171 | ...item,
172 | path,
173 | authority: item.authority || parentAuthority
174 | }
175 | if (item.children) {
176 | result.children = formatter(
177 | item.children,
178 | `${parentPath}${item.path}/`,
179 | item.authority
180 | )
181 | }
182 | return result
183 | })
184 | }
185 |
186 | export const getMenuData = () => formatter(menuData)
187 |
--------------------------------------------------------------------------------
/src/components/ActiveChart/ActiveChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 |
15 |
16 |
{{[...activeData].sort()[activeData.length - 1].y + 200}} 亿元
17 |
{{[...activeData].sort()[Math.floor(activeData.length / 2)].y}} 亿元
18 |
19 |
20 |
21 |
22 | 00:00
23 | {{activeData[Math.floor(activeData.length / 2)].x}}
24 | {{activeData[activeData.length - 1].x}}
25 |
26 |
27 |
28 |
29 |
30 |
73 |
74 |
107 |
--------------------------------------------------------------------------------
/src/components/ActiveChart/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import ActiveChart from './ActiveChart.vue'
3 |
4 | export { ActiveChart }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('ActiveChart', ActiveChart)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/AntIcon/AntIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/src/components/AntIcon/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import AntIcon from './AntIcon.vue'
3 |
4 | export { AntIcon }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('AntIcon', AntIcon)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Authorized/Authorized.vue:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/src/components/Authorized/CheckPermissions.ts:
--------------------------------------------------------------------------------
1 | import { VNode } from 'vue'
2 | // import PromiseRender from './PromiseRender';
3 | import { CURRENT } from './index'
4 |
5 | function isPromise(obj: any) {
6 | return (
7 | !!obj &&
8 | (typeof obj === 'object' || typeof obj === 'function') &&
9 | typeof obj.then === 'function'
10 | )
11 | }
12 |
13 | const hasPermission = (
14 | authority: string | string[] | Promise | Function,
15 | currentAuthority: string
16 | ) => {
17 | // 没有判定权限.默认查看所有
18 | // Retirement authority, return target;
19 | if (!authority) {
20 | return true
21 | }
22 | // 数组处理
23 | if (Array.isArray(authority)) {
24 | if (authority.indexOf(currentAuthority) >= 0) {
25 | return true
26 | }
27 | return false
28 | }
29 |
30 | // string 处理
31 | if (typeof authority === 'string') {
32 | if (authority === currentAuthority) {
33 | return true
34 | }
35 | return false
36 | }
37 |
38 | // Promise 处理
39 | // if (isPromise(authority)) {
40 | // return
41 | // }
42 |
43 | // Function 处理
44 | if (typeof authority === 'function') {
45 | try {
46 | const bool = authority(currentAuthority)
47 | if (bool) {
48 | return true
49 | }
50 | return false
51 | } catch (error) {
52 | throw error
53 | }
54 | }
55 | throw new Error('unsupported parameters')
56 | }
57 |
58 | /**
59 | * 通用权限检查方法
60 | * Common check permissions method
61 | * @param { 权限判定 Permission judgment type string |array | Promise | Function } authority
62 | * @param { 你的权限 Your permission description type:string} currentAuthority
63 | * @param { 通过的组件 Passing components } target
64 | * @param { 未通过的组件 no pass components } Exception
65 | */
66 | const checkPermissions = (
67 | authority: string | string[] | Promise | Function,
68 | currentAuthority: string,
69 | target: VNode | null,
70 | Exception: VNode | null
71 | ) => {
72 | if (hasPermission(authority, currentAuthority)) {
73 | return target
74 | }
75 | return Exception
76 | }
77 |
78 | const pass = (authority: string | string[] | Promise | Function) => {
79 | return hasPermission(authority, CURRENT)
80 | }
81 |
82 | export { checkPermissions, hasPermission, pass }
83 |
84 | const check = (
85 | authority: string | string[] | Promise | Function,
86 | target: VNode | null,
87 | Exception: VNode | null
88 | ) => {
89 | return checkPermissions(authority, CURRENT, target, Exception)
90 | }
91 |
92 | export default check
93 |
--------------------------------------------------------------------------------
/src/components/Authorized/index.ts:
--------------------------------------------------------------------------------
1 | import Component from 'vue-class-component'
2 | import Authorized from './Authorized.vue'
3 | import { hasPermission } from './CheckPermissions'
4 |
5 | let CURRENT: string = 'NULL'
6 |
7 | /**
8 | * use authority or getAuthority
9 | * @param {string|()=>String} currentAuthority
10 | */
11 | const renderAuthorize = (currentAuthority: string | any) => {
12 | if (currentAuthority) {
13 | if (currentAuthority.constructor.name === 'Function') {
14 | CURRENT = currentAuthority()
15 | }
16 | if (currentAuthority.constructor.name === 'String') {
17 | CURRENT = currentAuthority
18 | }
19 | } else {
20 | CURRENT = 'NULL'
21 | }
22 | return Authorized
23 | }
24 |
25 | export { CURRENT, hasPermission }
26 | export default renderAuthorize
27 |
--------------------------------------------------------------------------------
/src/components/Avatar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
53 |
54 |
107 |
108 |
--------------------------------------------------------------------------------
/src/components/Charts/Bar/Bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{title}}
4 |
5 |
6 |
7 |
8 |
9 |
148 |
--------------------------------------------------------------------------------
/src/components/Charts/Bar/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Bar from './Bar.vue'
3 |
4 | export { Bar }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Bar', Bar)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/ChartCard/ChartCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
28 |
29 |
30 |
60 |
61 |
138 |
139 |
--------------------------------------------------------------------------------
/src/components/Charts/ChartCard/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import ChartCard from './ChartCard.vue'
3 |
4 | export { ChartCard }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('ChartCard', ChartCard)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/Field/Field.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{label}}
4 |
5 |
6 |
7 |
8 |
17 |
18 |
36 |
37 |
--------------------------------------------------------------------------------
/src/components/Charts/Field/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Field from './Field.vue'
3 |
4 | export { Field }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Field', Field)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/Gauge/Gauge.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
13 |
14 |
128 |
--------------------------------------------------------------------------------
/src/components/Charts/Gauge/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Gauge from './Gauge.vue'
3 |
4 | export { Gauge }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Gauge', Gauge)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniArea/MiniArea.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
113 |
114 |
120 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniArea/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import MiniArea from './MiniArea.vue'
3 |
4 | export { MiniArea }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('MiniArea', MiniArea)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniBar/MiniBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
89 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniBar/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import MiniBar from './MiniBar.vue'
3 |
4 | export { MiniBar }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('MiniBar', MiniBar)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniProgress/MiniProgress.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
43 |
44 |
81 |
--------------------------------------------------------------------------------
/src/components/Charts/MiniProgress/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import MiniProgress from './MiniProgress.vue'
3 |
4 | export { MiniProgress }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('MiniProgress', MiniProgress)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/Pie/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Pie from './Pie.vue'
3 |
4 | export { Pie }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Pie', Pie)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/TagCloud/TagCloud.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
15 |
16 |
17 |
93 |
94 |
103 |
--------------------------------------------------------------------------------
/src/components/Charts/TagCloud/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import TagCloud from './TagCloud.vue'
3 |
4 | export { TagCloud }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('TagCloud', TagCloud)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/TimelineChart/TimelineChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{title}}
4 |
5 |
6 |
7 |
8 |
9 |
133 |
--------------------------------------------------------------------------------
/src/components/Charts/TimelineChart/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import TimelineChart from './TimelineChart.vue'
3 |
4 | export { TimelineChart }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('TimelineChart', TimelineChart)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/Trend/Trend.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
29 |
30 |
62 |
63 |
--------------------------------------------------------------------------------
/src/components/Charts/Trend/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Trend from './Trend.vue'
3 |
4 | export { Trend }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Trend', Trend)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/WaterWave/WaterWave.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
15 | {{title}}
16 |
{{percent}}%
17 |
18 |
19 |
20 |
21 |
90 |
91 |
115 |
--------------------------------------------------------------------------------
/src/components/Charts/WaterWave/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import WaterWave from './WaterWave.vue'
3 |
4 | export { WaterWave }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('WaterWave', WaterWave)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Charts/autoHeight.d.ts:
--------------------------------------------------------------------------------
1 | declare var autoHeight: any
2 |
3 | export default autoHeight
4 |
--------------------------------------------------------------------------------
/src/components/Charts/autoHeight.js:
--------------------------------------------------------------------------------
1 | function computeHeight(node) {
2 | const totalHeight = parseInt(getComputedStyle(node).height || '', 10)
3 | const padding =
4 | parseInt(getComputedStyle(node).paddingTop || '', 10) +
5 | parseInt(getComputedStyle(node).paddingBottom || '', 10)
6 | return totalHeight - padding
7 | }
8 |
9 | function getAutoHeight(n) {
10 | if (!n) {
11 | return 0
12 | }
13 |
14 | let node = n
15 |
16 | console.log('getAutoHeight.')
17 |
18 | let height = computeHeight(node.$el)
19 |
20 | while (!height) {
21 | node = node.$parent
22 | if (node) {
23 | height = computeHeight(node.$el)
24 | } else {
25 | break
26 | }
27 | }
28 |
29 | return height
30 | }
31 |
32 | export default {
33 | data() {
34 | return {
35 | computeHeight: 0
36 | }
37 | },
38 | methods: {},
39 | mounted() {
40 | this.$nextTick(() => {
41 | if (!this.height) {
42 | const h = getAutoHeight(this)
43 | this.computeHeight = h
44 | }
45 | })
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/Charts/default.ts:
--------------------------------------------------------------------------------
1 | const COLOR_PLATE_8 = [
2 | '#1890FF',
3 | '#2FC25B',
4 | '#FACC14',
5 | '#223273',
6 | '#8543E0',
7 | '#13C2C2',
8 | '#3436C7',
9 | '#F04864'
10 | ]
11 | const COLOR_PLATE_16 = [
12 | '#1890FF',
13 | '#41D9C7',
14 | '#2FC25B',
15 | '#FACC14',
16 | '#E6965C',
17 | '#223273',
18 | '#7564CC',
19 | '#8543E0',
20 | '#5C8EE6',
21 | '#13C2C2',
22 | '#5CA3E6',
23 | '#3436C7',
24 | '#B381E6',
25 | '#F04864',
26 | '#D598D9'
27 | ]
28 | const COLOR_PLATE_24 = [
29 | '#1890FF',
30 | '#66B5FF',
31 | '#41D9C7',
32 | '#2FC25B',
33 | '#6EDB8F',
34 | '#9AE65C',
35 | '#FACC14',
36 | '#E6965C',
37 | '#57AD71',
38 | '#223273',
39 | '#738AE6',
40 | '#7564CC',
41 | '#8543E0',
42 | '#A877ED',
43 | '#5C8EE6',
44 | '#13C2C2',
45 | '#70E0E0',
46 | '#5CA3E6',
47 | '#3436C7',
48 | '#8082FF',
49 | '#DD81E6',
50 | '#F04864',
51 | '#FA7D92',
52 | '#D598D9'
53 | ]
54 | const COLOR_PIE = [
55 | '#1890FF',
56 | '#13C2C2',
57 | '#2FC25B',
58 | '#FACC14',
59 | '#F04864',
60 | '#8543E0',
61 | '#3436C7',
62 | '#223273'
63 | ]
64 | const COLOR_PIE_16 = [
65 | '#1890FF',
66 | '#73C9E6',
67 | '#13C2C2',
68 | '#6CD9B3',
69 | '#2FC25B',
70 | '#9DD96C',
71 | '#FACC14',
72 | '#E6965C',
73 | '#F04864',
74 | '#D66BCA',
75 | '#8543E0',
76 | '#8E77ED',
77 | '#3436C7',
78 | '#737EE6',
79 | '#223273',
80 | '#7EA2E6'
81 | ]
82 |
83 | export const theme = {
84 | colors: COLOR_PLATE_8,
85 | colors16: COLOR_PLATE_16,
86 | colors24: COLOR_PLATE_24,
87 | colorsPie: COLOR_PIE,
88 | colorsPie16: COLOR_PIE_16
89 | }
90 |
91 | export const colorSpan = (color: string) => {
92 | return ``
93 | }
94 |
95 | export const defaultOptions = {
96 | color: COLOR_PLATE_8,
97 | tooltip: {
98 | textStyle: {
99 | fontSize: 12
100 | },
101 | padding: [10, 5]
102 | },
103 | grid: {
104 | left: 0,
105 | right: 0,
106 | top: 10,
107 | bottom: 5,
108 | containLabel: true
109 | },
110 | xAxis: {
111 | splitLine: {
112 | show: false
113 | },
114 | axisLabel: {
115 | color: '#797979',
116 | margin: 12
117 | },
118 | axisTick: {
119 | show: true,
120 | alignWithLabel: true
121 | },
122 | axisLine: {
123 | show: true,
124 | lineStyle: {
125 | color: '#bebebe'
126 | }
127 | },
128 | axisPointer: {
129 | status: 'hide'
130 | }
131 | },
132 | miniXAxis: {
133 | axisLabel: {
134 | show: false
135 | },
136 | axisTick: {
137 | show: false
138 | },
139 | axisLine: {
140 | show: false
141 | },
142 | axisPointer: {
143 | status: 'hide'
144 | }
145 | },
146 | yAxis: {
147 | axisLabel: {
148 | color: '#797979'
149 | },
150 | axisTick: {
151 | show: false
152 | },
153 | axisLine: {
154 | show: false
155 | },
156 | splitLine: {
157 | show: true,
158 | lineStyle: {
159 | type: 'dotted'
160 | }
161 | }
162 | },
163 | series: {
164 | line: {
165 | itemStyle: {
166 | shadowBlur: 15,
167 | borderWidth: 1,
168 | borderColor: '#fff'
169 | },
170 | getItemStyle: (color: string) => {
171 | return {
172 | color: color,
173 | shadowColor: color,
174 | shadowBlur: 15,
175 | borderWidth: 1,
176 | borderColor: '#fff'
177 | }
178 | },
179 | showSymbol: false,
180 | symbol: 'circle',
181 | symbolSize: 2
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/components/Charts/index.ts:
--------------------------------------------------------------------------------
1 | import * as numeral from 'numeral'
2 |
3 | import ChartCard from './ChartCard'
4 | import Field from './Field'
5 | import Trend from './Trend'
6 | import MiniArea from './MiniArea'
7 | import MiniBar from './MiniBar'
8 | import MiniProgress from './MiniProgress'
9 | import Bar from './Bar'
10 | import Pie from './Pie'
11 | import TimelineChart from './TimelineChart'
12 | import Gauge from './Gauge'
13 | import TagCloud from './TagCloud'
14 | import WaterWave from './WaterWave'
15 |
16 | const yuan = (val: number) => `¥ ${numeral(val).format('0,0')}`
17 |
18 | export {
19 | yuan,
20 | ChartCard,
21 | Field,
22 | Trend,
23 | MiniArea,
24 | MiniBar,
25 | MiniProgress,
26 | Bar,
27 | Pie,
28 | TimelineChart,
29 | Gauge,
30 | TagCloud,
31 | WaterWave
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/CountDown/CountDown.vue:
--------------------------------------------------------------------------------
1 |
2 |
104 |
--------------------------------------------------------------------------------
/src/components/CountDown/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import CountDown from './CountDown.vue'
3 |
4 | export { CountDown }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('CountDown', CountDown)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Description/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Description from '../DescriptionList/Description.vue'
3 |
4 | export { Description }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Description', Description)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/Description.vue:
--------------------------------------------------------------------------------
1 |
47 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/DescriptionList.vue:
--------------------------------------------------------------------------------
1 |
2 |
66 |
67 |
141 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import DescriptionList from './DescriptionList.vue'
3 |
4 | export { DescriptionList }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('DescriptionList', DescriptionList)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/DescriptionList/responsive.ts:
--------------------------------------------------------------------------------
1 | interface Responsive {
2 | [key: string]: any
3 | }
4 |
5 | const responsive: Responsive = {
6 | 1: { xs: 24 },
7 | 2: { xs: 24, sm: 12 },
8 | 3: { xs: 24, sm: 12, md: 8 },
9 | 4: { xs: 24, sm: 12, md: 6 }
10 | }
11 |
12 | export default responsive
13 |
--------------------------------------------------------------------------------
/src/components/Divider/Divider.vue:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
179 |
--------------------------------------------------------------------------------
/src/components/Divider/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Divider from './Divider.vue'
3 |
4 | export { Divider }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Divider', Divider)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/EChart/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
146 |
--------------------------------------------------------------------------------
/src/components/Exception/Exception.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
14 |
{{title || config[pageType].title}}
15 |
{{desc || config[pageType].desc}}
16 |
17 |
21 |
25 | 返回首页
26 |
27 |
28 |
29 |
30 |
31 |
32 |
63 |
64 |
154 |
155 |
--------------------------------------------------------------------------------
/src/components/Exception/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Exception from './Exception.vue'
3 |
4 | function install(vue: typeof Vue) {
5 | vue.component('Exception', Exception)
6 | }
7 |
8 | export { Exception }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Exception/typeConfig.ts:
--------------------------------------------------------------------------------
1 | const config = {
2 | 403: {
3 | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
4 | title: '403',
5 | desc: '抱歉,你无权访问该页面'
6 | },
7 | 404: {
8 | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg',
9 | title: '404',
10 | desc: '抱歉,你访问的页面不存在'
11 | },
12 | 500: {
13 | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
14 | title: '500',
15 | desc: '抱歉,服务器出错了'
16 | }
17 | }
18 |
19 | export default config
20 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/GlobalFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
36 |
37 |
68 |
--------------------------------------------------------------------------------
/src/components/GlobalFooter/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import GlobalFooter from './GlobalFooter.vue'
3 |
4 | export { GlobalFooter }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('GlobalFooter', GlobalFooter)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/HeaderSearch/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
78 |
79 |
110 |
111 |
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
13 |
Ecosystem
14 |
20 |
21 |
22 |
23 |
34 |
35 |
36 |
52 |
--------------------------------------------------------------------------------
/src/components/List/ListItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
47 |
48 |
--------------------------------------------------------------------------------
/src/components/List/ListItemMeta.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{title}}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{description}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
55 |
56 |
--------------------------------------------------------------------------------
/src/components/List/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 暂无数据
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/List/style/index.scss:
--------------------------------------------------------------------------------
1 |
2 | $list-cls: 'ep-list';
3 |
4 | $text-color: rgba(0, 0, 0, 0.65);
5 | $text-color-secondary: rgba(0, 0, 0, 0.45);
6 | $primary-color: #1890ff;
7 | $font-size-base: 14px;
8 | $border-color-split: #e8e8e8;
9 |
10 | .#{$list-cls} {
11 | position: relative;
12 | * {
13 | outline: none;
14 | }
15 |
16 | &-empty-text {
17 | padding: 16px;
18 | text-align: center;
19 | }
20 |
21 | &-item {
22 | align-items: center;
23 | display: flex;
24 | padding-top: 12px;
25 | padding-bottom: 12px;
26 | border-bottom: 1px solid $border-color-split;
27 |
28 | &-meta {
29 | align-items: flex-start;
30 | display: flex;
31 | flex: 1;
32 | font-size: 0;
33 | &-avatar {
34 | flex: 0;
35 | margin-right: 16px;
36 | }
37 | &-content {
38 | flex: 1 0;
39 | }
40 | &-title {
41 | color: $text-color;
42 | margin-bottom: 4px;
43 | font-size: $font-size-base;
44 | line-height: 22px;
45 | > a {
46 | color: $text-color;
47 | transition: all 0.3s;
48 | &:hover {
49 | color: $primary-color;
50 | }
51 | }
52 | }
53 | &-description {
54 | color: $text-color-secondary;
55 | font-size: $font-size-base;
56 | line-height: 22px;
57 | }
58 | }
59 | &-content {
60 | display: flex;
61 | flex: 1;
62 | justify-content: flex-end;
63 | }
64 | &-content-single {
65 | justify-content: flex-start;
66 | }
67 | &-action {
68 | font-size: 0;
69 | flex: 0 0 auto;
70 | margin-left: 48px;
71 | padding: 0;
72 | list-style: none;
73 | & > li {
74 | display: inline-block;
75 | color: $text-color-secondary;
76 | cursor: pointer;
77 | padding: 0 8px;
78 | position: relative;
79 | font-size: $font-size-base;
80 | line-height: 22px;
81 | text-align: center;
82 | }
83 | & > li:first-child {
84 | padding-left: 0;
85 | }
86 | &-split {
87 | background-color: $border-color-split;
88 | margin-top: -7px;
89 | position: absolute;
90 | top: 50%;
91 | right: 0;
92 | width: 1px;
93 | height: 14px;
94 | }
95 | }
96 | &-main {
97 | display: flex;
98 | flex: 1;
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/components/Login/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
115 |
116 |
154 |
--------------------------------------------------------------------------------
/src/components/Login/LoginItem.ts:
--------------------------------------------------------------------------------
1 | import Vue, { VNode } from 'vue'
2 | import { Form, FormItem, Button, Row, Col } from 'element-ui'
3 | import Component from 'vue-class-component'
4 | import { Inject } from 'vue-property-decorator'
5 |
6 | import map from './map'
7 |
8 | Vue.use(Form)
9 | Vue.use(FormItem)
10 | Vue.use(Button)
11 | Vue.use(Row)
12 | Vue.use(Col)
13 |
14 | function generator(context: {
15 | defaultProps: any
16 | defaultRules: any
17 | type: string
18 | component: string
19 | }) {
20 | @Component({
21 | props: {
22 | prop: {
23 | type: String,
24 | default: ''
25 | }
26 | }
27 | })
28 | class LoginItem extends Vue {
29 | @Inject('login') login: any
30 |
31 | prop: string
32 |
33 | count: number = 0
34 |
35 | created() {
36 | this.login.updateForm(this.prop)
37 | }
38 |
39 | mounted() {
40 | const parent: any = this.$parent
41 | const parentName = parent && parent.name ? parent.name : ''
42 | this.login.updateActive(this.prop, parentName)
43 | }
44 |
45 | onInput(value: any) {
46 | this.login.updateForm(this.prop, value)
47 | }
48 |
49 | render(h: any): VNode {
50 | const { defaultProps, defaultRules, type, component } = context
51 | return h(
52 | 'el-form-item',
53 | {
54 | props: {
55 | rules: defaultRules,
56 | prop: this.prop
57 | }
58 | },
59 | [
60 | h(component, {
61 | props: {
62 | ...defaultProps,
63 | ...this.$attrs
64 | },
65 | on: {
66 | input: this.onInput
67 | }
68 | })
69 | ]
70 | )
71 | }
72 | }
73 |
74 | return LoginItem
75 | }
76 |
77 | const LoginItem: any = {}
78 |
79 | Object.keys(map).forEach(item => {
80 | LoginItem[item] = generator({
81 | defaultProps: map[item].props,
82 | defaultRules: map[item].rules,
83 | type: item,
84 | component: map[item].component
85 | })
86 | })
87 |
88 | export default LoginItem
89 |
--------------------------------------------------------------------------------
/src/components/Login/LoginSubmit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 |
16 |
25 |
26 |
32 |
--------------------------------------------------------------------------------
/src/components/Login/LoginTab.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
26 |
--------------------------------------------------------------------------------
/src/components/Login/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Login from './Login.vue'
3 | import LoginItem from './LoginItem'
4 |
5 | export { Login, LoginItem }
6 |
7 | function install(vue: typeof Vue) {
8 | vue.component('Login', Login)
9 | // Object.keys(LoginItem).forEach(item => {
10 | // vue.component(item, LoginItem[item])
11 | // })
12 | }
13 |
14 | export default {
15 | install: install as PluginFunction
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/Login/map.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { Input } from 'element-ui'
3 | import AntIcon from '../AntIcon'
4 |
5 | Vue.use(Input)
6 | Vue.use(AntIcon)
7 |
8 | const anticon = (name: string) => {
9 | return `anticon anticon-${name}`
10 | }
11 |
12 | const map: any = {
13 | UserName: {
14 | component: 'el-input',
15 | props: {
16 | size: 'large',
17 | // prefix: ,
18 | prefixIcon: anticon('user'),
19 | placeholder: 'admin'
20 | },
21 | rules: [
22 | {
23 | required: true,
24 | message: 'Please enter username!'
25 | }
26 | ]
27 | },
28 | Password: {
29 | component: 'el-input',
30 | props: {
31 | size: 'large',
32 | // prefix: ,
33 | prefixIcon: anticon('lock'),
34 | type: 'password',
35 | placeholder: '888888'
36 | },
37 | rules: [
38 | {
39 | required: true,
40 | message: 'Please enter password!'
41 | }
42 | ]
43 | },
44 | Mobile: {
45 | component: 'el-input',
46 | props: {
47 | size: 'large',
48 | // prefix: ,
49 | prefixIcon: anticon('mobile'),
50 | placeholder: 'mobile number'
51 | },
52 | rules: [
53 | {
54 | required: true,
55 | message: 'Please enter mobile number!'
56 | },
57 | {
58 | pattern: /^1\d{10}$/,
59 | message: 'Wrong mobile number format!'
60 | }
61 | ]
62 | },
63 | Captcha: {
64 | component: 'el-input',
65 | props: {
66 | size: 'large',
67 | // prefix: ,
68 | prefixIcon: anticon('mail'),
69 | placeholder: 'captcha'
70 | },
71 | rules: [
72 | {
73 | required: true,
74 | message: 'Please enter Captcha!'
75 | }
76 | ]
77 | }
78 | }
79 |
80 | export default map
81 |
--------------------------------------------------------------------------------
/src/components/LoginSubmit/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import LoginSubmit from '../Login/LoginSubmit.vue'
3 |
4 | export { LoginSubmit }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('LoginSubmit', LoginSubmit)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/LoginTab/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import LoginTab from '../Login/LoginTab.vue'
3 |
4 | export { LoginTab }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('LoginTab', LoginTab)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/NoticeList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{item.title}}
10 |
11 |
12 |
{{item.description}}
13 |
{{item.datetime}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
47 |
48 |
90 |
--------------------------------------------------------------------------------
/src/components/NoticeIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
90 |
91 |
101 |
102 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/NumberInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{subTitle}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{total && total}}
22 |
23 |
24 | {{suffix && suffix}}
25 |
26 |
27 |
28 |
29 | {{subTotal}}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
61 |
62 |
132 |
--------------------------------------------------------------------------------
/src/components/NumberInfo/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import NumberInfo from './NumberInfo.vue'
3 |
4 | export { NumberInfo }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('NumberInfo', NumberInfo)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/PageHeader/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import PageHeader from './PageHeader.vue'
3 |
4 | export { PageHeader }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('PageHeader', PageHeader)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Result/Result.vue:
--------------------------------------------------------------------------------
1 |
63 |
64 |
117 |
--------------------------------------------------------------------------------
/src/components/Result/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import Result from './Result.vue'
3 |
4 | export { Result }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('Result', Result)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/StandardTable/StandardTable.vue:
--------------------------------------------------------------------------------
1 |
99 |
100 |
103 |
--------------------------------------------------------------------------------
/src/components/StandardTable/index.ts:
--------------------------------------------------------------------------------
1 | import Vue, { PluginFunction } from 'vue'
2 | import StandardTable from './StandardTable.vue'
3 |
4 | export { StandardTable }
5 |
6 | function install(vue: typeof Vue) {
7 | vue.component('StandardTable', StandardTable)
8 | }
9 |
10 | export default {
11 | install: install as PluginFunction
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/utils/pathTools.ts:
--------------------------------------------------------------------------------
1 | // /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
2 | export function urlToList(url: string) {
3 | const urllist = url.split('/').filter(i => i)
4 | return urllist.map((urlItem, index) => {
5 | return `/${urllist.slice(0, index + 1).join('/')}`
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/src/layouts/BlankLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/src/layouts/PageHeaderLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
46 |
47 |
60 |
--------------------------------------------------------------------------------
/src/layouts/UserLayout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
Vue 版 Ant Design Pro(基于 Element-ui 实现)
12 |
13 |
14 |
15 |
16 |
17 |
18 | Copyright
19 |
2018 Daizhe
20 |
21 |
22 |
23 |
24 |
25 |
26 |
65 |
66 |
128 |
--------------------------------------------------------------------------------
/src/main.scss:
--------------------------------------------------------------------------------
1 | html, body, #app {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | text-rendering: optimizeLegibility;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | font-size: 14px;
10 | color: rgba(0, 0, 0, 0.65);
11 | background-color: #fff;
12 | }
13 |
14 | .globalSpin {
15 | width: 100%;
16 | margin: 40px 0 !important;
17 | }
18 |
19 | a {
20 | outline: none;
21 | text-decoration: none;
22 |
23 | &:hover {
24 | outline: 0;
25 | text-decoration: none;
26 | }
27 | }
28 |
29 | h1, h2, h3, h4, h5, h6 {
30 | color: rgba(0, 0, 0, 0.85);
31 | font-weight: 500;
32 | }
33 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | // import ElementUI from 'element-ui'
5 | import 'moment/locale/zh-cn'
6 |
7 | import App from './App.vue'
8 | import router from './router'
9 | import store from './store'
10 |
11 | import './router/authority'
12 |
13 | import 'bootstrap/dist/css/bootstrap-reboot.css'
14 | import './theme/element-theme.scss'
15 | import './theme/anticon.scss'
16 | import './main.scss'
17 |
18 | Vue.config.productionTip = false
19 |
20 | // Vue.use(ElementUI)
21 |
22 | /* eslint-disable no-new */
23 | /* tslint:disable no-unused-expression */
24 | new Vue({
25 | el: '#app',
26 | router,
27 | store,
28 | template: '',
29 | components: { App }
30 | })
31 |
--------------------------------------------------------------------------------
/src/router/authority.ts:
--------------------------------------------------------------------------------
1 | import * as NProgress from 'nprogress'
2 | import 'nprogress/nprogress.css'
3 |
4 | import Authorized from 'utils/Authorized'
5 |
6 | import router from './index'
7 |
8 | const authorized: any = Authorized
9 |
10 | router.beforeEach((to, from, next) => {
11 | NProgress.start()
12 | const isUser = to.meta.isUserLayout
13 | if (isUser) {
14 | next()
15 | NProgress.done()
16 | return
17 | }
18 | if (!authorized.pass(['admin', 'user'])) {
19 | next({
20 | path: '/user/login',
21 | replace: true
22 | })
23 | NProgress.done()
24 | return
25 | }
26 | if (authorized.pass(to.meta.authority)) {
27 | next()
28 | } else {
29 | next({
30 | path: '/exception/403',
31 | replace: true
32 | })
33 | NProgress.done()
34 | }
35 | })
36 |
37 | router.afterEach(() => {
38 | NProgress.done() // finish progress bar
39 | })
40 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | import { getRouterData } from '../common/router'
5 |
6 | import { getRoutes } from '../utils/utils'
7 |
8 | Vue.use(Router)
9 |
10 | const routerData: any = getRouterData()
11 | const UserLayout = routerData['/user'].component
12 | const BasicLayout = routerData['/'].component
13 |
14 | const basicLayoutRoutes = getRoutes('/', routerData)
15 | const userLayoutRoutes = getRoutes('/user', routerData)
16 |
17 | userLayoutRoutes.forEach(item => {
18 | item.meta.isUserLayout = true
19 | })
20 |
21 | const routes = [
22 | {
23 | path: '/user',
24 | name: 'user',
25 | component: UserLayout,
26 | children: userLayoutRoutes
27 | },
28 | {
29 | path: '/',
30 | component: BasicLayout,
31 | redirect: '/dashboard/analysis',
32 | children: basicLayoutRoutes
33 | }
34 | ]
35 |
36 | const router = new Router({
37 | routes
38 | })
39 |
40 | export default router
41 |
--------------------------------------------------------------------------------
/src/services/api.ts:
--------------------------------------------------------------------------------
1 | import request from 'utils/request'
2 |
3 | export function queryRule(params: any) {
4 | return request.get('/api/rule', {
5 | params: params
6 | })
7 | }
8 |
9 | export function removeRule(params: any) {
10 | return request.post('/api/rule', {
11 | method: 'delete',
12 | ...params
13 | })
14 | }
15 |
16 | export function addRule(params: any) {
17 | return request.post('/api/rule', {
18 | method: 'post',
19 | ...params
20 | })
21 | }
22 |
23 | export function fakeAccountLogin(params: any) {
24 | return request.post('/api/login/account', params)
25 | }
26 |
27 | export function fakeSubmitForm(params: any) {
28 | return request.post('/api/forms', params)
29 | }
30 |
31 | export function fakeChartData() {
32 | return request.get('/api/fake_chart_data')
33 | }
34 |
35 | export function queryTags() {
36 | return request.get('/api/tags')
37 | }
38 |
39 | export function queryBasicProfile() {
40 | return request.get('/api/profile/basic')
41 | }
42 |
43 | export function queryAdvancedProfile() {
44 | return request.get('/api/profile/advanced')
45 | }
46 |
--------------------------------------------------------------------------------
/src/services/user.ts:
--------------------------------------------------------------------------------
1 | import request from 'utils/request'
2 |
3 | export function queryCurrent() {
4 | return request.get('/api/currentUser')
5 | }
6 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | import modules from './modules'
5 |
6 | Vue.use(Vuex)
7 |
8 | export default new Vuex.Store({
9 | modules
10 | })
11 |
--------------------------------------------------------------------------------
/src/store/modules/chart.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import { fakeChartData } from 'services/api'
4 |
5 | export interface State {
6 | chartData: {
7 | visitData: any[]
8 | visitData2: any[]
9 | salesData: any[]
10 | searchData: any[]
11 | offlineData: any[]
12 | offlineChartData: any[]
13 | salesTypeData: any[]
14 | salesTypeDataOnline: any[]
15 | salesTypeDataOffline: any[]
16 | radarData: any[]
17 | }
18 | loading: boolean
19 | }
20 |
21 | const state: State = {
22 | chartData: {
23 | visitData: [],
24 | visitData2: [],
25 | salesData: [],
26 | searchData: [],
27 | offlineData: [],
28 | offlineChartData: [],
29 | salesTypeData: [],
30 | salesTypeDataOnline: [],
31 | salesTypeDataOffline: [],
32 | radarData: []
33 | },
34 | loading: false
35 | }
36 |
37 | const mutations = {
38 | save(state: State, payload: any) {
39 | state.chartData = {
40 | ...state.chartData,
41 | ...payload
42 | }
43 | },
44 | setLoading(state: State, loading: boolean) {
45 | state.loading = loading
46 | }
47 | }
48 |
49 | const actions = {
50 | async fetch(context: { commit: Commit }) {
51 | try {
52 | context.commit('setLoading', true)
53 | const response = await fakeChartData()
54 | const data = response.data
55 | context.commit('setLoading', false)
56 | context.commit('save', data)
57 | return data
58 | } catch (error) {
59 | context.commit('setLoading', false)
60 | throw error
61 | }
62 | },
63 | async fetchSalesData(context: { commit: Commit }) {
64 | try {
65 | context.commit('setLoading', true)
66 | const response = await fakeChartData()
67 | const data = response.data
68 | context.commit('setLoading', false)
69 | context.commit('save', {
70 | salesData: data.salesData
71 | })
72 | return data
73 | } catch (error) {
74 | context.commit('setLoading', false)
75 | throw error
76 | }
77 | }
78 | }
79 |
80 | export default {
81 | namespaced: true,
82 | state,
83 | mutations,
84 | actions
85 | }
86 |
--------------------------------------------------------------------------------
/src/store/modules/form.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { Commit, Dispatch } from 'vuex'
3 | import { Message } from 'element-ui'
4 |
5 | import router from 'router'
6 |
7 | import { fakeSubmitForm } from 'services/api'
8 |
9 | interface Step {
10 | payAccount: string
11 | receiverAccount: string
12 | receiverName: string
13 | amount: string
14 | }
15 |
16 | export interface State {
17 | step: Step
18 | }
19 |
20 | const state: State = {
21 | step: {
22 | payAccount: 'ant-design@alipay.com',
23 | receiverAccount: 'test@example.com',
24 | receiverName: 'Alex',
25 | amount: '500'
26 | }
27 | }
28 |
29 | const mutations = {
30 | saveStepFormData(state: State, payload: Step) {
31 | state.step = {
32 | ...state.step,
33 | ...payload
34 | }
35 | }
36 | }
37 |
38 | const actions = {
39 | async submitRegularForm(context: { commit: Commit }, payload: any) {
40 | await fakeSubmitForm(payload)
41 | Message.success('提交成功')
42 | },
43 | async submitStepForm(context: { commit: Commit }, payload: any) {
44 | await fakeSubmitForm(payload)
45 | context.commit('saveStepFormData', payload)
46 | router.push('/form/step-form/result')
47 | }
48 | }
49 |
50 | export default {
51 | namespaced: true,
52 | state,
53 | mutations,
54 | actions
55 | }
56 |
--------------------------------------------------------------------------------
/src/store/modules/index.ts:
--------------------------------------------------------------------------------
1 | const files = require.context('.', false, /\.ts$/)
2 | const modules: any = {}
3 |
4 | files.keys().forEach((key: string) => {
5 | if (key === './index.ts') return
6 | modules[key.replace(/(\.\/|\.ts)/g, '')] = files(key).default
7 | })
8 |
9 | export default modules
10 |
--------------------------------------------------------------------------------
/src/store/modules/login.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import router from 'router'
4 |
5 | import { fakeAccountLogin } from 'services/api'
6 | import { setAuthority } from 'utils/authority'
7 | import { reloadAuthorized } from 'utils/Authorized'
8 |
9 | export interface State {
10 | status: string | boolean | undefined
11 | type: string | undefined
12 | }
13 |
14 | const state: State = {
15 | status: undefined,
16 | type: undefined
17 | }
18 |
19 | const mutations = {
20 | changeLoginStatus(state: State, payload: any) {
21 | payload = payload || {}
22 | setAuthority(payload.currentAuthority)
23 | state.status = payload.status
24 | state.type = payload.type
25 | }
26 | }
27 |
28 | const actions = {
29 | async login(context: { commit: Commit }, payload: any) {
30 | const response = await fakeAccountLogin(payload)
31 | const data = response ? response.data : {}
32 | context.commit('changeLoginStatus', data)
33 | // Login successfully
34 | if (data.status === 'ok') {
35 | reloadAuthorized()
36 |
37 | // 这里是重定向的,重定向到 url 的 redirect 参数所示地址
38 | const urlParams = new URL(window.location.href)
39 |
40 | const redirect = urlParams.searchParams.get('redirect')
41 | // Remove the parameters in the url
42 | if (redirect) {
43 | urlParams.searchParams.delete('redirect')
44 | window.history.replaceState(null, 'redirect', urlParams.href)
45 | router.push(redirect)
46 | } else {
47 | router.push('/')
48 | }
49 | }
50 | return data
51 | },
52 | async logout(context: { commit: Commit }) {
53 | try {
54 | // get location pathname
55 | const urlParams = new URL(window.location.href)
56 | const pathname = router.currentRoute.path
57 | // add the parameters in the url
58 | urlParams.searchParams.set('redirect', pathname)
59 | window.history.replaceState(null, 'login', urlParams.href)
60 | } finally {
61 | context.commit('changeLoginStatus', {
62 | status: false,
63 | currentAuthority: 'guest'
64 | })
65 | reloadAuthorized()
66 | router.push('/user/login')
67 | }
68 | }
69 | }
70 |
71 | export default {
72 | namespaced: true,
73 | state,
74 | mutations,
75 | actions
76 | }
77 |
--------------------------------------------------------------------------------
/src/store/modules/monitor.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import { queryTags } from 'services/api'
4 |
5 | export interface State {
6 | tags: any[]
7 | }
8 |
9 | const state: State = {
10 | tags: []
11 | }
12 |
13 | const mutations = {
14 | saveTags(state: State, tags: any[]) {
15 | state.tags = tags
16 | }
17 | }
18 |
19 | const actions = {
20 | async fetchTags(context: { commit: Commit }) {
21 | try {
22 | const response = await queryTags()
23 | const data = response.data.list || []
24 | context.commit('saveTags', data)
25 | return data
26 | } catch (error) {
27 | throw error
28 | }
29 | }
30 | }
31 |
32 | export default {
33 | namespaced: true,
34 | state,
35 | mutations,
36 | actions
37 | }
38 |
--------------------------------------------------------------------------------
/src/store/modules/profile.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import { queryBasicProfile, queryAdvancedProfile } from 'services/api'
4 |
5 | export interface State {
6 | basicGoods: any[]
7 | basicProgress: any[]
8 | advancedOperation1: any[]
9 | advancedOperation2: any[]
10 | advancedOperation3: any[]
11 | }
12 |
13 | const state: State = {
14 | basicGoods: [],
15 | basicProgress: [],
16 | advancedOperation1: [],
17 | advancedOperation2: [],
18 | advancedOperation3: []
19 | }
20 |
21 | const mutations = {
22 | saveBasic(state: State, payload: any) {
23 | state.basicGoods = payload.basicGoods
24 | state.basicProgress = payload.basicProgress
25 | },
26 | saveAdvanced(state: State, payload: any) {
27 | state.advancedOperation1 = payload.advancedOperation1
28 | state.advancedOperation2 = payload.advancedOperation2
29 | state.advancedOperation3 = payload.advancedOperation3
30 | }
31 | }
32 |
33 | const actions = {
34 | async fetchBasic(context: { commit: Commit }) {
35 | try {
36 | const response = await queryBasicProfile()
37 | const data = response.data
38 | context.commit('saveBasic', data)
39 | return data
40 | } catch (error) {
41 | throw error
42 | }
43 | },
44 | async fetchAdvanced(context: { commit: Commit }) {
45 | try {
46 | const response = await queryAdvancedProfile()
47 | const data = response.data
48 | context.commit('saveAdvanced', data)
49 | return data
50 | } catch (error) {
51 | throw error
52 | }
53 | }
54 | }
55 |
56 | export default {
57 | namespaced: true,
58 | state,
59 | mutations,
60 | actions
61 | }
62 |
--------------------------------------------------------------------------------
/src/store/modules/rule.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import { queryRule, addRule, removeRule } from 'services/api'
4 |
5 | export interface State {
6 | list: any[]
7 | pagination: any
8 | }
9 |
10 | const state: State = {
11 | list: [],
12 | pagination: {}
13 | }
14 |
15 | const mutations = {
16 | save(state: State, payload: any) {
17 | state.list = payload.list || []
18 | state.pagination = payload.pagination || {}
19 | }
20 | }
21 |
22 | const actions = {
23 | async fetch(context: { commit: Commit }, payload: any) {
24 | try {
25 | const response = await queryRule(payload)
26 | const data = response.data
27 | context.commit('save', data)
28 | return data
29 | } catch (error) {
30 | throw error
31 | }
32 | },
33 | async add(context: { commit: Commit }, payload: any) {
34 | try {
35 | const response = await addRule(payload)
36 | const data = response.data
37 | context.commit('save', data)
38 | return data
39 | } catch (error) {
40 | throw error
41 | }
42 | },
43 | async remove(context: { commit: Commit }, payload: any) {
44 | try {
45 | const response = await removeRule(payload)
46 | const data = response.data
47 | context.commit('save', data)
48 | return data
49 | } catch (error) {
50 | throw error
51 | }
52 | }
53 | }
54 |
55 | export default {
56 | namespaced: true,
57 | state,
58 | mutations,
59 | actions
60 | }
61 |
--------------------------------------------------------------------------------
/src/store/modules/user.ts:
--------------------------------------------------------------------------------
1 | import { Commit, Dispatch } from 'vuex'
2 |
3 | import { queryCurrent } from 'services/user'
4 |
5 | export interface State {
6 | list: any[],
7 | currentUser: any
8 | }
9 |
10 | const state: State = {
11 | list: [],
12 | currentUser: {}
13 | }
14 |
15 | const mutations = {
16 | saveCurrentUser(state: State, payload: any) {
17 | state.currentUser = payload || {}
18 | }
19 | }
20 |
21 | const actions = {
22 | async fetchCurrent(context: { commit: Commit }) {
23 | const response = await queryCurrent()
24 | context.commit('saveCurrentUser', response.data)
25 | }
26 | }
27 |
28 | export default {
29 | namespaced: true,
30 | state,
31 | mutations,
32 | actions
33 | }
34 |
--------------------------------------------------------------------------------
/src/theme/element-theme.scss:
--------------------------------------------------------------------------------
1 |
2 | $--font-path: '~element-ui/lib/theme-chalk/fonts';
3 |
4 | @import "~element-ui/packages/theme-chalk/src/index";
5 |
6 | .el-dropdown-menu.el-popper[x-placement^="bottom"] {
7 | margin-top: 4px;
8 | }
9 |
10 | .el-autocomplete-suggestion.el-popper[x-placement^="bottom"] {
11 | margin-top: 6px;
12 | }
13 |
14 | .el-badge {
15 | .el-badge__content {
16 | box-sizing: content-box;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/Authorized.ts:
--------------------------------------------------------------------------------
1 | import RenderAuthorized from 'components/Authorized'
2 | import { getAuthority } from './authority'
3 |
4 | let Authorized = RenderAuthorized(getAuthority()) // eslint-disable-line
5 |
6 | // Reload the rights component
7 | const reloadAuthorized = () => {
8 | Authorized = RenderAuthorized(getAuthority())
9 | }
10 |
11 | export { reloadAuthorized }
12 | export default Authorized
13 |
--------------------------------------------------------------------------------
/src/utils/authority.ts:
--------------------------------------------------------------------------------
1 | // use localStorage to store the authority info, which might be sent from server in actual project.
2 | export function getAuthority() {
3 | return localStorage.getItem('antd-pro-authority') || 'admin'
4 | }
5 |
6 | export function setAuthority(authority: string) {
7 | return localStorage.setItem('antd-pro-authority', authority)
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/request.ts:
--------------------------------------------------------------------------------
1 |
2 | import axios, { AxiosResponse } from 'axios'
3 |
4 | const service = axios.create({
5 | baseURL: process.env.BASE_URL,
6 | withCredentials: true
7 | })
8 |
9 | service.interceptors.response.use(
10 | response => {
11 | return response
12 | },
13 | (error: any) => {
14 | console.log(
15 | 'fetch',
16 | error.config.url,
17 | error.config.params,
18 | 'error. error:',
19 | error
20 | )
21 | return Promise.reject(error)
22 | }
23 | )
24 |
25 | export default service
26 |
27 |
--------------------------------------------------------------------------------
/src/utils/utils.scss:
--------------------------------------------------------------------------------
1 | %clearfix {
2 | zoom: 1;
3 | &:before,
4 | &:after {
5 | content: " ";
6 | display: table;
7 | }
8 | &:after {
9 | clear: both;
10 | visibility: hidden;
11 | font-size: 0;
12 | height: 0;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/utils/utils.ts:
--------------------------------------------------------------------------------
1 | import * as moment from 'moment'
2 |
3 | export function fixedZero(val: number) {
4 | return val * 1 < 10 ? `0${val}` : val
5 | }
6 |
7 | export function getTimeDistance(type: string) {
8 | const now = new Date()
9 | const oneDay = 1000 * 60 * 60 * 24
10 |
11 | if (type === 'today') {
12 | now.setHours(0)
13 | now.setMinutes(0)
14 | now.setSeconds(0)
15 | return [moment(now), moment(now.getTime() + (oneDay - 1000))]
16 | }
17 |
18 | if (type === 'week') {
19 | let day = now.getDay()
20 | now.setHours(0)
21 | now.setMinutes(0)
22 | now.setSeconds(0)
23 |
24 | if (day === 0) {
25 | day = 6
26 | } else {
27 | day -= 1
28 | }
29 |
30 | const beginTime = now.getTime() - day * oneDay
31 |
32 | return [moment(beginTime), moment(beginTime + (7 * oneDay - 1000))]
33 | }
34 |
35 | if (type === 'month') {
36 | const year = now.getFullYear()
37 | const month = now.getMonth()
38 | const nextDate = moment(now).add(1, 'months')
39 | const nextYear = nextDate.year()
40 | const nextMonth = nextDate.month()
41 |
42 | return [
43 | moment(`${year}-${fixedZero(month + 1)}-01 00:00:00`),
44 | moment(
45 | moment(
46 | `${nextYear}-${fixedZero(nextMonth + 1)}-01 00:00:00`
47 | ).valueOf() - 1000
48 | )
49 | ]
50 | }
51 |
52 | if (type === 'year') {
53 | const year = now.getFullYear()
54 |
55 | return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)]
56 | }
57 | }
58 |
59 | export function digitUppercase(n: number) {
60 | const fraction = ['角', '分']
61 | const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
62 | const unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']]
63 | let num = Math.abs(n)
64 | let s = ''
65 | fraction.forEach((item, index) => {
66 | s += (digit[Math.floor(num * 10 * 10 ** index) % 10] + item).replace(
67 | /零./,
68 | ''
69 | )
70 | })
71 | s = s || '整'
72 | num = Math.floor(num)
73 | for (let i = 0; i < unit[0].length && num > 0; i += 1) {
74 | let p = ''
75 | for (let j = 0; j < unit[1].length && num > 0; j += 1) {
76 | p = digit[num % 10] + unit[1][j] + p
77 | num = Math.floor(num / 10)
78 | }
79 | s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s
80 | }
81 |
82 | return s
83 | .replace(/(零.)*零元/, '元')
84 | .replace(/(零.)+/g, '零')
85 | .replace(/^整$/, '零元整')
86 | }
87 |
88 | /**
89 | * Get router routing configuration
90 | * { path:{name,...param}}=>Array<{name,path ...param}>
91 | * @param {string} path
92 | * @param {routerData} routerData
93 | */
94 | export function getRoutes(path: string, routerData: any) {
95 | let routes = Object.keys(routerData).filter(
96 | routePath => routePath.indexOf(path) === 0 && routePath !== path
97 | )
98 | // Replace path to '' eg. path='user' /user/name => name
99 | routes = routes.map(item => item.replace(path, ''))
100 | // Conversion and stitching parameters
101 | return routes.map(item => {
102 | return {
103 | ...routerData[`${path}${item}`],
104 | key: `${path}${item}`,
105 | path: `${path}${item}`
106 | }
107 | })
108 | }
109 |
110 | /* eslint no-useless-escape:0 */
111 | const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/g
112 |
113 | export function isUrl(path: string) {
114 | return reg.test(path)
115 | }
116 |
--------------------------------------------------------------------------------
/src/views/Dashboard/Workplace/index.vue:
--------------------------------------------------------------------------------
1 |
2 | Workplace
3 |
4 |
5 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/views/Exception/403.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
19 |
20 |
--------------------------------------------------------------------------------
/src/views/Exception/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
19 |
20 |
--------------------------------------------------------------------------------
/src/views/Exception/500.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
19 |
20 |
--------------------------------------------------------------------------------
/src/views/Forms/StepForm/Step1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
14 |
19 |
20 |
21 |
22 |
26 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
45 |
49 |
50 |
51 |
55 |
59 | ¥
60 |
61 |
62 |
63 |
67 | 下一步
68 |
69 |
70 |
71 |
74 |
75 |
说明
76 |
转账到支付宝账户
77 |
78 | 如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。
79 |
80 |
转账到银行卡
81 |
82 | 如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。如果需要,这里可以放一些关于产品的常见问题说明。
83 |
84 |
85 |
86 |
87 |
88 |
163 |
164 |
166 |
--------------------------------------------------------------------------------
/src/views/Forms/StepForm/Step2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
16 |
19 | {{formData.payAccount}}
20 |
21 |
24 | {{formData.receiverAccount}}
25 |
26 |
29 | {{formData.receiverName}}
30 |
31 |
34 | {{formData.amount}}
35 | ({{digitUppercase(formData.amount)}})
36 |
37 |
40 |
44 |
50 |
51 |
52 |
55 |
60 | 提交
61 |
62 |
66 | 上一步
67 |
68 |
69 |
70 |
71 |
72 |
73 |
134 |
135 |
138 |
--------------------------------------------------------------------------------
/src/views/Forms/StepForm/Step3.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 | 再转一笔
14 |
15 | 查看账单
16 |
17 |
21 |
22 |
26 | 付款账户:
27 |
28 |
31 | {{formData.payAccount}}
32 |
33 |
34 |
35 |
39 | 收款账户:
40 |
41 |
44 | {{formData.receiverAccount}}
45 |
46 |
47 |
48 |
52 | 收款人姓名:
53 |
54 |
57 | {{formData.receiverName}}
58 |
59 |
60 |
61 |
65 | 转账金额:
66 |
67 |
70 | {{formData.amount}} 元
71 |
72 |
73 |
74 |
75 |
76 |
77 |
105 |
106 |
109 |
110 |
--------------------------------------------------------------------------------
/src/views/Forms/StepForm/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
53 |
54 |
60 |
--------------------------------------------------------------------------------
/src/views/Forms/StepForm/style.scss:
--------------------------------------------------------------------------------
1 | @import '~theme/theme.scss';
2 |
3 | @mixin money {
4 | font-family: 'Helvetica Neue', sans-serif;
5 | font-weight: 500;
6 | font-size: 20px;
7 | line-height: 14px;
8 | }
9 |
10 | .step-form {
11 | margin: 40px auto 0;
12 | max-width: 500px;
13 | .el-form-item {
14 | &__content {
15 | width: 80%;
16 | .el-input__prefix {
17 | left: 10px;
18 | }
19 | }
20 | &__label {
21 | margin-bottom: 0;
22 | &::after {
23 | content: ':';
24 | margin: 0 0 0 2px;
25 | position: relative;
26 | top: -0.5px;
27 | }
28 | }
29 | }
30 |
31 | .money {
32 | @include money;
33 | }
34 |
35 | .uppercase {
36 | font-size: 12px;
37 | }
38 | }
39 |
40 | .step-desc {
41 | padding: 0 56px;
42 | color: $text-color-secondary;
43 | h3 {
44 | font-size: 16px;
45 | margin: 0 0 12px 0;
46 | color: $text-color-secondary;
47 | line-height: 32px;
48 | }
49 | h4 {
50 | margin: 0 0 4px 0;
51 | color: $text-color-secondary;
52 | font-size: 14px;
53 | line-height: 22px;
54 | }
55 | p {
56 | margin-top: 0;
57 | margin-bottom: 12px;
58 | line-height: 22px;
59 | }
60 | }
61 |
62 | .step-result {
63 | margin: 0 auto;
64 | max-width: 560px;
65 | padding: 24px 0 8px;
66 |
67 | .money {
68 | @include money;
69 | }
70 | }
71 |
72 | .step-information {
73 | line-height: 22px;
74 | .el-row:not(:last-child) {
75 | margin-bottom: 24px;
76 | }
77 | .label {
78 | color: $heading-color;
79 | text-align: right;
80 | padding-right: 8px;
81 | }
82 | }
83 |
84 | @media screen and (max-width: $screen-md) {
85 | .step-desc {
86 | padding: 0;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/views/List/BasicList/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
32 |
--------------------------------------------------------------------------------
/src/views/Result/Error/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
21 | 您提交的内容有如下错误:
22 |
23 |
35 |
47 |
48 |
49 | 返回修改
50 |
51 |
52 |
53 |
54 |
55 |
56 |
75 |
76 |
79 |
--------------------------------------------------------------------------------
/src/views/User/Login/Message.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
22 |
23 |
--------------------------------------------------------------------------------
/src/views/User/Login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
16 |
17 |
18 |
22 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
39 | 自动登录
40 |
41 |
忘记密码
42 |
43 | 登录
44 |
45 | 其他登录方式
46 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
121 |
122 |
153 |
154 |
--------------------------------------------------------------------------------
/src/vue-shim.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import Vue from "vue";
3 | export default Vue;
4 | }
5 |
6 | declare module '*.png'
7 |
8 | declare module 'el-table-wrapper'
9 | declare module 'vue-antd-ui'
10 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qidaizhe11/element-pro/588bc474ba82f130fc521f997785558242a648d2/static/.gitkeep
--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // The assertion name is the filename.
3 | // Example usage:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // For more information on custom assertions see:
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 |
10 | exports.assertion = function (selector, count) {
11 | this.message = 'Testing if element <' + selector + '> has count: ' + count
12 | this.expected = count
13 | this.pass = function (val) {
14 | return val === this.expected
15 | }
16 | this.value = function (res) {
17 | return res.value
18 | }
19 | this.command = function (cb) {
20 | var self = this
21 | return this.api.execute(function (selectorToCount) {
22 | return document.querySelectorAll(selectorToCount).length
23 | }, [selector], function (res) {
24 | cb.call(self, res)
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | var config = require('../../config')
3 |
4 | // http://nightwatchjs.org/gettingstarted#settings-file
5 | module.exports = {
6 | src_folders: ['test/e2e/specs'],
7 | output_folder: 'test/e2e/reports',
8 | custom_assertions_path: ['test/e2e/custom-assertions'],
9 |
10 | selenium: {
11 | start_process: true,
12 | server_path: require('selenium-server').path,
13 | host: '127.0.0.1',
14 | port: 4444,
15 | cli_args: {
16 | 'webdriver.chrome.driver': require('chromedriver').path
17 | }
18 | },
19 |
20 | test_settings: {
21 | default: {
22 | selenium_port: 4444,
23 | selenium_host: 'localhost',
24 | silent: true,
25 | globals: {
26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 | }
28 | },
29 |
30 | chrome: {
31 | desiredCapabilities: {
32 | browserName: 'chrome',
33 | javascriptEnabled: true,
34 | acceptSslCerts: true
35 | }
36 | },
37 |
38 | firefox: {
39 | desiredCapabilities: {
40 | browserName: 'firefox',
41 | javascriptEnabled: true,
42 | acceptSslCerts: true
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 |
4 | const webpack = require('webpack')
5 | const DevServer = require('webpack-dev-server')
6 |
7 | const webpackConfig = require('../../build/webpack.prod.conf')
8 | const devConfigPromise = require('../../build/webpack.dev.conf')
9 |
10 | let server
11 |
12 | devConfigPromise.then(devConfig => {
13 | const devServerOptions = devConfig.devServer
14 | const compiler = webpack(webpackConfig)
15 | server = new DevServer(compiler, devServerOptions)
16 | const port = devServerOptions.port
17 | const host = devServerOptions.host
18 | return server.listen(port, host)
19 | })
20 | .then(() => {
21 | // 2. run the nightwatch test suite against it
22 | // to run in additional browsers:
23 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
24 | // 2. add it to the --env flag below
25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
26 | // For more information on Nightwatch's config file, see
27 | // http://nightwatchjs.org/guide#settings-file
28 | let opts = process.argv.slice(2)
29 | if (opts.indexOf('--config') === -1) {
30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
31 | }
32 | if (opts.indexOf('--env') === -1) {
33 | opts = opts.concat(['--env', 'chrome'])
34 | }
35 |
36 | const spawn = require('cross-spawn')
37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
38 |
39 | runner.on('exit', function (code) {
40 | server.close()
41 | process.exit(code)
42 | })
43 |
44 | runner.on('error', function (err) {
45 | server.close()
46 | throw err
47 | })
48 | })
49 |
--------------------------------------------------------------------------------
/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': function (browser) {
6 | // automatically uses dev Server port from /config.index.js
7 | // default: http://localhost:8080
8 | // see nightwatch.conf.js
9 | const devServer = browser.globals.devServerURL
10 |
11 | browser
12 | .url(devServer)
13 | .waitForElementVisible('#app', 5000)
14 | .assert.elementPresent('.hello')
15 | .assert.containsText('h1', 'Welcome to Your Vue.js App')
16 | .assert.elementCount('img', 1)
17 | .end()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | },
5 | "globals": {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/unit/jest.conf.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | rootDir: path.resolve(__dirname, '../../'),
5 | moduleFileExtensions: [
6 | 'js',
7 | 'json',
8 | 'vue'
9 | ],
10 | moduleNameMapper: {
11 | '^@/(.*)$': '/src/$1'
12 | },
13 | transform: {
14 | '^.+\\.js$': '/node_modules/babel-jest',
15 | '.*\\.(vue)$': '/node_modules/vue-jest'
16 | },
17 | testPathIgnorePatterns: [
18 | '/test/e2e'
19 | ],
20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'],
21 | setupFiles: ['/test/unit/setup'],
22 | mapCoverage: true,
23 | coverageDirectory: '/test/unit/coverage',
24 | collectCoverageFrom: [
25 | 'src/**/*.{js,vue}',
26 | '!src/main.js',
27 | '!src/router/index.js',
28 | '!**/node_modules/**'
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/test/unit/setup.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
--------------------------------------------------------------------------------
/test/unit/specs/HelloWorld.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import HelloWorld from '@/components/HelloWorld'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('should render correct contents', () => {
6 | const Constructor = Vue.extend(HelloWorld)
7 | const vm = new Constructor().$mount()
8 | expect(vm.$el.querySelector('.hello h1').textContent)
9 | .toEqual('Welcome to Your Vue.js App')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // tsconfig.json
2 | {
3 | "compilerOptions": {
4 | // 与 Vue 的浏览器支持保持一致
5 | "target": "es2017",
6 | // 这可以对 `this` 上的数据属性进行更严格的推断
7 | "strict": true,
8 | // 如果使用 webpack 2+ 或 rollup,可以利用 tree-shake:
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 |
12 | "experimentalDecorators": true,
13 | "baseUrl": "src"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint-config-standard",
3 | "globals": {
4 | "require": true
5 | },
6 | "rules": {
7 | "no-consecutive-blank-lines": false,
8 | "space-before-function-paren": [true, "never"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------