├── .stylelintignore
├── .browserslistrc
├── .gitattributes
├── src
├── components
│ ├── Chart
│ │ ├── README.md
│ │ ├── MiniBar.vue
│ │ ├── Bar.vue
│ │ ├── MiniArea.vue
│ │ ├── MiniSearchArea.vue
│ │ ├── RankList.vue
│ │ ├── SalePercentCard.vue
│ │ ├── MiniProgress.vue
│ │ ├── ChartCard.vue
│ │ ├── HotSearchCard.vue
│ │ ├── TimelineChart.vue
│ │ └── Pie.vue
│ ├── Layout
│ │ ├── index.js
│ │ ├── ViewLayout.vue
│ │ ├── App
│ │ │ ├── LayoutFooter.vue
│ │ │ ├── LayoutSider.vue
│ │ │ ├── LayoutHeader.vue
│ │ │ └── Layout.vue
│ │ └── AppLayout.vue
│ ├── Trend
│ │ └── index.vue
│ ├── Logo
│ │ └── index.vue
│ ├── ValidateCode
│ │ └── index.vue
│ ├── DetailList
│ │ └── DetailList.vue
│ ├── Setting
│ │ ├── tools.js
│ │ └── index.vue
│ ├── ModifyPassword
│ │ └── index.vue
│ ├── Actions
│ │ └── index.vue
│ └── Menu
│ │ └── index.vue
├── assets
│ ├── images
│ │ ├── 404.png
│ │ ├── 500.png
│ │ ├── excel.png
│ │ ├── logo.png
│ │ ├── login_bg.png
│ │ ├── macbook-gold.png
│ │ └── excel.svg
│ └── styles
│ │ ├── mixins.scss
│ │ ├── variable.scss
│ │ ├── index.scss
│ │ ├── base.scss
│ │ └── common.scss
├── plugins
│ ├── d3.js
│ ├── v-charts.js
│ ├── viser.js
│ ├── index.js
│ ├── vue-ls.js
│ └── ant-design-vue.js
├── views
│ ├── dashboard
│ │ ├── park.png
│ │ ├── monitor.vue
│ │ ├── components
│ │ │ ├── CustomTab.vue
│ │ │ ├── OfflineData.vue
│ │ │ └── NumberInfo.vue
│ │ ├── track.vue
│ │ ├── config.js
│ │ ├── vcharts.vue
│ │ └── analysis.vue
│ ├── d3
│ │ ├── line.vue
│ │ ├── bar.vue
│ │ └── components
│ │ │ └── Bar
│ │ │ ├── Transition.vue
│ │ │ ├── TransitionOther.vue
│ │ │ └── BarChart.vue
│ ├── g2
│ │ ├── line.vue
│ │ └── bar.vue
│ ├── error
│ │ ├── 404.vue
│ │ └── 500.vue
│ ├── table
│ │ ├── components
│ │ │ └── AccountModal.vue
│ │ └── table.vue
│ └── auth
│ │ └── login.vue
├── mixins
│ ├── index.js
│ ├── device.js
│ ├── queryForm.js
│ ├── rangePicker.js
│ ├── table.js
│ └── appStore.js
├── api
│ ├── auth.js
│ └── form.js
├── store
│ ├── modules
│ │ ├── permission.js
│ │ ├── user.js
│ │ └── app.js
│ ├── config.js
│ ├── index.js
│ ├── mutation-types.js
│ └── initStore.js
├── utils
│ ├── index.js
│ ├── time.js
│ ├── optimize.js
│ ├── device.js
│ └── request.js
├── main.js
├── App.vue
├── filters
│ └── index.js
└── router
│ ├── index.js
│ └── routes.js
├── public
├── favicon.ico
└── index.html
├── tests
├── unit
│ ├── .eslintrc.js
│ └── example.spec.js
└── e2e
│ ├── specs
│ └── test.js
│ └── custom-assertions
│ └── elementCount.js
├── postcss.config.js
├── .prettierrc
├── .editorconfig
├── deploy.sh
├── babel.config.js
├── .travis.yml
├── lint-staged.config.js
├── .gitignore
├── .stylelintrc.js
├── .eslintrc.js
├── server
├── package.json
├── db.json
└── index.js
├── jest.config.js
├── vue.config.js
├── README.zh-CN.md
├── package.json
└── README.md
/.stylelintignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/dist
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.less linguist-language=Vue
2 |
--------------------------------------------------------------------------------
/src/components/Chart/README.md:
--------------------------------------------------------------------------------
1 | https://github.com/ant-design/pro-blocks
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/404.png
--------------------------------------------------------------------------------
/src/assets/images/500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/500.png
--------------------------------------------------------------------------------
/src/plugins/d3.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import * as d3 from 'd3'
3 |
4 | Vue.prototype.$d3 = d3
5 |
--------------------------------------------------------------------------------
/src/plugins/v-charts.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VCharts from 'v-charts'
3 | Vue.use(VCharts)
4 |
--------------------------------------------------------------------------------
/src/plugins/viser.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Viser from 'viser-vue'
3 |
4 | Vue.use(Viser)
5 |
--------------------------------------------------------------------------------
/src/assets/images/excel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/excel.png
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/views/dashboard/park.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/views/dashboard/park.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "tabWidth": 2,
5 | "useTabs": true
6 | }
7 |
--------------------------------------------------------------------------------
/src/assets/images/login_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/login_bg.png
--------------------------------------------------------------------------------
/src/assets/styles/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin hover-focus {
2 | &:hover,
3 | &:focus {
4 | @content;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/assets/images/macbook-gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luichooy/vue-antd-pro/HEAD/src/assets/images/macbook-gold.png
--------------------------------------------------------------------------------
/src/assets/styles/variable.scss:
--------------------------------------------------------------------------------
1 | $spaces: (
2 | 1: 4px,
3 | 2: 8px,
4 | 3: 16px,
5 | 4: 32px,
6 | 5: 48px
7 | );
8 |
--------------------------------------------------------------------------------
/src/views/dashboard/monitor.vue:
--------------------------------------------------------------------------------
1 |
2 | 监控页
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | import './ant-design-vue'
2 | import './vue-ls'
3 | import './viser'
4 | import './v-charts'
5 | import './d3'
6 |
--------------------------------------------------------------------------------
/src/components/Layout/index.js:
--------------------------------------------------------------------------------
1 | export { default as ViewLayout } from './ViewLayout'
2 |
3 | export { default as AppLayout } from './AppLayout'
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/src/assets/styles/index.scss:
--------------------------------------------------------------------------------
1 | //@import "iconfont.css";
2 | @import 'variable.scss';
3 | @import 'mixins.scss';
4 | @import 'base.scss';
5 | @import 'common.scss';
6 |
--------------------------------------------------------------------------------
/src/views/d3/line.vue:
--------------------------------------------------------------------------------
1 |
2 | 123
3 |
4 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/src/views/g2/line.vue:
--------------------------------------------------------------------------------
1 |
2 | g2-line
3 |
4 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # 确保脚本抛出遇到的错误
4 | set -e
5 |
6 | # 项目打包
7 | yarn build --report
8 |
9 | # 删除服务器上的代码
10 | ssh root@192.0.0.0 "rm -rf /opt/app/vue-antd-pro/*"
11 |
12 | # 上传到服务器
13 | scp -r ./dist/* root@192.0.0.0:/opt/app/vue-antd-pro
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@vue/app'],
3 | plugins: [
4 | [
5 | 'import',
6 | {
7 | libraryName: 'ant-design-vue',
8 | libraryDirectory: 'es',
9 | style: true
10 | }
11 | ]
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js # 设置语言
2 |
3 | node_js: '10.16.3' # 设置语言版本
4 |
5 | cache:
6 | directories:
7 | - node_modules # 缓存依赖
8 |
9 | # start: build lifecycle
10 | install:
11 | - yarn
12 |
13 | script:
14 | - yarn build
15 |
16 | branches:
17 | only:
18 | - master
19 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.{js,jsx,vue}': ['yarn lint', 'git add'],
3 | '*.{css}': ['stylelint --fix', 'git add'],
4 | '*.{scss}': ['stylelint --syntax=scss --fix', 'git add'],
5 | '*.{vue}': ['stylelint --fix', 'git add'],
6 | '*.{json}': ['prettier --write', 'git add']
7 | }
8 |
--------------------------------------------------------------------------------
/src/mixins/index.js:
--------------------------------------------------------------------------------
1 | export { default as tableMixin } from './table'
2 |
3 | export { default as queryFormMixin } from './queryForm'
4 |
5 | export { default as appStoreMixin } from './appStore'
6 |
7 | export { default as rangePickerMixin } from './rangePicker'
8 |
9 | export { default as deviceMixin } from './device'
10 |
--------------------------------------------------------------------------------
/src/api/auth.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function login (params) {
4 | return request.get('/login')
5 | }
6 |
7 | export function logout () {
8 | return request.get('/logout')
9 | }
10 |
11 | export function modifyPassword (params) {
12 | return request.post('/modifyPassword', params)
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | /tests/e2e/reports/
6 | selenium-debug.log
7 |
8 | # local env files
9 | .env.local
10 | .env.*.local
11 |
12 | # Log files
13 | npm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 |
17 | # Editor directories and files
18 | .idea
19 | .vscode
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/src/store/modules/permission.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { MENUS } from '../mutation-types'
3 |
4 | const permission = {
5 | namespaced: true,
6 | state: {
7 | access: [],
8 | menus: []
9 | },
10 |
11 | mutations: {
12 | SET_MENUS (state, menus) {
13 | Vue.ss.set(MENUS, menus)
14 | state.menus = menus
15 | }
16 | }
17 | }
18 |
19 | export default permission
20 |
--------------------------------------------------------------------------------
/tests/unit/example.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import HelloWorld from '@/components/HelloWorld.vue'
3 |
4 | describe('HelloWorld.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(HelloWorld, {
8 | propsData: { msg }
9 | })
10 | expect(wrapper.text()).toMatch(msg)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'stylelint-config-standard',
3 | plugins: [],
4 | processors: [],
5 | // defaultSeverity: 'error', //指定应用到所有rules上的严格程度,可选值有‘warning','error'
6 | rules: {
7 | 'font-family-no-missing-generic-family-keyword': null,
8 | 'no-empty-source': null,
9 | 'no-eol-whitespace': null,
10 | // 解决 vue 深度选择器 ::v-deep
11 | 'selector-pseudo-element-no-unknown': null
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/mixins/device.js:
--------------------------------------------------------------------------------
1 | import { mapState } from 'vuex'
2 | import { DEVICE_TYPE } from '@/utils/device'
3 |
4 | export default {
5 | computed: {
6 | ...mapState('app', ['device']),
7 | isDesktop () {
8 | return this.device === DEVICE_TYPE.DESKTOP
9 | },
10 | isTablat () {
11 | return this.device === DEVICE_TYPE.TABLET
12 | },
13 | isMobile () {
14 | return this.device === DEVICE_TYPE.MOBILE
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': browser => {
6 | browser
7 | .url(process.env.VUE_DEV_SERVER_URL)
8 | .waitForElementVisible('#app', 5000)
9 | .assert.elementPresent('.hello')
10 | .assert.containsText('h1', 'Welcome to Your Vue.js App')
11 | .assert.elementCount('img', 1)
12 | .end()
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/mixins/queryForm.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data () {
3 | return {
4 | // 控制查询条件折叠
5 | formFold: true,
6 | formItemLayout: {
7 | labelCol: {
8 | md: { span: 6 },
9 | sm: { span: 4 }
10 | },
11 | wrapperCol: {
12 | md: { span: 18 },
13 | sm: { span: 20 }
14 | }
15 | }
16 | }
17 | },
18 | methods: {
19 | toggleFold () {
20 | this.formFold = !this.formFold
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/assets/styles/base.scss:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100vh;
3 | font-family: Chinese Quote, -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
4 | scroll-behavior: smooth;
5 |
6 | &.color-weak {
7 | filter: invert(80%);
8 | }
9 | }
10 |
11 | h1, h2, h3, h4, h5, h6, p, ul, ol {
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
16 | ul, ol {
17 | list-style: none;
18 | }
19 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: ['plugin:vue/essential', '@vue/standard'],
7 | rules: {
8 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
9 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
10 | 'no-trailing-spaces': 'off',
11 | semi: ['error', 'never'],
12 | 'no-tabs': 'off',
13 | 'template-curly-spacing': 'off'
14 | },
15 | parserOptions: {
16 | parser: 'babel-eslint'
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | import { Base64 } from 'js-base64'
2 |
3 | const salt1 = 'VUE-ANTD-PRO'
4 | const salt2 = 'FRONTEND'
5 |
6 | export function encryptpwd (pwd) {
7 | return Base64.encode(salt1 + pwd + salt2)
8 | }
9 |
10 | export function generateOpenKeys (keyPath) {
11 | const openKeys = []
12 | for (let i = 0, len = keyPath.length; i < len; i++) {
13 | let subMenu = ''
14 | for (let j = 0; j <= i; j++) {
15 | subMenu += '/' + keyPath[j]
16 | }
17 | openKeys.push(subMenu)
18 | }
19 |
20 | return openKeys
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/time.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 |
3 | export function timeFix () {
4 | const time = new Date()
5 | const hour = time.getHours()
6 | return hour < 9
7 | ? '早上好'
8 | : hour <= 11
9 | ? '上午好'
10 | : hour <= 13
11 | ? '中午好'
12 | : hour < 20
13 | ? '下午好'
14 | : '晚上好'
15 | }
16 |
17 | export function getCurrent (key) {
18 | const current = moment()
19 | return [
20 | current[key](current[key]()).startOf(key),
21 | current[key](current[key]()).endOf(key)
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "User json-server as backend API of vue-antd-pro ",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "json-server --watch index.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [
11 | "json-server",
12 | "backend",
13 | "API"
14 | ],
15 | "author": "luichooy@163.com",
16 | "license": "ISC",
17 | "dependencies": {
18 | "casual": "^1.6.2",
19 | "json-server": "^0.15.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router/index'
4 | import { store, initStore } from './store/index'
5 | import './plugins'
6 | import * as filters from './filters'
7 | import './assets/styles/index.scss'
8 |
9 | Vue.config.productionTip = false
10 |
11 | /* 定义全局过滤器 */
12 | Object.keys(filters).forEach(key => {
13 | Vue.filter(key, filters[key])
14 | })
15 |
16 | new Vue({
17 | router,
18 | store,
19 | render: h => h(App),
20 | created () {
21 | initStore()
22 | }
23 | }).$mount('#app')
24 |
--------------------------------------------------------------------------------
/src/views/d3/bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
--------------------------------------------------------------------------------
/src/plugins/vue-ls.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Storage from 'vue-ls'
3 |
4 | /*
5 | * 同时使用 loal 和 session
6 | * https://github.com/RobinCK/vue-ls/issues/83
7 | * */
8 |
9 | const SessionStorage = {
10 | install (Vue, options = {}) {
11 | const _options = Object.assign({}, options, {
12 | storage: options.storage || 'session',
13 | name: options.name || 'ss'
14 | })
15 |
16 | Storage.install(Vue, _options)
17 | }
18 | }
19 |
20 | Vue.use(Storage, { namespace: 'VAPRO__' })
21 | Vue.use(SessionStorage, { namespace: 'VAPRO__' })
22 |
--------------------------------------------------------------------------------
/src/components/Layout/ViewLayout.vue:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
--------------------------------------------------------------------------------
/src/utils/optimize.js:
--------------------------------------------------------------------------------
1 | export function throttle (fn, delay) {
2 | delay = delay || 300
3 | let prev = 0
4 |
5 | return function (args) {
6 | const context = this
7 | let now = Date.now()
8 | if (now - prev > delay) {
9 | fn.call(context, args)
10 | prev = now
11 | }
12 | }
13 | }
14 |
15 | export function debounce (fn, delay) {
16 | delay = delay || 300
17 | let timer
18 |
19 | return function (args) {
20 | const context = this
21 | if (timer) clearTimeout(timer)
22 |
23 | timer = setTimeout(() => {
24 | fn.call(context, args)
25 | }, delay)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-antd-pro
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/Layout/App/LayoutFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
17 |
18 |
30 |
--------------------------------------------------------------------------------
/src/mixins/rangePicker.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 |
3 | export default {
4 | data () {
5 | return {
6 | rangeDate: [moment().subtract(6, 'days'), moment()]
7 | }
8 | },
9 | watch: {
10 | rangeDate: {
11 | handler: function (momentArr) {
12 | this.form.startTime = momentArr[0].format('YYYY-MM-DD 00:00:00')
13 | this.form.endTime = momentArr[1].format('YYYY-MM-DD 23:59:59')
14 | },
15 | immediate: true
16 | }
17 | },
18 | methods: {
19 | handleResetForm () {
20 | this.form = {}
21 | this.rangeDate = [moment().subtract(6, 'days'), moment()]
22 | },
23 | moment
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/store/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | app: {
3 | menuTheme: 'dark', // theme for nav menu
4 | color: '#1890FF', // primary color of ant design
5 | layout: 'side', // nav menu position: sidemenu or topmenu
6 | contentWidth: 'fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu
7 | fixedHeader: true, // sticky header
8 | fixSiderbar: true, // sticky siderbar
9 | autoHideHeader: false, // auto hide header
10 | colorWeak: false,
11 | multiTab: false
12 | },
13 |
14 | user: {
15 | token: '',
16 | user: null,
17 | routes: null
18 | },
19 |
20 | permission: {
21 | menus: null
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/Chart/MiniBar.vue:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import createLogger from 'vuex/dist/logger'
4 |
5 | import app from './modules/app'
6 | import user from './modules/user'
7 | import permission from './modules/permission'
8 |
9 | export { default as initStore } from './initStore'
10 |
11 | Vue.use(Vuex)
12 |
13 | const debug = process.env.NODE_ENV !== 'production'
14 |
15 | export const store = new Vuex.Store({
16 | modules: {
17 | app,
18 | user,
19 | permission
20 | },
21 | state: {},
22 | mutations: {},
23 | actions: {},
24 | getters: {},
25 | strict: debug,
26 | plugins: debug ? [createLogger()] : []
27 | })
28 |
29 | export default store
30 |
--------------------------------------------------------------------------------
/src/api/form.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getUsers (params) {
4 | return request.get(`/users?_sort=updateTime&_order=desc&_page=${params.current}&_limit=${params.pageSize}`)
5 | }
6 |
7 | export function deleteAccount (id) {
8 | return request.delete(`/users/${id}`)
9 | }
10 |
11 | export function createAccount (params) {
12 | params.createTime = Date.now()
13 | params.updateTime = Date.now()
14 | return request.post('/users', params)
15 | }
16 |
17 | export function modifyAccount (params) {
18 | params.updateTime = Date.now()
19 | const { id, ...user } = params
20 | return request.patch(`/users/${id}`, user)
21 | }
22 |
23 | export function getRoles () {
24 | return request.get('/roles')
25 | }
26 |
--------------------------------------------------------------------------------
/tests/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 elementCount (selector, count) {
11 | this.message = `Testing if element <${selector}> has count: ${count}`
12 | this.expected = count
13 | this.pass = val => val === count
14 | this.value = res => res.value
15 | function evaluator (_selector) {
16 | return document.querySelectorAll(_selector).length
17 | }
18 | this.command = cb => this.api.execute(evaluator, [selector], cb)
19 | }
20 |
--------------------------------------------------------------------------------
/src/views/g2/bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
34 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: [
3 | 'js',
4 | 'jsx',
5 | 'json',
6 | 'vue'
7 | ],
8 | transform: {
9 | '^.+\\.vue$': 'vue-jest',
10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
11 | '^.+\\.jsx?$': 'babel-jest'
12 | },
13 | transformIgnorePatterns: [
14 | '/node_modules/'
15 | ],
16 | moduleNameMapper: {
17 | '^@/(.*)$': '/src/$1'
18 | },
19 | snapshotSerializers: [
20 | 'jest-serializer-vue'
21 | ],
22 | testMatch: [
23 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
24 | ],
25 | testURL: 'http://localhost/',
26 | watchPlugins: [
27 | 'jest-watch-typeahead/filename',
28 | 'jest-watch-typeahead/testname'
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/server/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "posts": [
3 | {
4 | "id": 1,
5 | "title": "json-server",
6 | "author": "typicode"
7 | }
8 | ],
9 | "comments": [
10 | {
11 | "id": 1,
12 | "body": "some comment",
13 | "postId": 1
14 | }
15 | ],
16 | "profile": {
17 | "name": "typicode"
18 | },
19 | "users": [
20 | {
21 | "id": "V20190324121645AP",
22 | "username": "常山赵子龙",
23 | "contacts": "赵子龙",
24 | "contactsEmail": "zhaozilong@qq.com",
25 | "provinceName": "河北省",
26 | "cityName": "石家庄市",
27 | "areaName": "",
28 | "address": "",
29 | "roleId": "",
30 | "roleName": "管理员",
31 | "status": 1,
32 | "createTime": "2019-03-24 15:16:45",
33 | "updateTime": "2019-03-24 15:16:45"
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Layout/AppLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
21 |
22 |
37 |
--------------------------------------------------------------------------------
/src/store/mutation-types.js:
--------------------------------------------------------------------------------
1 | export const SYSTEM_MENU_THEME = 'DEFAULT_THEME'
2 | export const SYSTEM_COLOR = 'DEFAULT_COLOR'
3 | export const SYSTEM_LAYOUT = 'SYSTEM_LAYOUT'
4 | export const SYSTEM_CONTENT_WIDTH = 'DEFAULT_CONTENT_WIDTH'
5 | export const SYSTEM_FIXED_HEADER = 'DEFAULT_FIXED_HEADER'
6 | export const SYSTEM_FIXED_SIDERBAR = 'DEFAULT_FIXED_SIDERBAR'
7 | export const SYSTEM_AUTOHIDE_HEADER = 'DEFAULT_AUTOHIDE_HEADER'
8 | export const SYSTEM_COLORWEAK = 'SYSTEM_COLORWEAK'
9 |
10 | export const ACCESS_TOKEN = 'Authorization'
11 | export const USER = 'USER'
12 |
13 | export const MENUS = 'MENUS'
14 |
15 | export const SIDEBAR_TYPE = 'SIDEBAR_TYPE'
16 |
17 | export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB'
18 |
19 | export const CONTENT_WIDTH_TYPE = {
20 | Fluid: 'Fluid',
21 | Fixed: 'Fixed'
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Chart/Bar.vue:
--------------------------------------------------------------------------------
1 |
38 |
--------------------------------------------------------------------------------
/src/utils/device.js:
--------------------------------------------------------------------------------
1 | import enquireJs from 'enquire.js'
2 |
3 | export const DEVICE_TYPE = {
4 | DESKTOP: 'desktop',
5 | TABLET: 'tablet',
6 | MOBILE: 'mobile'
7 | }
8 |
9 | export const deviceEnquire = function (callback) {
10 | const matchDesktop = {
11 | match: () => {
12 | callback && callback(DEVICE_TYPE.DESKTOP)
13 | }
14 | }
15 |
16 | const matchTablet = {
17 | match: () => {
18 | callback && callback(DEVICE_TYPE.TABLET)
19 | }
20 | }
21 |
22 | const matchMobile = {
23 | match: () => {
24 | callback && callback(DEVICE_TYPE.MOBILE)
25 | }
26 | }
27 |
28 | enquireJs
29 | .register('screen and (min-width: 1200px)', matchDesktop)
30 | .register(
31 | 'screen and (min-width: 576px) and (max-width: 1200px)',
32 | matchTablet
33 | )
34 | .register('screen and (max-width: 576px)', matchMobile)
35 | }
36 |
--------------------------------------------------------------------------------
/src/mixins/table.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data () {
3 | return {
4 | // 表头数据
5 | columns: null,
6 | // 行数据
7 | rows: null,
8 | // table loading
9 | tableLoading: false,
10 | // table 分页
11 | pagination: {
12 | current: 1,
13 | pageSize: 10,
14 | total: 0,
15 | showSizeChanger: true,
16 | showTotal (total, range) {
17 | return `共${total}条`
18 | }
19 | }
20 | }
21 | },
22 | computed: {
23 | serial () {
24 | const {
25 | pagination: { current, pageSize }
26 | } = this
27 | return (current - 1) * pageSize
28 | }
29 | },
30 | methods: {
31 | handleTableChange (pagination, filters, sorter) {
32 | this.pagination.current = pagination.current
33 | this.pagination.pageSize = pagination.pageSize
34 | this.search()
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/mixins/appStore.js:
--------------------------------------------------------------------------------
1 | import { mapState, mapActions } from 'vuex'
2 |
3 | const appStore = {
4 | computed: {
5 | ...mapState('app', {
6 | device: state => state.device,
7 | menuTheme: state => state.menuTheme,
8 | defaultColor: state => state.color,
9 | layoutMode: state => state.layout,
10 | contentWidth: state => state.contentWidth,
11 | fixedHeader: state => state.fixedHeader,
12 | fixedSiderbar: state => state.fixedSiderbar,
13 | autoHideHeader: state => state.autoHideHeader,
14 | colorWeak: state => state.colorWeak
15 | })
16 | },
17 | methods: {
18 | ...mapActions('app', [
19 | 'set_menuTheme',
20 | 'set_color',
21 | 'set_layout',
22 | 'set_contentWidth',
23 | 'set_fixedHeader',
24 | 'set_fixedSiderbar',
25 | 'set_autoHideHeader',
26 | 'set_colorWeak'
27 | ])
28 | }
29 | }
30 |
31 | export default appStore
32 |
--------------------------------------------------------------------------------
/src/filters/index.js:
--------------------------------------------------------------------------------
1 | function padLeftZero (str) {
2 | return ('00' + str).substr(str.length)
3 | }
4 |
5 | export function formatDate (date, fmt) {
6 | if (/(y+)/.test(fmt)) {
7 | fmt = fmt.replace(
8 | RegExp.$1,
9 | (date.getFullYear() + '').substr(4 - RegExp.$1.length)
10 | )
11 | }
12 | if (/(s+)/.test(fmt)) {
13 | fmt = fmt.replace(
14 | RegExp.$1,
15 | (date.getMilliseconds() + '').substring(0, RegExp.$1.length)
16 | )
17 | }
18 | const o = {
19 | 'M+': date.getMonth() + 1,
20 | 'd+': date.getDate(),
21 | 'h+': date.getHours(),
22 | 'm+': date.getMinutes(),
23 | 'S+': date.getSeconds()
24 | }
25 | for (let k in o) {
26 | if (new RegExp(`(${k})`).test(fmt)) {
27 | let str = o[k] + ''
28 | fmt = fmt.replace(
29 | RegExp.$1,
30 | RegExp.$1.length === 1 ? str : padLeftZero(str)
31 | )
32 | }
33 | }
34 | return fmt
35 | }
36 |
37 | export const noDataFilter = text =>
38 | text === (null || undefined || '') ? '---' : text
39 |
--------------------------------------------------------------------------------
/src/views/dashboard/components/CustomTab.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
20 |
21 |
22 |
23 |
24 |
40 |
41 |
44 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import NProgress from 'nprogress'
4 | import 'nprogress/nprogress.css' // progress bar style
5 | import routes from './routes'
6 | import { ACCESS_TOKEN } from '@/store/mutation-types'
7 |
8 | Vue.use(Router)
9 | NProgress.configure({ showSpinner: false }) // NProgress Configuration
10 |
11 | const whiteList = ['login', '404'] // no redirect whitelist
12 |
13 | export const createRouter = () =>
14 | new Router({
15 | mode: 'history',
16 | base: process.env.BASE_URL,
17 | routes
18 | })
19 |
20 | const router = createRouter()
21 |
22 | router.beforeEach((to, from, next) => {
23 | NProgress.start()
24 | const token = Vue.ss.get(ACCESS_TOKEN)
25 | if (token) {
26 | if (to.name === 'login') {
27 | next('/')
28 | } else {
29 | next()
30 | }
31 | } else {
32 | if (whiteList.includes(to.name)) {
33 | next()
34 | } else {
35 | next({ path: '/login' })
36 | }
37 | }
38 | })
39 |
40 | router.afterEach(() => {
41 | NProgress.done() // finish progress bar
42 | })
43 |
44 | export default router
45 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | // const webpack = require('webpack');
3 |
4 | function resolve (dir) {
5 | return path.join(__dirname, dir)
6 | }
7 |
8 | // vue.config.js
9 | module.exports = {
10 | publicPath: '/',
11 |
12 | transpileDependencies: ['resize-detector', 'ant-design-vue'],
13 |
14 | // configureWebpack: {
15 | // plugins: [
16 | // // Ignore all locale files of moment.js
17 | // new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
18 | // ]
19 | // },
20 |
21 | chainWebpack: config => {
22 | config.resolve.alias
23 | .set('public', resolve('public'))
24 | .set('@', resolve('src'))
25 | .set('@api', resolve('src/api'))
26 | .set('@assets', resolve('src/assets'))
27 | .set('@comp', resolve('src/components'))
28 | .set('@views', resolve('src/views'))
29 | },
30 |
31 | devServer: {
32 | proxy: {
33 | '/api': {
34 | target: 'http://localhost:3000', // 后端服务器
35 | ws: false,
36 | changeOrigin: true,
37 | pathRewrite: {
38 | '^/api': ''
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/Chart/MiniArea.vue:
--------------------------------------------------------------------------------
1 |
46 |
--------------------------------------------------------------------------------
/src/components/Layout/App/LayoutSider.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
36 |
37 |
60 |
--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { login as userLogin, logout as userLogout } from '@/api/auth'
3 | import { ACCESS_TOKEN, USER } from '@/store/mutation-types'
4 |
5 | const user = {
6 | namespaced: true,
7 | state: {
8 | token: '',
9 | user: null
10 | },
11 | getters: {
12 | username (state) {
13 | return (state.user && state.user.username) || ''
14 | }
15 | },
16 |
17 | mutations: {
18 | SET_TOKEN: (state, token) => {
19 | Vue.ss.set(ACCESS_TOKEN, token, 7 * 24 * 60 * 60 * 1000)
20 | state.token = token
21 | },
22 | SET_USER: (state, user) => {
23 | Vue.ss.set(USER, user)
24 | state.user = user
25 | }
26 | },
27 |
28 | actions: {
29 | // 登录
30 | Login ({ commit, dispatch }, userInfo) {
31 | return new Promise(async (resolve, reject) => {
32 | const res = await userLogin(userInfo)
33 | if (res.status === 200) {
34 | commit('SET_TOKEN', res.data.token)
35 | commit('SET_USER', res.data.user)
36 | commit('permission/SET_MENUS', res.data.menus, {
37 | root: true
38 | })
39 | resolve(res)
40 | } else {
41 | reject(res)
42 | }
43 | })
44 | },
45 | Logout ({ commit, state }) {
46 | return new Promise(async resolve => {
47 | await userLogout()
48 | commit('SET_TOKEN', '')
49 | Vue.ss.remove(ACCESS_TOKEN)
50 | resolve()
51 | })
52 | }
53 | }
54 | }
55 |
56 | export default user
57 |
--------------------------------------------------------------------------------
/src/components/Trend/index.vue:
--------------------------------------------------------------------------------
1 |
38 |
39 |
69 |
--------------------------------------------------------------------------------
/src/components/Chart/MiniSearchArea.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 | {{ value }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
36 |
37 |
64 |
--------------------------------------------------------------------------------
/src/plugins/ant-design-vue.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import {
3 | Avatar,
4 | Button,
5 | Card,
6 | Col,
7 | DatePicker,
8 | Divider,
9 | Dropdown,
10 | Form,
11 | Icon,
12 | Input,
13 | Radio,
14 | Layout,
15 | List,
16 | LocaleProvider,
17 | message,
18 | Menu,
19 | Modal,
20 | notification,
21 | Pagination,
22 | Popover,
23 | Row,
24 | Select,
25 | Spin,
26 | Table,
27 | Transfer,
28 | TimePicker,
29 | Tooltip,
30 | Upload,
31 | Drawer,
32 | Skeleton,
33 | Progress,
34 | Tag,
35 | Switch,
36 | Tabs
37 | } from 'ant-design-vue'
38 |
39 | Vue.prototype.$message = message
40 | Vue.prototype.$notification = notification
41 | Vue.prototype.$info = Modal.info
42 | Vue.prototype.$success = Modal.success
43 | Vue.prototype.$error = Modal.error
44 | Vue.prototype.$warning = Modal.warning
45 | Vue.prototype.$confirm = Modal.confirm
46 |
47 | /* v1.1.3+ registration methods */
48 | Vue.use(Avatar)
49 | Vue.use(Button)
50 | Vue.use(Card)
51 | Vue.use(Col)
52 | Vue.use(DatePicker)
53 | Vue.use(Divider)
54 | Vue.use(Drawer)
55 | Vue.use(Dropdown)
56 | Vue.use(Form)
57 | Vue.use(Icon)
58 | Vue.use(Input)
59 | Vue.use(Radio)
60 | Vue.use(Radio.Group)
61 | Vue.use(Layout)
62 | Vue.use(List)
63 | Vue.use(LocaleProvider)
64 | Vue.use(Menu)
65 | Vue.use(Modal)
66 | Vue.use(Pagination)
67 | Vue.use(Popover)
68 | Vue.use(Row)
69 | Vue.use(Select)
70 | Vue.use(Spin)
71 | Vue.use(Table)
72 | Vue.use(Transfer)
73 | Vue.use(TimePicker)
74 | Vue.use(Tooltip)
75 | Vue.use(Upload)
76 | Vue.use(Skeleton)
77 | Vue.use(Progress)
78 | Vue.use(Tag)
79 | Vue.use(Switch)
80 | Vue.use(Tabs)
81 | Vue.use(Tabs.TabPane)
82 |
--------------------------------------------------------------------------------
/src/views/dashboard/components/OfflineData.vue:
--------------------------------------------------------------------------------
1 |
52 |
61 |
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | # vue-antd-pro
2 | 基于vue-cli3 和 [ant-design-vue](https://vue.ant.design/docs/vue/introduce/)搭建的后台管理系统模板,使用json-server做数据mock。
3 |
4 | ### 数据mock服务启用
5 | 使用json-server做数据mock,关于json-server请移步[官网](https://github.com/typicode/json-server)
6 | ```
7 | // 进入到server目录
8 | cd server
9 |
10 | // 安装依赖
11 | yarn install
12 |
13 | // 启动mock服务
14 | yarn start
15 | ```
16 |
17 |
18 | ### 前端服务启动
19 | ```
20 | // 返回项目根目录
21 | cd ..
22 |
23 | // 安装依赖
24 | yarn install
25 |
26 | // 启动本地服务
27 | yarn serve
28 | ```
29 |
30 | ### 登录账号
31 | 用户名: 任意输入
32 | 密码: 字符数字[4-16]位的任意输入
33 |
34 | ### 生产环境构建
35 | ```
36 | yarn run build
37 | ```
38 |
39 | ### 效果预览
40 | 
41 |
42 | 
43 |
44 | 
45 |
46 | 
47 |
48 | 
49 |
50 | 
51 |
52 | ## Todo:
53 | 1. Pie组件颜色有问题
54 | 2. Pie组件Legend点击饼图没有变化
55 | 3. offlineData组件中选中tab字体颜色问题
56 | 5. 销售额类别占比 图和legend响应式 图没有及时更新以及mounted的时候未执行初始逻辑
57 | 6. v-slider组件onchange方法不起作用
58 |
--------------------------------------------------------------------------------
/src/views/error/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
呃...
7 |
抱歉,找不到您访问的资源啦 :(
8 |
13 |
14 |
15 |
16 |

17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
48 |
--------------------------------------------------------------------------------
/src/store/initStore.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from './index'
3 | import config from './config'
4 | import {
5 | SYSTEM_MENU_THEME,
6 | SYSTEM_COLOR,
7 | SYSTEM_LAYOUT,
8 | SYSTEM_CONTENT_WIDTH,
9 | SYSTEM_FIXED_HEADER,
10 | SYSTEM_FIXED_SIDERBAR,
11 | SYSTEM_AUTOHIDE_HEADER,
12 | SYSTEM_COLORWEAK,
13 | ACCESS_TOKEN,
14 | USER,
15 | MENUS
16 | } from './mutation-types'
17 |
18 | const { app, user, permission } = config
19 |
20 | export default function initStore () {
21 | store.commit(
22 | 'app/SET_MENUTHEME',
23 | Vue.ls.get(SYSTEM_MENU_THEME, app.menuTheme)
24 | )
25 | store.commit('app/SET_COLOR', Vue.ls.get(SYSTEM_COLOR, app.color))
26 | store.commit('app/SET_LAYOUT', Vue.ls.get(SYSTEM_LAYOUT, app.layout))
27 | store.commit(
28 | 'app/SET_CONTENTWIDTH',
29 | Vue.ls.get(SYSTEM_CONTENT_WIDTH, app.contentWidth)
30 | )
31 | store.commit(
32 | 'app/SET_FIXEDHEADER',
33 | Vue.ls.get(SYSTEM_FIXED_HEADER, app.fixedHeader)
34 | )
35 | store.commit(
36 | 'app/SET_FIXEDSIDERBAR',
37 | Vue.ls.get(SYSTEM_FIXED_SIDERBAR, app.fixSiderbar)
38 | )
39 | store.commit(
40 | 'app/SET_AUTOHIDENHEADER',
41 | Vue.ls.get(SYSTEM_AUTOHIDE_HEADER, app.autoHideHeader)
42 | )
43 | store.commit(
44 | 'app/SET_COLORWEAK',
45 | Vue.ls.get(SYSTEM_COLORWEAK, app.colorWeak)
46 | )
47 |
48 | store.commit('user/SET_TOKEN', Vue.ss.get(ACCESS_TOKEN, user.token))
49 | store.commit('user/SET_USER', Vue.ss.get(USER, user.user))
50 |
51 | store.commit('permission/SET_MENUS', Vue.ss.get(MENUS, permission.menus))
52 |
53 | // store.commit('SET_SIDEBAR_TYPE', Vue.ls.get(SIDEBAR_TYPE, true));
54 | //
55 | // store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab));
56 |
57 | // last step
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/Logo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

10 |

16 |
VUE-ANTD-PRO
17 |
VUE-ANTD-PRO
18 |
19 |
20 |
34 |
74 |
--------------------------------------------------------------------------------
/src/components/Chart/RankList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ title }}
4 |
5 | -
6 | {{ idx + 1 }}
7 | {{ item.label }}
8 | {{ item.value }}
9 |
10 |
11 |
12 |
13 |
14 |
29 |
30 |
80 |
--------------------------------------------------------------------------------
/src/views/dashboard/track.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
64 |
65 |
68 |
--------------------------------------------------------------------------------
/src/views/dashboard/config.js:
--------------------------------------------------------------------------------
1 | export const pieOptions = {
2 | color: ['#1890FF', '#13C2C2', '#2FC25B', '#FACC14', '#F04864', '#8543E0'],
3 | tooltip: {
4 | trigger: 'item',
5 | formatter (params) {
6 | return `${params.seriesName}${params.marker}${params.name}:${
7 | params.data.value
8 | } (${params.percent}%)`
9 | },
10 | confine: true,
11 | padding: [10, 10, 6, 10],
12 | backgroundColor: 'rgba(255, 255, 255, 0.9)',
13 | extraCssText: 'box-shadow: rgb(174, 174, 174) 0px 0px 10px;',
14 | textStyle: {
15 | color: 'rgb(87, 87, 87)'
16 | }
17 | },
18 | dataset: {
19 | source: []
20 | },
21 | series: [
22 | {
23 | name: '单量',
24 | type: 'pie',
25 | radius: ['45%', '60%'],
26 | label: {
27 | formatter: params => {
28 | const name = params.name
29 | return name.length > 5 ? name.substring(0, 5) + '...' : name
30 | }
31 | }
32 | }
33 | ]
34 | }
35 |
36 | // 单量分布
37 | export const countColumn = [
38 | {
39 | title: '口岸',
40 | dataIndex: 'customsName',
41 | align: 'center',
42 | width: 200
43 | },
44 | {
45 | title: '单量',
46 | dataIndex: 'totalDeclareCount',
47 | align: 'center',
48 | width: 100
49 | },
50 | {
51 | title: '占比',
52 | dataIndex: 'rate',
53 | scopedSlots: { customRender: 'percent' },
54 | align: 'center',
55 | width: 150
56 | }
57 | ]
58 |
59 | // 申报成功率
60 | export const rateColumn = [
61 | {
62 | title: '口岸',
63 | dataIndex: 'customsName',
64 | align: 'center',
65 | width: 200
66 | },
67 | {
68 | title: '回执单量',
69 | dataIndex: 'receiptDeclareCount',
70 | align: 'center',
71 | width: 100
72 | },
73 | {
74 | title: '成功',
75 | dataIndex: 'succDeclareCount',
76 | align: 'center',
77 | width: 100
78 | },
79 | {
80 | title: '成功率',
81 | dataIndex: 'successRate',
82 | scopedSlots: { customRender: 'percent' },
83 | align: 'center',
84 | width: 200
85 | }
86 | ]
87 |
--------------------------------------------------------------------------------
/src/components/Chart/SalePercentCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 全部渠道
7 | 线上
8 | 门店
9 |
10 |
11 |
12 |
13 |
14 |
15 | 操作一
16 |
17 |
18 | 操作二
19 |
20 |
21 |
22 |
23 |
24 |
36 |
37 |
38 |
39 |
70 |
71 |
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-antd-pro",
3 | "version": "0.1.0",
4 | "description": "A Vue.js template for PC backstage base ant-design-vue",
5 | "author": "luichooy",
6 | "license": "MIT",
7 | "homepage": "https://github.com/luichooy/vue-antd-pro",
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/luichooy/vue-antd-pro.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/luichooy/vue-antd-pro/issues"
14 | },
15 | "private": true,
16 | "scripts": {
17 | "serve": "vue-cli-service serve",
18 | "build": "vue-cli-service build",
19 | "lint": "vue-cli-service lint",
20 | "test:e2e": "vue-cli-service test:e2e",
21 | "test:unit": "vue-cli-service test:unit"
22 | },
23 | "dependencies": {
24 | "@antv/data-set": "^0.10.2",
25 | "@antv/g2": "^3.5.11",
26 | "ant-design-vue": "^1.4.5",
27 | "axios": "^0.19.0",
28 | "core-js": "^2.6.5",
29 | "d3": "^5.14.1",
30 | "echarts": "^4.4.0",
31 | "enquire.js": "^2.1.6",
32 | "js-base64": "^2.5.1",
33 | "moment": "^2.24.0",
34 | "nprogress": "^0.2.0",
35 | "v-charts": "^1.19.0",
36 | "viser-vue": "^2.4.6",
37 | "vue": "^2.6.10",
38 | "vue-ls": "^3.2.1",
39 | "vue-router": "^3.0.3",
40 | "vuex": "^3.0.1"
41 | },
42 | "devDependencies": {
43 | "@vue/cli-plugin-babel": "^3.8.0",
44 | "@vue/cli-plugin-e2e-nightwatch": "^3.8.0",
45 | "@vue/cli-plugin-eslint": "^3.8.0",
46 | "@vue/cli-plugin-unit-jest": "^3.8.0",
47 | "@vue/cli-service": "^3.8.0",
48 | "@vue/eslint-config-standard": "^4.0.0",
49 | "@vue/test-utils": "1.0.0-beta.29",
50 | "babel-core": "7.0.0-bridge.0",
51 | "babel-eslint": "^10.0.1",
52 | "babel-jest": "^23.6.0",
53 | "babel-plugin-import": "^1.12.0",
54 | "eslint": "^5.16.0",
55 | "eslint-plugin-vue": "^5.0.0",
56 | "husky": "^4.0.0-beta.2",
57 | "less": "2.7.3",
58 | "less-loader": "^5.0.0",
59 | "lint-staged": "^9.4.0",
60 | "sass": "^1.18.0",
61 | "sass-loader": "^7.1.0",
62 | "stylelint": "^11.0.0",
63 | "stylelint-config-standard": "^19.0.0",
64 | "vue-template-compiler": "^2.6.10"
65 | },
66 | "husky": {
67 | "hooks": {
68 | "pre-commit": "lint-staged"
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/views/dashboard/components/NumberInfo.vue:
--------------------------------------------------------------------------------
1 |
38 |
39 |
84 |
--------------------------------------------------------------------------------
/src/components/Chart/MiniProgress.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
59 |
60 |
99 |
--------------------------------------------------------------------------------
/src/assets/styles/common.scss:
--------------------------------------------------------------------------------
1 | @each $i, $size in $spaces {
2 | .m-#{$i} {
3 | margin: $size;
4 | }
5 |
6 | .p-#{$i} {
7 | padding: $size;
8 | }
9 |
10 | .ml-#{$i} {
11 | margin-left: $size;
12 | }
13 |
14 | .mr-#{$i} {
15 | margin-right: $size;
16 | }
17 |
18 | .mt-#{$i} {
19 | margin-top: $size;
20 | }
21 |
22 | .mb-#{$i} {
23 | margin-bottom: $size;
24 | }
25 |
26 | .pl-#{$i} {
27 | padding-left: $size;
28 | }
29 |
30 | .pr-#{$i} {
31 | padding-right: $size;
32 | }
33 |
34 | .pt-#{$i} {
35 | padding-top: $size;
36 | }
37 |
38 | .pb-#{$i} {
39 | padding-bottom: $size;
40 | }
41 | }
42 |
43 | .text-success {
44 | color: #00bcd4 !important;
45 | }
46 |
47 | .text-danger {
48 | color: #d9534f !important;
49 | }
50 |
51 | .text-center {
52 | text-align: center;
53 | }
54 |
55 | .text-right {
56 | text-align: right;
57 | }
58 |
59 | .cursor-pointer {
60 | cursor: pointer;
61 | }
62 |
63 | .border-none {
64 | border: none !important;
65 | }
66 |
67 | .page-container__stretch {
68 | position: absolute;
69 | top: 0;
70 | left: 0;
71 | bottom: 0;
72 | right: 0;
73 | }
74 |
75 | .operate-wrapper {
76 | margin: 16px 0;
77 |
78 | .btn-item {
79 | margin: 0 8px;
80 |
81 | &:first-child {
82 | margin-left: 0;
83 | }
84 |
85 | &:last-child {
86 | margin-right: 0;
87 | }
88 | }
89 | }
90 |
91 | .table-search-wrapper {
92 | .ant-form-item {
93 | display: flex;
94 | margin-bottom: 24px;
95 | margin-right: 0;
96 |
97 | .ant-form-item-control-wrapper {
98 | flex: 1 1;
99 | display: inline-block;
100 | vertical-align: middle;
101 | }
102 |
103 | .ant-form-item-label {
104 | line-height: 32px;
105 | padding-right: 8px;
106 | }
107 | .ant-form-item-control {
108 | height: 32px;
109 | line-height: 32px;
110 | }
111 | }
112 |
113 | .search-buttons {
114 | display: block;
115 | margin-bottom: 24px;
116 | margin-left: 24px;
117 | white-space: nowrap;
118 | }
119 | }
120 |
121 | .echarts {
122 | width: 100%;
123 | height: 100%;
124 | }
125 |
126 | // 手机端 左侧菜单drawer样式
127 | .ant-drawer.drawer-sider {
128 | .ant-drawer-body {
129 | padding: 0;
130 | }
131 | }
132 |
133 | // layoutHeader dropdown下拉菜单样式
134 | .user-menu .user {
135 | cursor: default;
136 | user-select: none;
137 | &:hover {
138 | background-color: #fff;
139 | }
140 | }
141 |
142 | // 表格内容单元格允许单词换行,防止内容过多将单元格撑大,
143 | .ant-table td {
144 | word-break: break-all;
145 | }
146 |
--------------------------------------------------------------------------------
/src/components/Chart/ChartCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
25 |
26 |
27 |
43 |
44 |
108 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from '@/store/index'
3 | import router from '@/router'
4 | import axios from 'axios'
5 | import { ACCESS_TOKEN } from '@/store/mutation-types'
6 | import notification from 'ant-design-vue/es/notification'
7 |
8 | // api 配置
9 |
10 | let timer = null
11 |
12 | const onError = error => {
13 | if (error.response) {
14 | const status = error.response.status
15 | const message = error.response.statusText
16 | const token = Vue.ss.get(ACCESS_TOKEN)
17 |
18 | if (status === 403) {
19 | notification.error({ message: '禁止访问', description: message })
20 | }
21 |
22 | if (status === 404) {
23 | notification.error({ message: '未知资源', description: message })
24 | }
25 |
26 | if (status === 500) {
27 | notification.error({
28 | message: '服务器错误',
29 | description: message
30 | })
31 | }
32 |
33 | if (status === 401 && !timer) {
34 | timer = setTimeout(() => {
35 | notification.error({
36 | message: '未授权',
37 | description: '授权失败,请重新登录'
38 | })
39 | if (token) {
40 | store.dispatch('user/Logout').then(() => router.replace('/login'))
41 | }
42 | timer = null
43 | }, 500)
44 | }
45 | }
46 | return Promise.reject(error)
47 | }
48 |
49 | const request = axios.create({
50 | baseURL: '/api',
51 | timeout: 5000,
52 | headers: {
53 | 'Content-Type': 'application/json;charset=UTF-8'
54 | },
55 | transformRequest: [
56 | function (data, headers) {
57 | const token = Vue.ss.get(ACCESS_TOKEN)
58 | if (token) {
59 | headers[ACCESS_TOKEN] = 'Bearer ' + token
60 | }
61 | if (headers['Content-Type'] === 'multipart/form-data') {
62 | return data
63 | } else {
64 | return JSON.stringify(data)
65 | }
66 | }
67 | ]
68 | })
69 |
70 | // 请求拦截器
71 | request.interceptors.request.use(
72 | config => {
73 | // 开发环境下,如果请求是 post,put,patch,则打印数据体,方便调试
74 | if (process.env.NODE_ENV === 'development') {
75 | const { method } = config
76 | if (['post', 'put', 'patch'].includes(method)) {
77 | console.log(config.data)
78 | }
79 | }
80 |
81 | return config
82 | },
83 | error => {
84 | notification.error({
85 | message: '请求失败',
86 | description: '发送请求失败,请检查您的网络'
87 | })
88 | return Promise.reject(error)
89 | }
90 | )
91 |
92 | // 响应拦截器
93 | request.interceptors.response.use(res => {
94 | console.log(res)
95 | const jsonPattern = /application\/json/gi
96 | if (jsonPattern.test(res.headers['content-type'])) {
97 | return res.data
98 | } else {
99 | return res
100 | }
101 | }, onError)
102 |
103 | export default request
104 |
--------------------------------------------------------------------------------
/src/components/ValidateCode/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
89 |
100 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import {
3 | SYSTEM_MENU_THEME,
4 | SYSTEM_COLOR,
5 | SYSTEM_LAYOUT,
6 | SYSTEM_CONTENT_WIDTH,
7 | SYSTEM_FIXED_HEADER,
8 | SYSTEM_FIXED_SIDERBAR,
9 | SYSTEM_AUTOHIDE_HEADER,
10 | SYSTEM_COLORWEAK
11 | } from '../mutation-types'
12 |
13 | const app = {
14 | namespaced: true,
15 | state: {
16 | // 根据尺寸确定的客户端类型 desktop/tablat/mobile
17 | device: 'desktop',
18 | // menu的主题 dark light
19 | menuTheme: '',
20 | // 主题色
21 | color: '',
22 | // 页面布局 侧边栏布局 顶部布局
23 | layout: '',
24 | // 内容区域宽度 fixed fluid
25 | contentWidth: '',
26 | // 是否固定顶部栏
27 | fixedHeader: '',
28 | // 是否固定侧边栏
29 | fixedSiderbar: '',
30 | // 当顶部栏不固定时,页面向下滚动时,是否自动隐藏顶部栏
31 | autoHideHeader: '',
32 | // 色弱模式
33 | colorWeak: ''
34 | },
35 | mutations: {
36 | SET_DEVICE (state, device) {
37 | state.device = device
38 | },
39 | SET_MENUTHEME (state, theme) {
40 | Vue.ls.set(SYSTEM_MENU_THEME, theme)
41 | state.menuTheme = theme
42 | },
43 | SET_COLOR (state, color) {
44 | Vue.ls.set(SYSTEM_COLOR, color)
45 | state.color = color
46 | },
47 | SET_LAYOUT (state, layout) {
48 | Vue.ls.set(SYSTEM_LAYOUT, layout)
49 | state.layout = layout
50 | },
51 | SET_CONTENTWIDTH (state, contentWidth) {
52 | Vue.ls.set(SYSTEM_CONTENT_WIDTH, contentWidth)
53 | state.contentWidth = contentWidth
54 | },
55 | SET_FIXEDHEADER (state, isFixed) {
56 | Vue.ls.set(SYSTEM_FIXED_HEADER, isFixed)
57 | state.fixedHeader = isFixed
58 | },
59 | SET_FIXEDSIDERBAR (state, isFixed) {
60 | Vue.ls.set(SYSTEM_FIXED_SIDERBAR, isFixed)
61 | state.fixedSiderbar = isFixed
62 | },
63 | SET_AUTOHIDENHEADER (state, isHidden) {
64 | Vue.ls.set(SYSTEM_AUTOHIDE_HEADER, isHidden)
65 | state.autoHideHeader = isHidden
66 | },
67 | SET_COLORWEAK (state, colorWeak) {
68 | Vue.ls.set(SYSTEM_COLORWEAK, colorWeak)
69 | state.colorWeak = colorWeak
70 | }
71 | },
72 | actions: {
73 | set_menuTheme ({ commit }, menuTheme) {
74 | commit('SET_MENUTHEME', menuTheme)
75 | },
76 | set_color ({ commit }, color) {
77 | commit('SET_COLOR', color)
78 | },
79 | set_layout ({ commit }, layout) {
80 | commit('SET_LAYOUT', layout)
81 | },
82 | set_contentWidth ({ commit }, contentWidth) {
83 | commit('SET_CONTENTWIDTH', contentWidth)
84 | },
85 | set_fixedHeader ({ commit }, isFixed) {
86 | commit('SET_FIXEDHEADER', isFixed)
87 | },
88 | set_fixedSiderbar ({ commit }, isFixed) {
89 | commit('SET_FIXEDSIDERBAR', isFixed)
90 | },
91 | set_autoHideHeader ({ commit }, isHidden) {
92 | commit('SET_AUTOHIDENHEADER', isHidden)
93 | },
94 | set_colorWeak ({ commit }, colorWeak) {
95 | commit('SET_COLORWEAK', colorWeak)
96 | }
97 | }
98 | }
99 |
100 | export default app
101 |
--------------------------------------------------------------------------------
/src/components/Chart/HotSearchCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 操作一
9 |
10 |
11 | 操作二
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
34 |
35 | {{ keyword }}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Welcome to vue-antd-pro 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | > 基于vue-cli3 和 [ant-design-vue](https://vue.ant.design/docs/vue/introduce/)搭建的后台管理系统模板,使用json-server做数据mock。
16 |
17 | ## 安装依赖
18 |
19 | 安装前端依赖
20 | ```sh
21 | yarn
22 | ```
23 |
24 | 安装数据mock依赖
25 | ```sh
26 | cd server
27 |
28 | yarn
29 | ```
30 |
31 | ## 运行
32 |
33 | 运行数据mock服务
34 | ```sh
35 | // enter server directory
36 | cd server
37 |
38 | yarn start
39 | ```
40 |
41 | 运行前端本地服务
42 | ```sh
43 | // go back to project root directory
44 | cd ..
45 |
46 | yarn serve
47 | ```
48 |
49 | ## 账号
50 | ```
51 | 用户名: 任意输入
52 | 密码: 字符数字[4-16]位的任意输入
53 | ```
54 |
55 | ## 预览
56 |
57 | 
58 |
59 | 
60 |
61 | 
62 |
63 | 
64 |
65 | 
66 |
67 | 
68 |
69 | ## Todos
70 | * Pie组件颜色有问题
71 | * Pie组件Legend点击饼图没有变化
72 | * offlineData组件中选中tab字体颜色问题
73 | * 销售额类别占比 图和legend响应式 图没有及时更新以及mounted的时候未执行初始逻辑
74 | * v-slider组件onchange方法不起作用
75 |
76 |
77 | ## Author
78 |
79 | 👤 **luichooy**
80 |
81 | * 掘金: [@luichooy ](https://juejin.im/user/57fe62225bbb50005b47e277 )
82 | * Github: [@luichooy](https://github.com/luichooy)
83 |
84 | ## 🤝 Contributing
85 |
86 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/luichooy/vue-antd-pro/issues).
87 |
88 | ## Show your support
89 |
90 | Give a ⭐️ if this project helped you!
91 |
92 |
93 |
94 |
95 |
96 | ## 📝 License
97 |
98 | Copyright © 2019 [luichooy](https://github.com/luichooy).
99 | This project is [MIT]( ) licensed.
100 |
101 | ***
102 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
103 |
--------------------------------------------------------------------------------
/src/components/DetailList/DetailList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ title }}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
79 |
80 |
148 |
--------------------------------------------------------------------------------
/src/components/Layout/App/LayoutHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
16 |
19 |
20 |
21 |
22 |
83 |
129 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | let casual = require('casual')
2 |
3 | const roleMap = [
4 | {
5 | label: '超级管理员',
6 | value: 1
7 | },
8 | {
9 | label: '管理员',
10 | value: 2
11 | },
12 | {
13 | label: '操作员A',
14 | value: 3
15 | },
16 | {
17 | label: '操作员B',
18 | value: 4
19 | }
20 | ]
21 |
22 | const menus = [
23 | {
24 | id: '1',
25 | icon: 'table',
26 | path: '/table',
27 | name: 'table',
28 | title: '表格页'
29 | },
30 | {
31 | id: '2',
32 | icon: 'dashboard',
33 | path: '/dashboard',
34 | name: 'dashboard',
35 | title: 'Dashboard',
36 | children: [
37 | {
38 | id: '21',
39 | icon: '',
40 | path: '/dashboard/analysis',
41 | name: 'analysis',
42 | title: '分析页'
43 | },
44 | {
45 | id: '22',
46 | icon: '',
47 | path: '/dashboard/monitor',
48 | name: 'monitor',
49 | title: '监控页'
50 | },
51 | {
52 | id: '23',
53 | icon: '',
54 | path: '/dashboard/v-charts',
55 | name: 'v-charts',
56 | title: 'v-charts'
57 | },
58 | {
59 | id: '24',
60 | icon: '',
61 | path: '/dashboard/track',
62 | name: 'track',
63 | title: '轨迹图'
64 | }
65 | ]
66 | },
67 | {
68 | id: '3',
69 | icon: 'stock',
70 | path: '/d3',
71 | name: 'd3',
72 | title: 'D3',
73 | children: [
74 | {
75 | id: '31',
76 | icon: '',
77 | path: '/d3/bar',
78 | name: 'bar',
79 | title: 'bar'
80 | },
81 | {
82 | id: '32',
83 | icon: '',
84 | path: '/d3/line',
85 | name: 'line',
86 | title: 'line'
87 | }
88 | ]
89 | },
90 | {
91 | id: '4',
92 | icon: 'stock',
93 | path: '/g2',
94 | name: 'g2',
95 | title: 'g2',
96 | children: [
97 | {
98 | id: '41',
99 | icon: '',
100 | path: '/g2/bar',
101 | name: 'bar',
102 | title: 'bar'
103 | },
104 | {
105 | id: '42',
106 | icon: '',
107 | path: '/g2/line',
108 | name: 'line',
109 | title: 'line'
110 | }
111 | ]
112 | }
113 | ]
114 |
115 | casual.define('user', function (role) {
116 | return {
117 | id: casual.card_number(),
118 | username: casual.username,
119 | contacts: casual.full_name,
120 | contactsEmail: casual.email,
121 | address: casual.address,
122 | roleId: role.value,
123 | status: casual.integer(0, 1),
124 | createTime: casual.unix_time,
125 | updateTime: casual.unix_time
126 | }
127 | })
128 |
129 | module.exports = () => {
130 | const data = {
131 | users: [],
132 | roles: roleMap,
133 | login: {
134 | status: 200,
135 | data: {
136 | token:
137 | 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIwMTU2MjA5MyIsImNyZWF0ZWQiOjE1NjE1MTY3NjU5MzMsImNvbXBhbnlOYW1lIjoiQee9kSIsInRlbmFudElkIjoxLCJ1c2VyVHlwZSI6InN0YWZmIiwiaWQiOjEsImV4cCI6MTU2MTUyMDM2NX0.j8sWbwXzHnSgvz7em2DjAhNDU5xaxysEFES8SlyJZnj0lVgXKax4tEDNGawZivW6Ip1734Rnvb6z2te8jGmIWQ"',
138 | menus: menus,
139 | user: casual.user(casual.random_element(roleMap))
140 | },
141 | message: 'success'
142 | },
143 | logout: {
144 | status: 200,
145 | message: 'success'
146 | }
147 | }
148 |
149 | for (let i = 0; i < 54; i++) {
150 | data.users.push(casual.user(casual.random_element(roleMap)))
151 | }
152 |
153 | return data
154 | }
155 |
--------------------------------------------------------------------------------
/src/components/Setting/tools.js:
--------------------------------------------------------------------------------
1 | import { message } from 'ant-design-vue/es'
2 |
3 | const colorList = [
4 | {
5 | name: '薄暮',
6 | value: '#F5222D'
7 | },
8 | {
9 | name: '火山',
10 | value: '#FA541C'
11 | },
12 | {
13 | name: '日暮',
14 | value: '#FAAD14'
15 | },
16 | {
17 | name: '明青',
18 | value: '#13C2C2'
19 | },
20 | {
21 | name: '极光绿',
22 | value: '#52C41A'
23 | },
24 | {
25 | name: '拂晓蓝(默认)',
26 | value: '#1890FF'
27 | },
28 | {
29 | name: '极客蓝',
30 | value: '#2F54EB'
31 | },
32 | {
33 | name: '酱紫',
34 | value: '#722ED1'
35 | }
36 | ]
37 |
38 | const menuThemeList = [
39 | {
40 | name: '暗色菜单风格',
41 | value: 'dark',
42 | imgSrc:
43 | 'https://gw.alipayobjects.com/zos/rmsportal/LCkqqYNmvBEbokSDscrm.svg'
44 | },
45 | {
46 | name: '亮色菜单风格',
47 | value: 'light',
48 | imgSrc:
49 | 'https://gw.alipayobjects.com/zos/rmsportal/jpRkZQMyYRryryPNtyIC.svg'
50 | }
51 | ]
52 |
53 | const layoutModeList = [
54 | {
55 | name: '侧边栏导航',
56 | value: 'side',
57 | imgSrc:
58 | 'https://gw.alipayobjects.com/zos/rmsportal/JopDzEhOqwOjeNTXkoje.svg'
59 | },
60 | {
61 | name: '顶部栏导航',
62 | value: 'top',
63 | imgSrc:
64 | 'https://gw.alipayobjects.com/zos/rmsportal/KDNDBbriJhLwuqMoxcAr.svg'
65 | }
66 | ]
67 |
68 | let lessNodesAppended
69 | const updateTheme = color => {
70 | // Don't compile less in production!
71 | /* if (process.env.NODE_ENV === 'production') {
72 | return;
73 | } */
74 | // Determine if the component is remounted
75 | if (!color) {
76 | return
77 | }
78 | const hideMessage = message.loading('正在编译主题!', 0)
79 |
80 | function buildIt () {
81 | if (!window.less) {
82 | return
83 | }
84 | setTimeout(() => {
85 | window.less
86 | .modifyVars({
87 | '@primary-color': color
88 | })
89 | .then(() => {
90 | hideMessage()
91 | })
92 | .catch(() => {
93 | message.error('更新主题色失败')
94 | hideMessage()
95 | })
96 | }, 200)
97 | }
98 |
99 | if (!lessNodesAppended) {
100 | // insert less.js and color.less
101 | const lessStyleNode = document.createElement('link')
102 | const lessConfigNode = document.createElement('script')
103 | const lessScriptNode = document.createElement('script')
104 | lessStyleNode.setAttribute('rel', 'stylesheet/less')
105 | lessStyleNode.setAttribute('href', '/color.less')
106 | lessConfigNode.innerHTML = `
107 | window.less = {
108 | async: true,
109 | env: 'production',
110 | javascriptEnabled: true
111 | };
112 | `
113 | lessScriptNode.src =
114 | 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js'
115 | lessScriptNode.async = true
116 | lessScriptNode.onload = () => {
117 | buildIt()
118 | lessScriptNode.onload = null
119 | }
120 | document.body.appendChild(lessStyleNode)
121 | document.body.appendChild(lessConfigNode)
122 | document.body.appendChild(lessScriptNode)
123 | lessNodesAppended = true
124 | } else {
125 | buildIt()
126 | }
127 | }
128 |
129 | const updateColorWeak = colorWeak => {
130 | // document.body.className = colorWeak ? 'colorWeak' : '';
131 | colorWeak
132 | ? document.body.classList.add('color-weak')
133 | : document.body.classList.remove('color-weak')
134 | }
135 |
136 | export {
137 | menuThemeList,
138 | colorList,
139 | layoutModeList,
140 | updateTheme,
141 | updateColorWeak
142 | }
143 |
--------------------------------------------------------------------------------
/src/components/Layout/App/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | this.collapsed = false"
12 | wrapClassName="drawer-sider"
13 | >
14 | this.collapsed = false">
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
93 |
94 |
127 |
--------------------------------------------------------------------------------
/src/assets/images/excel.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/views/d3/components/Bar/Transition.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
107 |
108 |
130 |
--------------------------------------------------------------------------------
/src/components/ModifyPassword/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
21 |
22 |
23 |
34 |
35 |
36 |
48 |
49 |
50 |
51 |
52 |
53 |
128 |
--------------------------------------------------------------------------------
/src/views/d3/components/Bar/TransitionOther.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
107 |
108 |
133 |
--------------------------------------------------------------------------------
/src/router/routes.js:
--------------------------------------------------------------------------------
1 | import { AppLayout, ViewLayout } from '@/components/Layout'
2 |
3 | const otherRoutes = [
4 | {
5 | path: '/login',
6 | name: 'login',
7 | component: () => import(/* webpackChunkName: "login" */ '@/views/auth/login')
8 | },
9 | {
10 | path: '/error',
11 | name: 'error',
12 | redirect: '404',
13 | component: ViewLayout,
14 | children: [
15 | {
16 | path: '404',
17 | name: '404',
18 | component: () => import(/* webpackChunkName: "404" */ '@/views/error/404')
19 | }
20 | ]
21 | }
22 | ]
23 |
24 | export const appRoutes = [
25 | {
26 | path: '/',
27 | name: 'index',
28 | redirect: '/dashboard/analysis',
29 | component: AppLayout,
30 | children: [
31 | {
32 | path: 'table',
33 | name: 'table',
34 | meta: {
35 | title: '表格页',
36 | icon: 'table'
37 | },
38 | component: () => import(/* webpackChunkName: "table" */ '@/views/table/table')
39 | },
40 | {
41 | path: 'dashboard',
42 | name: 'dashboard',
43 | meta: {
44 | title: 'Dashboard',
45 | icon: 'dashboard'
46 | },
47 | component: ViewLayout,
48 | children: [
49 | {
50 | path: 'analysis',
51 | name: 'dashboard_analysis',
52 | meta: {
53 | title: '分析页'
54 | },
55 | component: () => import(/* webpackChunkName: "analysis" */ '@/views/dashboard/analysis')
56 | },
57 | {
58 | path: 'monitor',
59 | name: 'dashboard_monitor',
60 | meta: {
61 | title: '监控页'
62 | },
63 | component: () => import(/* webpackChunkName: "monitor" */ '@/views/dashboard/monitor')
64 | },
65 | {
66 | path: 'v-charts',
67 | name: 'v-charts',
68 | meta: {
69 | title: 'v-charts'
70 | },
71 | component: () => import(/* webpackChunkName: "v-charts" */ '@/views/dashboard/vcharts')
72 | },
73 | {
74 | path: 'track',
75 | name: 'track',
76 | meta: {
77 | title: '轨迹图'
78 | },
79 | component: () => import(/* webpackChunkName: "track" */ '@/views/dashboard/track')
80 | }
81 | ]
82 | },
83 | {
84 | path: 'd3',
85 | name: 'd3',
86 | meta: {
87 | title: 'D3',
88 | icon: 'stock'
89 | },
90 | component: ViewLayout,
91 | children: [
92 | {
93 | path: 'bar',
94 | name: 'd3_bar',
95 | meta: {
96 | title: 'bar'
97 | },
98 | component: () => import(/* webpackChunkName: "tutorials" */ '@/views/d3/bar')
99 | },
100 | {
101 | path: 'line',
102 | name: 'd3_line',
103 | meta: {
104 | title: 'line'
105 | },
106 | component: () => import(/* webpackChunkName: "tutorials" */ '@/views/d3/line')
107 | }
108 | ]
109 | },
110 | {
111 | path: 'g2',
112 | name: 'g2',
113 | meta: {
114 | title: 'g2',
115 | icon: 'stock'
116 | },
117 | component: ViewLayout,
118 | children: [
119 | {
120 | path: 'bar',
121 | name: 'g2_bar',
122 | meta: {
123 | title: 'bar'
124 | },
125 | component: () => import(/* webpackChunkName: "tutorials" */ '@/views/g2/bar')
126 | },
127 | {
128 | path: 'line',
129 | name: 'g2_line',
130 | meta: {
131 | title: 'line'
132 | },
133 | component: () => import(/* webpackChunkName: "tutorials" */ '@/views/g2/line')
134 | }
135 | ]
136 | }
137 | ]
138 | }
139 | ]
140 |
141 | const routes = [...otherRoutes, ...appRoutes]
142 |
143 | export default routes
144 |
--------------------------------------------------------------------------------
/src/components/Chart/TimelineChart.vue:
--------------------------------------------------------------------------------
1 |
155 |
--------------------------------------------------------------------------------
/src/components/Actions/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
18 |
19 |
20 |
21 | {{ username.slice(0,1) }}
25 |
26 | 欢迎您,{{username}}
27 |
28 |
29 |
30 |
36 |
59 |
60 |
61 |
62 |
63 |
64 |
104 |
105 |
138 |
--------------------------------------------------------------------------------
/src/components/Menu/index.vue:
--------------------------------------------------------------------------------
1 |
148 |
173 |
--------------------------------------------------------------------------------
/src/views/dashboard/vcharts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Change symbolSize
10 |
11 |
12 |
13 |
14 |
152 |
--------------------------------------------------------------------------------
/src/views/table/components/AccountModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
27 |
28 |
29 |
38 |
39 |
40 |
52 |
53 |
54 |
64 |
65 |
66 |
76 |
77 |
78 |
87 | 启用
88 | 禁用
89 |
90 |
91 |
92 |
93 |
94 |
95 |
159 |
--------------------------------------------------------------------------------
/src/views/d3/components/Bar/BarChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Part Ⅰ
6 |
7 |
8 |
9 | Part Ⅱ
10 |
11 |
12 |
13 | Part Ⅲ
14 |
15 |
16 |
17 |
18 |
19 |
20 |
137 |
138 |
189 |
--------------------------------------------------------------------------------
/src/views/auth/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
VUE-ANTD-PRO
7 |
8 |
9 |
10 |
11 |
24 |
25 |
26 |
27 |
28 |
29 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
67 |
68 |
69 |
70 |
71 | validateCode = code">
72 |
73 |
74 |
75 |
76 | 登录
84 |
85 |
86 |
87 |
88 |
91 |
92 |
93 |
94 |
160 |
161 |
210 |
--------------------------------------------------------------------------------
/src/views/table/table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
30 | 启用
31 | 禁用
32 |
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
47 |
48 |
52 | 查询
53 | 重置
54 |
55 | {{ formFold ? '展开' : '收起' }}
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
67 |
68 |
78 | {{ serial + index + 1 }}
79 |
80 | {{ roleId | roleFilter(roleOptions) }}
81 |
82 | {{ status === 1 ? '启用' : '禁用' }}
83 |
84 |
85 | {{ new Date(updateTime) | formatDate('yyyy-MM-dd hh:mm:ss') }}
86 |
87 |
88 |
89 |
编辑
90 |
91 |
删除
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
278 |
--------------------------------------------------------------------------------
/src/views/error/500.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
呃...出错了!
7 |
抱歉,服务器出错了。
8 |
13 |
14 | 或
15 |
16 | 联系我们
17 |
18 |
19 |
20 |
21 |
22 |

23 |
24 |
25 |

26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
60 |
--------------------------------------------------------------------------------
/src/components/Chart/Pie.vue:
--------------------------------------------------------------------------------
1 |
253 |
333 |
--------------------------------------------------------------------------------
/src/views/dashboard/analysis.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 日销售额
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 日访问量
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 转化率
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
75 |
76 |
77 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
132 |
133 |
134 |
135 |
249 |
250 |
306 |
--------------------------------------------------------------------------------
/src/components/Setting/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
整体风格设置
14 |
15 |
16 |
17 |
18 |
![]()
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
主题色
29 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
导航模式
47 |
48 |
49 |
50 |
51 |
![]()
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
69 | 固定
70 | 流式
71 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
84 |
85 |
86 |
87 |
94 |
95 | 下滑时隐藏 Header
99 |
100 |
101 |
102 |
103 |
104 |
111 |
112 | 固定侧边菜单
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
其他设置
126 |
127 |
128 |
129 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
146 |
147 |
148 |
149 |
150 |
247 |
248 |
327 |
--------------------------------------------------------------------------------