├── static └── .gitkeep ├── Dockerfile ├── .eslintignore ├── test ├── unit │ ├── setup.js │ ├── .eslintrc │ ├── specs │ │ └── HelloWorld.spec.js │ └── jest.conf.js └── e2e │ ├── specs │ └── test.js │ ├── custom-assertions │ └── elementCount.js │ ├── nightwatch.conf.js │ └── runner.js ├── src ├── router │ ├── import_production.js │ └── import_development.js ├── assets │ ├── logo.png │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ └── custom-theme │ │ └── fonts │ │ ├── element-icons.ttf │ │ └── element-icons.woff ├── api │ ├── base │ │ ├── logs.js │ │ ├── frame.js │ │ ├── users.js │ │ ├── permissions.js │ │ └── menus.js │ └── example │ │ ├── detail.js │ │ ├── form.js │ │ ├── table.js │ │ └── articles.js ├── module-dashboard │ ├── assets │ │ ├── logo-64.png │ │ ├── banner-64.png │ │ └── bigUserHeader.png │ ├── router │ │ └── index.js │ ├── components │ │ ├── layoutIndex.js │ │ ├── layoutAppMain.vue │ │ ├── pageTool.vue │ │ ├── layoutSidebar.vue │ │ ├── layoutSidebarItem.vue │ │ ├── dashboardPieChart.vue │ │ ├── loginSocialSignin.vue │ │ ├── dashboardBarChart.vue │ │ ├── dashboardRaddarChart.vue │ │ └── dashboardLineChart.vue │ ├── pages │ │ ├── authredirect.vue │ │ ├── layout.vue │ │ └── 401.vue │ ├── store │ │ ├── errorLog.js │ │ ├── app.js │ │ ├── permission.js │ │ ├── tagsView.js │ │ └── user.js │ └── index.js ├── App.vue ├── icons │ ├── generateIconsView.js │ ├── index.js │ └── svg │ │ ├── chart.svg │ │ ├── money.svg │ │ ├── email.svg │ │ ├── component.svg │ │ ├── menu-fold.svg │ │ ├── menu-unfold.svg │ │ ├── documentation.svg │ │ ├── user.svg │ │ ├── excel.svg │ │ ├── message.svg │ │ ├── drag.svg │ │ ├── icon.svg │ │ ├── example.svg │ │ ├── lock.svg │ │ ├── theme.svg │ │ ├── tab.svg │ │ ├── star.svg │ │ ├── password.svg │ │ ├── clipboard.svg │ │ ├── peoples.svg │ │ ├── github.svg │ │ ├── language.svg │ │ ├── 404.svg │ │ ├── copyright.svg │ │ ├── shoppingCard.svg │ │ ├── bug.svg │ │ ├── people.svg │ │ ├── table.svg │ │ ├── eye.svg │ │ ├── international.svg │ │ ├── dashboard.svg │ │ ├── wechat.svg │ │ ├── zip.svg │ │ └── form.svg ├── styles │ ├── variables.scss │ ├── transition.scss │ ├── element-ui.scss │ ├── mixin.scss │ ├── btn.scss │ └── sidebar.scss ├── utils │ ├── auth.js │ ├── i18n.js │ ├── permission.js │ ├── validate.js │ ├── openWindow.js │ └── request.js ├── mock │ ├── index.js │ ├── table.js │ ├── articles.js │ └── detailTable.js ├── components │ ├── Charts │ │ ├── mixins │ │ │ └── resize.js │ │ └── keyboard.vue │ ├── TreeTable │ │ ├── utils │ │ │ ├── expand.vue │ │ │ └── dataTranslate.js │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ ├── LangSelect │ │ └── index.vue │ ├── Breadcrumb │ │ └── index.vue │ ├── ScrollBar │ │ └── index.vue │ ├── Screenfull │ │ └── index.vue │ ├── ScrollPane │ │ └── index.vue │ └── ErrorLog │ │ └── index.vue ├── module-form │ ├── index.js │ ├── store │ │ ├── app.js │ │ └── store.js │ ├── router │ │ └── step.js │ ├── pages │ │ ├── activePublic │ │ │ ├── step3.vue │ │ │ └── index.vue │ │ └── step-form.vue │ └── components │ │ └── address.vue ├── module-list │ ├── index.js │ ├── store │ │ └── app.js │ └── router │ │ └── index.js ├── module-details │ ├── index.js │ ├── store │ │ └── app.js │ └── router │ │ └── index.js ├── module-manage │ ├── index.js │ ├── store │ │ └── app.js │ ├── components │ │ ├── page-tool.vue │ │ └── user-add.vue │ └── router │ │ └── index.js ├── errorLog.js ├── lang │ ├── index.js │ ├── zh.js │ └── en.js ├── store │ ├── index.js │ └── getters.js ├── main.js └── filters │ └── index.js ├── assets └── img │ ├── itheima.png │ └── vue-element-admin-itheima logo.psd ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── ci ├── deploy.sh ├── sonar_analyze.sh ├── build.sh └── sonar_preview.sh ├── .editorconfig ├── .postcssrc.js ├── .gitignore ├── index.html ├── .babelrc ├── .gitlab-ci.yml ├── script └── module-add │ ├── template │ ├── index.js │ ├── store │ │ └── app.js │ └── router │ │ └── index.js │ └── index.js ├── LICENSE ├── sonar-project.properties ├── .eslintrc.js ├── README.md ├── package.json └── _package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | COPY ./dist /usr/share/nginx/html 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /src/router/import_production.js: -------------------------------------------------------------------------------- 1 | module.exports = file => () => import('@/module-' + file + '.vue') 2 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /assets/img/itheima.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/assets/img/itheima.png -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | BASE_API: '"api"' 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/router/import_development.js: -------------------------------------------------------------------------------- 1 | module.exports = file => require('@/module-' + file + '.vue').default // vue-loader at least v13.0.0+ 2 | -------------------------------------------------------------------------------- /src/api/base/logs.js: -------------------------------------------------------------------------------- 1 | import {createAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/base/logs/', 'get', data) 4 | -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /ci/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pwd 4 | ls -la 5 | 6 | ssh root@172.17.0.120 'cd /home/hmadmin && docker-compose down && docker-compose up -d' -------------------------------------------------------------------------------- /src/module-dashboard/assets/logo-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/module-dashboard/assets/logo-64.png -------------------------------------------------------------------------------- /src/module-dashboard/assets/banner-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/module-dashboard/assets/banner-64.png -------------------------------------------------------------------------------- /assets/img/vue-element-admin-itheima logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/assets/img/vue-element-admin-itheima logo.psd -------------------------------------------------------------------------------- /src/api/example/detail.js: -------------------------------------------------------------------------------- 1 | import {createAPI, createFormAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/details/list', 'get', data) 4 | -------------------------------------------------------------------------------- /src/api/example/form.js: -------------------------------------------------------------------------------- 1 | import {createAPI, createFormAPI} from '@/utils/request' 2 | 3 | export const form = data => createAPI('/form/user', 'post', data) 4 | -------------------------------------------------------------------------------- /src/api/example/table.js: -------------------------------------------------------------------------------- 1 | import {createAPI, createFormAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/table/list', 'get', data) 4 | -------------------------------------------------------------------------------- /src/module-dashboard/assets/bigUserHeader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/module-dashboard/assets/bigUserHeader.png -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itheima2017/vue-element-admin-itheima/HEAD/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/module-dashboard/router/index.js: -------------------------------------------------------------------------------- 1 | // import Layout from '@/module-dashboard/pages/layout' 2 | // const _import = require('@/router/import_' + process.env.NODE_ENV) 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/icons/generateIconsView.js: -------------------------------------------------------------------------------- 1 | const data = { 2 | state: { 3 | iconsMap: [] 4 | }, 5 | generate(iconsMap) { 6 | this.state.iconsMap = iconsMap 7 | } 8 | } 9 | 10 | export default data 11 | -------------------------------------------------------------------------------- /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_API: '"api"' 8 | }) 9 | -------------------------------------------------------------------------------- /src/module-dashboard/components/layoutIndex.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './layoutNavbar' 2 | export { default as Sidebar } from './layoutSidebar' 3 | export { default as TagsView } from './layoutTags' 4 | export { default as AppMain } from './layoutAppMain' 5 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $blue:#324157; 2 | $light-blue:#3A71A8; 3 | $red:#C03639; 4 | $pink: #E65D6E; 5 | $green: #30B08F; 6 | $tiffany: #4AB7BD; 7 | $yellow:#FEC171; 8 | $panGreen: #30B08F; 9 | 10 | //sidebar 11 | $menuBg:#001529; 12 | $subMenuBg:#001529; 13 | $menuHover:#001529; 14 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.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 | *.zip 19 | img -------------------------------------------------------------------------------- /src/module-dashboard/pages/authredirect.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /ci/sonar_analyze.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sonar-scanner \ 4 | -Dsonar.projectKey=gitlab:$CI_COMMIT_REF_NAME:$CI_PROJECT_NAME \ 5 | -Dsonar.projectName=gitlab:$CI_COMMIT_REF_NAME:$CI_PROJECT_NAME \ 6 | -Dsonar.projectVersion=1.0.$CI_PIPELINE_ID 7 | 8 | if [ $? -eq 0 ]; then 9 | echo "sonarqube code-publish over." 10 | fi -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 黑马Admin 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Admin-Token-Itheima' 4 | 5 | export function getToken () { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken (token) { 10 | return Cookies.set(TokenKey, token) 11 | } 12 | 13 | export function removeToken () { 14 | return Cookies.remove(TokenKey) 15 | } 16 | -------------------------------------------------------------------------------- /src/module-dashboard/store/errorLog.js: -------------------------------------------------------------------------------- 1 | const errorLog = { 2 | state: { 3 | logs: [] 4 | }, 5 | mutations: { 6 | ADD_ERROR_LOG: (state, log) => { 7 | state.logs.push(log) 8 | } 9 | }, 10 | actions: { 11 | addErrorLog({ commit }, log) { 12 | commit('ADD_ERROR_LOG', log) 13 | } 14 | } 15 | } 16 | 17 | export default errorLog 18 | -------------------------------------------------------------------------------- /src/utils/i18n.js: -------------------------------------------------------------------------------- 1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview 2 | export function generateTitle(title) { 3 | const hasKey = this.$te('route.' + title) 4 | const translatedTitle = this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js 5 | 6 | if (hasKey) { 7 | return translatedTitle 8 | } 9 | return title 10 | } 11 | -------------------------------------------------------------------------------- /src/mock/index.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import TableAPI from './table' 3 | import ArticlesAPI from './articles' 4 | import DetailAPI from './detailTable' 5 | 6 | Mock.setup({ 7 | timeout: '1000' 8 | }) 9 | 10 | Mock.mock(/\/table\/list\.*/, 'get', TableAPI.list) 11 | Mock.mock(/\/articles\/list\.*/, 'get', ArticlesAPI.list) 12 | Mock.mock(/\/details\/list\.*/, 'get', DetailAPI.list) 13 | -------------------------------------------------------------------------------- /src/api/example/articles.js: -------------------------------------------------------------------------------- 1 | import {createAPI, createFormAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/articles/list', 'get', data) 4 | export const types = [ 5 | '类型一', 6 | '类型二', 7 | '类型三', 8 | '类型四', 9 | '类型五', 10 | '类型六', 11 | '类型七', 12 | '类型八', 13 | '类型九', 14 | '类型十', 15 | '类型十一', 16 | '类型十二', 17 | '类型十三', 18 | '类型十四', 19 | '类型十五', 20 | '类型十六' 21 | ] 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/Charts/mixins/resize.js: -------------------------------------------------------------------------------- 1 | import { debounce } from '@/utils' 2 | 3 | export default { 4 | mounted() { 5 | this.__resizeHanlder = debounce(() => { 6 | if (this.chart) { 7 | this.chart.resize() 8 | } 9 | }, 100) 10 | window.addEventListener('resize', this.__resizeHanlder) 11 | }, 12 | beforeDestroy() { 13 | window.removeEventListener('resize', this.__resizeHanlder) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/base/frame.js: -------------------------------------------------------------------------------- 1 | import {createAPI, createFormAPI} from '@/utils/request' 2 | 3 | export const login = data => createFormAPI('/base/frame/login', 'post', data) 4 | export const register = data => createAPI('/base/frame/register', 'post', data) 5 | export const logout = data => createAPI('/base/frame/logout', 'post', data) 6 | export const passwd = data => createAPI('/base/frame/passwd', 'post', data) 7 | export const profile = data => createAPI('/base/frame/profile', 'post', data) 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pwd 4 | ls -la 5 | 6 | cnpm i 7 | npm run build 8 | 9 | docker build -t 172.17.0.57:5000/hmadmin-vue:1.0.$CI_PIPELINE_ID . 10 | docker tag 172.17.0.57:5000/hmadmin-vue:1.0.$CI_PIPELINE_ID 172.17.0.57:5000/hmadmin-vue:latest 11 | docker push 172.17.0.57:5000/hmadmin-vue:latest 12 | 13 | docker rmi 172.17.0.57:5000/hmadmin-vue:1.0.$CI_PIPELINE_ID 14 | docker rmi 172.17.0.57:5000/hmadmin-vue:latest 15 | 16 | # docker rmi -f $(docker images | grep hmadmin-vue) -------------------------------------------------------------------------------- /src/api/base/users.js: -------------------------------------------------------------------------------- 1 | import {createAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/base/users/', 'get', data) 4 | export const simple = data => createAPI('/base/users/simple', 'get', data) 5 | export const add = data => createAPI('/base/users', 'post', data) 6 | export const update = data => createAPI(`/base/users/${data.id}`, 'put', data) 7 | export const remove = data => createAPI(`/base/users/${data.id}`, 'delete', data) 8 | export const detail = data => createAPI(`/base/users/${data.id}`, 'get', data) 9 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg组件 3 | import generateIconsView from './generateIconsView.js'// just for @/views/icons , you can delete it 4 | 5 | // register globally 6 | Vue.component('svg-icon', SvgIcon) 7 | 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | const req = require.context('./svg', false, /\.svg$/) 10 | const iconMap = requireAll(req) 11 | 12 | generateIconsView.generate(iconMap) // just for @/views/icons , you can delete it 13 | -------------------------------------------------------------------------------- /src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/TreeTable/utils/expand.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/api/base/permissions.js: -------------------------------------------------------------------------------- 1 | import {createAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/base/permissions', 'get', data) 4 | export const simple = data => createAPI('/base/permissions/simple', 'get', data) 5 | export const add = data => createAPI('/base/permissions', 'post', data) 6 | export const update = data => createAPI(`/base/permissions/${data.id}`, 'put', data) 7 | export const remove = data => createAPI(`/base/permissions/${data.id}`, 'delete', data) 8 | export const detail = data => createAPI(`/base/permissions/${data.id}`, 'get', data) 9 | -------------------------------------------------------------------------------- /src/utils/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | // 检查是否有权限 4 | export function hasPermission(roles, route) { 5 | if (roles.menus && route.name) { 6 | return roles.menus.some(role => route.name.toLowerCase() === role.toLowerCase()) 7 | } else { 8 | return false 9 | } 10 | } 11 | 12 | // 检查是否有权限点 13 | export function hasPermissionPoint(point) { 14 | let points = store.getters.roles.points 15 | if (points) { 16 | return points.some(it => it.toLowerCase() === point.toLowerCase()) 17 | } else { 18 | return false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | //globl transition css 2 | 3 | /*fade*/ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /*fade*/ 15 | .breadcrumb-enter-active, 16 | .breadcrumb-leave-active { 17 | transition: all .5s; 18 | } 19 | 20 | .breadcrumb-enter, 21 | .breadcrumb-leave-active { 22 | opacity: 0; 23 | transform: translateX(20px); 24 | } 25 | 26 | .breadcrumb-move { 27 | transition: all .5s; 28 | } 29 | 30 | .breadcrumb-leave-active { 31 | position: absolute; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - test 3 | - build 4 | - deploy 5 | 6 | sonar_analyze: 7 | stage: test 8 | script: 9 | - ci/sonar_analyze.sh 10 | tags: 11 | - hmadmin-vue 12 | 13 | sonar_preview: 14 | stage: test 15 | script: 16 | - ci/sonar_preview.sh 17 | tags: 18 | - hmadmin-vue 19 | 20 | build_image: 21 | stage: build 22 | # services: 23 | # - node:8 24 | script: 25 | - ci/build.sh 26 | tags: 27 | - hmadmin-vue 28 | 29 | deploy_image: 30 | stage: deploy 31 | # services: 32 | # - node:8 33 | script: 34 | - ci/deploy.sh 35 | tags: 36 | - hmadmin-vue 37 | -------------------------------------------------------------------------------- /src/module-form/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 表单页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:04 7 | */ 8 | 9 | // vue-router 10 | import {asyncRouterMap} from '@/router' 11 | import routerMaps from './router' 12 | // vuex 13 | import app from './store/app' 14 | 15 | export default { 16 | install(module, store) { 17 | // 注册路由 18 | asyncRouterMap.push(routerMaps[0]) 19 | // 注册状态管理 20 | if (store !== undefined) { 21 | // store.registerModule('app', app) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/module-list/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 列表页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:04 7 | */ 8 | 9 | // vue-router 10 | import {asyncRouterMap} from '@/router' 11 | import routerMaps from './router' 12 | // vuex 13 | import app from './store/app' 14 | 15 | export default { 16 | install(module, store) { 17 | // 注册路由 18 | asyncRouterMap.push(routerMaps[0]) 19 | // 注册状态管理 20 | if (store !== undefined) { 21 | // store.registerModule('app', app) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /script/module-add/template/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Description: {{description}} 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:04 7 | */ 8 | 9 | // vue-router 10 | import {asyncRouterMap} from '@/router' 11 | import routerMaps from './router' 12 | // vuex 13 | import app from './store/app' 14 | 15 | export default { 16 | install(module, store) { 17 | // 注册路由 18 | asyncRouterMap.push(routerMaps[0]) 19 | // 注册状态管理 20 | if (store !== undefined) { 21 | // store.registerModule('app', app) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-details/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description:详情页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:04 7 | */ 8 | 9 | // vue-router 10 | import {asyncRouterMap} from '@/router' 11 | import routerMaps from './router' 12 | // vuex 13 | import app from './store/app' 14 | 15 | export default { 16 | install(module, store) { 17 | // 注册路由 18 | asyncRouterMap.push(routerMaps[0]) 19 | // 注册状态管理 20 | if (store !== undefined) { 21 | // store.registerModule('app', app) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/module-manage/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description:详情页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:04 7 | */ 8 | 9 | // vue-router 10 | import {asyncRouterMap} from '@/router' 11 | import routerMaps from './router' 12 | // vuex 13 | import app from './store/app' 14 | 15 | export default { 16 | install(module, store) { 17 | // 注册路由 18 | asyncRouterMap.push(routerMaps[0]) 19 | // 注册状态管理 20 | if (store !== undefined) { 21 | // store.registerModule('app', app) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/errorLog.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from './store' 3 | 4 | // you can set only in production env show the error-log 5 | // if (process.env.NODE_ENV === 'production') { 6 | 7 | Vue.config.errorHandler = function(err, vm, info, a) { 8 | // Don't ask me why I use Vue.nextTick, it just a hack. 9 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 10 | Vue.nextTick(() => { 11 | store.dispatch('addErrorLog', { 12 | err, 13 | vm, 14 | info, 15 | url: window.location.href 16 | }) 17 | console.error(err, info) 18 | }) 19 | } 20 | 21 | // } 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/components/layoutAppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | -------------------------------------------------------------------------------- /src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-form/store/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 表单页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:33 7 | */ 8 | 9 | const app = { 10 | state: { 11 | name: '' 12 | }, 13 | mutations: { 14 | SET_NAME: (state, name) => { 15 | state.name = name 16 | } 17 | }, 18 | actions: { 19 | setName({ commit }, userInfo) { 20 | const username = userInfo.username.trim() 21 | return new Promise((resolve, reject) => { 22 | commit('SET_NAME', username) 23 | resolve() 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default app 30 | -------------------------------------------------------------------------------- /src/module-list/store/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 列表页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:33 7 | */ 8 | 9 | const app = { 10 | state: { 11 | name: '' 12 | }, 13 | mutations: { 14 | SET_NAME: (state, name) => { 15 | state.name = name 16 | } 17 | }, 18 | actions: { 19 | setName({ commit }, userInfo) { 20 | const username = userInfo.username.trim() 21 | return new Promise((resolve, reject) => { 22 | commit('SET_NAME', username) 23 | resolve() 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default app 30 | -------------------------------------------------------------------------------- /script/module-add/template/store/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Description: {{description}} 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:33 7 | */ 8 | 9 | const app = { 10 | state: { 11 | name: '' 12 | }, 13 | mutations: { 14 | SET_NAME: (state, name) => { 15 | state.name = name 16 | } 17 | }, 18 | actions: { 19 | setName({ commit }, userInfo) { 20 | const username = userInfo.username.trim() 21 | return new Promise((resolve, reject) => { 22 | commit('SET_NAME', username) 23 | resolve() 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default app 30 | -------------------------------------------------------------------------------- /src/module-details/store/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 详情页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:33 7 | */ 8 | 9 | const app = { 10 | state: { 11 | name: '' 12 | }, 13 | mutations: { 14 | SET_NAME: (state, name) => { 15 | state.name = name 16 | } 17 | }, 18 | actions: { 19 | setName({ commit }, userInfo) { 20 | const username = userInfo.username.trim() 21 | return new Promise((resolve, reject) => { 22 | commit('SET_NAME', username) 23 | resolve() 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default app 30 | -------------------------------------------------------------------------------- /src/module-manage/store/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 列表页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 16:15:33 7 | */ 8 | 9 | const app = { 10 | state: { 11 | name: '' 12 | }, 13 | mutations: { 14 | SET_NAME: (state, name) => { 15 | state.name = name 16 | } 17 | }, 18 | actions: { 19 | setName({ commit }, userInfo) { 20 | const username = userInfo.username.trim() 21 | return new Promise((resolve, reject) => { 22 | commit('SET_NAME', username) 23 | resolve() 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default app 30 | -------------------------------------------------------------------------------- /src/icons/svg/menu-fold.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lang/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import Cookies from 'js-cookie' 4 | import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang 5 | import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang 6 | import enLocale from './en' 7 | import zhLocale from './zh' 8 | 9 | Vue.use(VueI18n) 10 | 11 | const messages = { 12 | en: { 13 | ...enLocale, 14 | ...elementEnLocale 15 | }, 16 | zh: { 17 | ...zhLocale, 18 | ...elementZhLocale 19 | } 20 | } 21 | 22 | const i18n = new VueI18n({ 23 | locale: Cookies.get('language') || 'zh', // set locale 24 | messages // set locale messages 25 | }) 26 | 27 | export default i18n 28 | -------------------------------------------------------------------------------- /script/module-add/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: hans.taozhiwei 3 | * @Description: 模块安装 itheima-moduleAdd 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-13 17:26:52 7 | */ 8 | 9 | module.exports = { 10 | prompts: { 11 | name: { 12 | type: 'string', 13 | required: true, 14 | message: '模块名称' 15 | }, 16 | author: { 17 | type: 'string', 18 | required: false, 19 | message: '作者', 20 | default: 'yourname ' 21 | }, 22 | description: { 23 | type: 'string', 24 | required: false, 25 | message: '说明', 26 | default: 'xxx业务模块' 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/icons/svg/menu-unfold.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | // import app from '@/module-dashboard/store/app' 4 | // import errorLog from '@/module-dashboard/store/errorLog' 5 | // import permission from '@/module-dashboard/store/permission' 6 | // import tagsView from '@/module-dashboard/store/tagsView' 7 | // import user from '@/module-dashboard/store/user' 8 | import getters from './getters' 9 | 10 | Vue.use(Vuex) 11 | 12 | const store = new Vuex.Store({ 13 | getters 14 | }) 15 | 16 | // const store = new Vuex.Store({ 17 | // modules: { 18 | // app, 19 | // errorLog, 20 | // permission, 21 | // tagsView, 22 | // user 23 | // }, 24 | // getters 25 | // }) 26 | 27 | export default store 28 | -------------------------------------------------------------------------------- /src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | // 手动添加需要的 快捷 getters 2 | const getters = { 3 | sidebar: state => state.app.sidebar, 4 | language: state => state.app.language, 5 | visitedViews: state => state.tagsView.visitedViews, 6 | cachedViews: state => state.tagsView.cachedViews, 7 | token: state => state.user.token, 8 | avatar: state => state.user.avatar, 9 | name: state => state.user.name, 10 | introduction: state => state.user.introduction, 11 | status: state => state.user.status, 12 | roles: state => state.user.roles, 13 | setting: state => state.user.setting, 14 | permission_routers: state => state.permission.routers, 15 | addRouters: state => state.permission.addRouters, 16 | errorLogs: state => state.errorLog.logs 17 | } 18 | 19 | export default getters 20 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /src/module-dashboard/index.js: -------------------------------------------------------------------------------- 1 | // vue-router 2 | import {asyncRouterMap} from '@/router' 3 | import routerMaps from './router' 4 | // vuex 5 | import app from './store/app' 6 | import errorLog from './store/errorLog' 7 | import permission from './store/permission' 8 | import tagsView from './store/tagsView' 9 | import user from './store/user' 10 | 11 | export default { 12 | install(module, store) { 13 | // 注册路由 14 | // asyncRouterMap.push(routerMaps[0]) 15 | // 注册状态管理 16 | if (store !== undefined) { 17 | store.registerModule('app', app) 18 | store.registerModule('errorLog', errorLog) 19 | store.registerModule('permission', permission) 20 | store.registerModule('tagsView', tagsView) 21 | store.registerModule('user', user) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /script/module-add/template/router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: {{author}} 3 | * @Description: {{description}} 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-05-22 17:04:36 7 | */ 8 | 9 | import Layout from '@/module-dashboard/pages/layout' 10 | const _import = require('@/router/import_' + process.env.NODE_ENV) 11 | 12 | export default [ 13 | { 14 | path: '/{{name}}', 15 | component: Layout, 16 | redirect: 'noredirect', 17 | name: '{{name}}', 18 | meta: { 19 | title: '{{name}}管理', 20 | icon: 'component' 21 | }, 22 | children: [ 23 | { 24 | path: 'table', 25 | component: _import('{{name}}/pages/index'), 26 | name: '{{name}}-table', 27 | meta: {title: '列表模块'} 28 | } 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | 34 | 43 | -------------------------------------------------------------------------------- /src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/store/app.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const app = { 4 | state: { 5 | sidebar: { 6 | opened: !+Cookies.get('sidebarStatus') 7 | }, 8 | language: Cookies.get('language') || 'en' 9 | }, 10 | mutations: { 11 | TOGGLE_SIDEBAR: state => { 12 | if (state.sidebar.opened) { 13 | Cookies.set('sidebarStatus', 1) 14 | } else { 15 | Cookies.set('sidebarStatus', 0) 16 | } 17 | state.sidebar.opened = !state.sidebar.opened 18 | }, 19 | SET_LANGUAGE: (state, language) => { 20 | state.language = language 21 | Cookies.set('language', language) 22 | } 23 | }, 24 | actions: { 25 | toggleSideBar({ commit }) { 26 | commit('TOGGLE_SIDEBAR') 27 | }, 28 | setLanguage({ commit }, language) { 29 | commit('SET_LANGUAGE', language) 30 | } 31 | } 32 | } 33 | 34 | export default app 35 | -------------------------------------------------------------------------------- /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 (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/components/pageTool.vue: -------------------------------------------------------------------------------- 1 | 15 | 35 | 38 | -------------------------------------------------------------------------------- /src/module-manage/components/page-tool.vue: -------------------------------------------------------------------------------- 1 | 15 | 35 | 38 | -------------------------------------------------------------------------------- /src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ci/sonar_preview.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sonar-scanner -X \ 4 | -Dsonar.analysis.mode=preview \ 5 | -Dsonar.projectKey=gitlab:$CI_COMMIT_REF_NAME:$CI_PROJECT_NAME \ 6 | -Dsonar.projectName=gitlab:$CI_COMMIT_REF_NAME:$CI_PROJECT_NAME \ 7 | -Dsonar.projectVersion=1.0.$CI_PIPELINE_ID \ 8 | -Dsonar.issuesReport.html.enable=true \ 9 | -Dsonar.gitlab.project_id=$CI_PROJECT_ID \ 10 | -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA \ 11 | -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME 12 | 13 | # -Dsonar.gitlab.ignore_certificate=true \ 14 | # -Dsonar.gitlab.disable_global_comment=true \ 15 | # -Dsonar.gitlab.unique_issue_per_inline=true 16 | # -Dsonar.gitlab.failure_notification_mode=exit-code 17 | # -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA \ 18 | # -Dsonar.gitlab.disable_global_comment=true \ 19 | # -Dsonar.gitlab.unique_issue_per_inline=true \ 20 | # -Dsonar.gitlab.failure_notification_mode=exit-code \ 21 | # -Dsonar.gitlab.only_issue_from_commit_file=false 22 | 23 | if [ $? -eq 0 ]; then 24 | echo "sonarqube code-publish over." 25 | fi -------------------------------------------------------------------------------- /src/module-details/router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 列表页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-17 09:48:44 7 | */ 8 | 9 | import Layout from '@/module-dashboard/pages/layout' 10 | const _import = require('@/router/import_' + process.env.NODE_ENV) 11 | 12 | export default [ 13 | { 14 | path: '/details', 15 | component: Layout, 16 | redirect: 'noredirect', 17 | name: 'details', 18 | meta: { 19 | title: 'details', 20 | icon: 'tab' 21 | }, 22 | children: [ 23 | { 24 | path: 'basics-details', 25 | component: _import('details/pages/basicsDetails'), 26 | name: 'basics-details', 27 | meta: {title: 'BasicsDetails'} 28 | }, 29 | { 30 | path: 'senior-details', 31 | component: _import('details/pages/seniorDetails'), 32 | name: 'senior-details', 33 | meta: {title: 'seniorDetails'} 34 | } 35 | ] 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /src/module-dashboard/components/layoutSidebar.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 35 | -------------------------------------------------------------------------------- /src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/module-manage/router/index.js: -------------------------------------------------------------------------------- 1 | import Layout from '@/module-dashboard/pages/layout' 2 | const _import = require('@/router/import_' + process.env.NODE_ENV) 3 | 4 | export default [ 5 | { 6 | path: '/base', 7 | component: Layout, 8 | redirect: 'noredirect', 9 | name: 'base', 10 | meta: { 11 | title: 'manage', 12 | icon: 'component' 13 | }, 14 | children: [ 15 | { 16 | path: 'users', 17 | component: _import('manage/pages/users'), 18 | name: 'base-users', 19 | meta: {title: 'users'} 20 | }, 21 | { 22 | path: 'menus', 23 | name: 'base-menus', 24 | component: _import('manage/pages/menus'), 25 | meta: {title: 'menus'} 26 | }, 27 | { 28 | path: 'permissions', 29 | name: 'base-permissions', 30 | component: _import('manage/pages/permissions'), 31 | meta: {title: 'permissions'} 32 | }, 33 | { 34 | path: 'logs', 35 | name: 'base-logs', 36 | component: _import('manage/pages/logs'), 37 | meta: {title: 'logs'} 38 | } 39 | ] 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present itheima 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 | -------------------------------------------------------------------------------- /src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.host.url=http://sonar.itcast.cn 2 | sonar.login=b45c1cfb006d1d5c359d5b101eef867dfe0f5e91 3 | sonar.sourceEncoding=UTF-8 4 | sonar.modules=javascript-module 5 | # sonar.modules=java-module,javascript-module,html-module,css-module 6 | 7 | # sonar.sources=. 8 | # sonar.tests=test 9 | # sonar.language=js 10 | # sonar.exclusions=test 11 | 12 | # # Java module 13 | # java-module.sonar.projectName=java-module 14 | # java-module.sonar.language=java 15 | # # .表示projectBaseDir指定的目录 16 | # java-module.sonar.sources=. 17 | # java-module.sonar.projectBaseDir=src 18 | # sonar.binaries=classes 19 | 20 | # JavaScript module 21 | javascript-module.sonar.projectName=javascript-module 22 | javascript-module.sonar.language=js 23 | javascript-module.sonar.sources=. 24 | javascript-module.sonar.projectBaseDir=src 25 | 26 | # Html module 27 | # html-module.sonar.projectName=html-module 28 | # html-module.sonar.language=web 29 | # html-module.sonar.sources=. 30 | # html-module.sonar.projectBaseDir=web 31 | 32 | # CSS module 33 | # css-module.sonar.projectName=css-module 34 | # css-module.sonar.language=css 35 | # css-module.sonar.sources=. 36 | # css-module.sonar.projectBaseDir=web 37 | -------------------------------------------------------------------------------- /src/module-list/router/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 列表页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-17 09:48:44 7 | */ 8 | 9 | import Layout from '@/module-dashboard/pages/layout' 10 | const _import = require('@/router/import_' + process.env.NODE_ENV) 11 | 12 | export default [ 13 | { 14 | path: '/list', 15 | component: Layout, 16 | redirect: 'noredirect', 17 | name: 'list', 18 | meta: { 19 | title: 'list', 20 | icon: 'table' 21 | }, 22 | children: [ 23 | { 24 | path: 'table-list', 25 | component: _import('list/pages/table-list'), 26 | name: 'table-list', 27 | meta: {title: 'tableList'} 28 | }, 29 | { 30 | path: 'basic-list', 31 | component: _import('list/pages/basic-list'), 32 | name: 'basic-list', 33 | meta: {title: 'basicList'} 34 | }, 35 | { 36 | path: 'card-list', 37 | component: _import('list/pages/card-list'), 38 | name: 'card-list', 39 | meta: {title: 'cardList'} 40 | } 41 | ] 42 | } 43 | ] 44 | -------------------------------------------------------------------------------- /src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/LangSelect/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 43 | -------------------------------------------------------------------------------- /src/mock/table.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { param2Obj } from '@/utils' 3 | 4 | const List = [] 5 | const count = 100 6 | 7 | for (let i = 0; i < count; i++) { 8 | List.push(Mock.mock({ 9 | id: '@increment', 10 | timestamp: +Mock.Random.date('T'), 11 | author: '@cname', 12 | reviewer: '@cname', 13 | title: '@csentence(15, 45)', 14 | forecast: '@float(0, 100, 2, 2)', 15 | importance: '@integer(1, 3)', 16 | 'type|1': ['CN', 'US', 'JP', 'EU'], 17 | 'status|1': ['published', 'draft', 'deleted'], 18 | display_time: '@datetime', 19 | pageviews: '@integer(300, 5000)' 20 | })) 21 | } 22 | 23 | export default { 24 | list: config => { 25 | const { type, title, page = 1, limit = 20, sort } = param2Obj(config.url) 26 | 27 | let mockList = List.filter(item => { 28 | if (type && item.type !== type) return false 29 | if (title && item.title.indexOf(title) < 0) return false 30 | return true 31 | }) 32 | 33 | if (sort === '-id') { 34 | mockList = mockList.reverse() 35 | } 36 | 37 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) 38 | 39 | return { 40 | total: mockList.length, 41 | items: pageList 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/TreeTable/utils/dataTranslate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: sunlandong 3 | * @Date: 2017-03-11 12:06:49 4 | * @Last Modified by: sunlandong 5 | * @Last Modified time: 2017-03-11 16:30:03 6 | */ 7 | 8 | import Vue from 'vue' 9 | function DataTransfer (data) { 10 | if (!(this instanceof DataTransfer)) { 11 | return new DataTransfer(data, null, null) 12 | } 13 | } 14 | 15 | DataTransfer.treeToArray = function (data, parent, level, expandedAll) { 16 | let tmp = [] 17 | Array.from(data).forEach(function (record) { 18 | if (record._expanded === undefined) { 19 | Vue.set(record, '_expanded', expandedAll) 20 | } 21 | if (parent) { 22 | Vue.set(record, '_parent', parent) 23 | } 24 | let _level = 0 25 | if (level !== undefined && level !== null) { 26 | _level = level + 1 27 | } 28 | Vue.set(record, '_level', _level) 29 | tmp.push(record) 30 | if (record.childs && record.childs.length > 0) { 31 | let childs = DataTransfer.treeToArray(record.childs, record, _level, expandedAll) 32 | tmp = tmp.concat(childs) 33 | } 34 | if (record.points && record.points.length > 0) { 35 | let points = DataTransfer.treeToArray(record.points, record, _level, expandedAll) 36 | tmp = tmp.concat(points) 37 | } 38 | }) 39 | return tmp 40 | } 41 | 42 | export default DataTransfer 43 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | "no-undef": 0, //不允许未声明的变量 25 | "no-unused-vars": [0, {"vars": "all", "args": "after-used"}], //不允许有声明后未使用的变量或者参数 26 | "no-tabs": 0, //不允许tabs 27 | 28 | "no-trailing-spaces": 0, 29 | "padded-blocks": 0, 30 | "no-control-regex": 0, 31 | 32 | // allow async-await 33 | 'generator-star-spacing': 'off', 34 | 35 | "indent": [0, 2],//缩进风格 36 | // "camelcase": [0, {"properties": "never"}], //强制驼峰命名规则 37 | "space-before-function-paren": [0, {"anonymous": "always", "named": "never"}], //函数定义时括号前的空格 38 | // allow debugger during development 39 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 40 | } 41 | } -------------------------------------------------------------------------------- /src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/store/permission.js: -------------------------------------------------------------------------------- 1 | import { asyncRouterMap, constantRouterMap } from '@/router' 2 | import { hasPermission } from '@/utils/permission' 3 | 4 | /** 5 | * 递归过滤异步路由表,返回符合用户角色权限的路由表 6 | * @param asyncRouterMap 7 | * @param roles 8 | */ 9 | function filterAsyncRouter(asyncRouterMap, roles) { 10 | const accessedRouters = asyncRouterMap.filter(route => { 11 | if (hasPermission(roles, route)) { 12 | if (route.children && route.children.length) { 13 | route.children = filterAsyncRouter(route.children, roles) 14 | } 15 | return true 16 | } 17 | return false 18 | }) 19 | return accessedRouters 20 | } 21 | 22 | const permission = { 23 | state: { 24 | routers: constantRouterMap, 25 | addRouters: [] 26 | }, 27 | mutations: { 28 | SET_ROUTERS: (state, routers) => { 29 | state.addRouters = routers 30 | state.routers = constantRouterMap.concat(routers) 31 | } 32 | }, 33 | actions: { 34 | GenerateRoutes({ commit }, data) { 35 | return new Promise(resolve => { 36 | // const { roles } = data 37 | // let accessedRouters = filterAsyncRouter(asyncRouterMap, roles) 38 | // commit('SET_ROUTERS', accessedRouters) 39 | commit('SET_ROUTERS', asyncRouterMap) 40 | resolve() 41 | }) 42 | } 43 | } 44 | } 45 | 46 | export default permission 47 | -------------------------------------------------------------------------------- /src/module-form/router/step.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: taoshiwei 3 | * @Description: 表单页 4 | * @Date: 2018-04-13 16:13:27 5 | * @Last Modified by: hans.taozhiwei 6 | * @Last Modified time: 2018-04-17 09:29:01 7 | */ 8 | import activePublic from '../pages/activePublic/index' 9 | import Layout from '@/module-dashboard/pages/layout' 10 | const _import = require('@/router/import_' + process.env.NODE_ENV) 11 | 12 | export default [ 13 | { 14 | path: '/activePublic', 15 | component: activePublic, 16 | children: [ 17 | { 18 | path: '/', 19 | component: _import('form/pages/activePublic/step1'), 20 | name: 'step0', 21 | meta: { 22 | title: 'step' 23 | } 24 | }, 25 | { 26 | path: 'step1', 27 | component: _import('form/pages/activePublic/step1'), 28 | name: 'step1', 29 | meta: { 30 | title: 'step' 31 | } 32 | }, 33 | { 34 | path: 'step2', 35 | component: _import('form/pages/activePublic/step2'), 36 | name: 'step2', 37 | meta: { 38 | title: 'step' 39 | } 40 | }, 41 | { 42 | path: 'step3', 43 | component: _import('form/pages/activePublic/step3'), 44 | name: 'step3', 45 | meta: { 46 | title: 'step' 47 | } 48 | } 49 | ] 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![vue](https://img.shields.io/badge/vue-2.5.9-brightgreen.svg?style=flat-square ':no-zoom')](https://github.com/vuejs/vue) 2 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square ':no-zoom')](https://github.com/itheima2017/vue-element-admin-itheima/blob/master/LICENSE) 3 | [![GitHub release](https://img.shields.io/github/release/itheima2017/vue-element-admin-itheima.svg?style=flat-square ':no-zoom')](https://github.com/itheima2017/vue-element-admin-itheima/releases) 4 | [![GitHub stars](https://img.shields.io/github/stars/itheima2017/vue-element-admin-itheima.svg?style=flat-square&label=Stars ':no-zoom')](https://github.com/itheima2017/vue-element-admin-itheima) 5 | 6 | # 说明 7 | 8 | `黑马Admin` 的前端项目 9 | 10 | [安装及快速开始 go!](http://itheimaadmin.itcast.cn/book/help/#/getting-started) 11 | 12 | * [在线演示](http://itheimaadmin.itcast.cn/preview/vue/dist) 13 | * 代码 14 | * [前端 Vue](https://github.com/itheima2017/vue-element-admin-itheima) 15 | * [后端 Java](https://github.com/itheima2017/vue-element-admin-api-java-itheima) 16 | * 文档 17 | * [API 接口文档](http://itheimaadmin.itcast.cn/book/api/_book/) 18 | * [使用帮助文档](http://itheimaadmin.itcast.cn/book/help/) 19 | 20 | # 版权 21 | 22 | [MIT](https://github.com/itheima2017/vue-element-admin-itheima/blob/master/LICENSE) license. 23 | 24 | @传智研究院-研发部 25 | 26 | 江苏传智播客教育科技股份有限公司  版权所有 Copyright 2006-2018, All Rights Reserved 27 | -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 16/11/18. 3 | */ 4 | 5 | export function isvalidUsername(str) { 6 | const validMap = ['admin', 'editor'] 7 | return validMap.indexOf(str.trim()) >= 0 8 | } 9 | 10 | /* 合法uri */ 11 | export function validateURL(textval) { 12 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 13 | return urlregex.test(textval) 14 | } 15 | 16 | /* 小写字母 */ 17 | export function validateLowerCase(str) { 18 | const reg = /^[a-z]+$/ 19 | return reg.test(str) 20 | } 21 | 22 | /* 大写字母 */ 23 | export function validateUpperCase(str) { 24 | const reg = /^[A-Z]+$/ 25 | return reg.test(str) 26 | } 27 | 28 | /* 大小写字母 */ 29 | export function validatAlphabets(str) { 30 | const reg = /^[A-Za-z]+$/ 31 | return reg.test(str) 32 | } 33 | 34 | /** 35 | * validate email 36 | * @param email 37 | * @returns {boolean} 38 | */ 39 | export function validateEmail(email) { 40 | // eslint-disable-next-line 41 | const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 42 | return re.test(email) 43 | } 44 | -------------------------------------------------------------------------------- /src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | //覆盖一些element-ui样式 2 | 3 | .el-breadcrumb__inner, .el-breadcrumb__inner a{ 4 | font-weight: 400!important; 5 | } 6 | 7 | .el-upload { 8 | input[type="file"] { 9 | display: none !important; 10 | } 11 | } 12 | 13 | .el-upload__input { 14 | display: none; 15 | } 16 | 17 | .cell { 18 | .el-tag { 19 | margin-right: 0px; 20 | } 21 | } 22 | 23 | .small-padding { 24 | .cell { 25 | padding-left: 5px; 26 | padding-right: 5px; 27 | } 28 | } 29 | 30 | .fixed-width{ 31 | .el-button--mini{ 32 | padding: 7px 10px; 33 | width: 60px; 34 | } 35 | } 36 | 37 | .status-col { 38 | .cell { 39 | padding: 0 10px; 40 | text-align: center; 41 | .el-tag { 42 | margin-right: 0px; 43 | } 44 | } 45 | } 46 | 47 | //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461 48 | .el-dialog { 49 | transform: none; 50 | left: 0; 51 | position: relative; 52 | margin: 0 auto; 53 | } 54 | 55 | //文章页textarea修改样式 56 | .article-textarea { 57 | textarea { 58 | padding-right: 40px; 59 | resize: none; 60 | border: none; 61 | border-radius: 0px; 62 | border-bottom: 1px solid #bfcbd9; 63 | } 64 | } 65 | 66 | //element ui upload 67 | .upload-container { 68 | .el-upload { 69 | width: 100%; 70 | .el-upload-dragger { 71 | width: 100%; 72 | height: 200px; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/utils/openWindow.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by jiachenpan on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | 9 | export default function openWindow(url, title, w, h) { 10 | // Fixes dual-screen position Most browsers Firefox 11 | const dualScreenLeft = 12 | window.screenLeft !== undefined ? window.screenLeft : screen.left 13 | const dualScreenTop = 14 | window.screenTop !== undefined ? window.screenTop : screen.top 15 | 16 | const width = window.innerWidth 17 | ? window.innerWidth 18 | : document.documentElement.clientWidth 19 | ? document.documentElement.clientWidth 20 | : screen.width 21 | const height = window.innerHeight 22 | ? window.innerHeight 23 | : document.documentElement.clientHeight 24 | ? document.documentElement.clientHeight 25 | : screen.height 26 | 27 | const left = width / 2 - w / 2 + dualScreenLeft 28 | const top = height / 2 - h / 2 + dualScreenTop 29 | const newWindow = window.open( 30 | url, 31 | title, 32 | 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + 33 | w + 34 | ', height=' + 35 | h + 36 | ', top=' + 37 | top + 38 | ', left=' + 39 | left 40 | ) 41 | 42 | // Puts focus on the newWindow 43 | if (window.focus) { 44 | newWindow.focus() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/mock/articles.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { 3 | param2Obj 4 | } from '@/utils' 5 | const List = [] 6 | const count = 100 7 | for (let i = 0; i < count; i++) { 8 | List.push(Mock.mock({ 9 | id: '@increment', 10 | title: '@ctitle', 11 | author: '@cname', 12 | 'icon|1': ['alipay', 'angular', 'antDesign', 'antDesignPro', 'bootstrap', 'vue', 'webpack'], 13 | tags: ['html', 'javascript', 'css'], 14 | description: '@csentence(50, 350)', 15 | url: '@url', 16 | timestamp: +Mock.Random.date('T'), 17 | displayTime: '@datetime', 18 | stars: '@integer(300, 5000)', 19 | likes: '@integer(300, 5000)', 20 | messages: '@integer(300, 5000)' 21 | })) 22 | } 23 | export default { 24 | list: config => { 25 | const { 26 | title, 27 | type, 28 | owner, 29 | page = 1, 30 | limit = 20, 31 | sort 32 | } = param2Obj(config.url) 33 | let mockList 34 | mockList = List.filter(item => { 35 | if (type && item.type !== type) return false 36 | if (owner && item.owner !== owner) return false 37 | if (title && item.title.indexOf(title) < 0) return false 38 | return true 39 | }) 40 | if (sort === '-id') { 41 | mockList = mockList.reverse() 42 | } 43 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) 44 | return { 45 | total: mockList.length, 46 | items: pageList 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/icons/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import 'normalize.css/normalize.css'// A modern alternative to CSS resets 3 | import Element from 'element-ui' 4 | import 'element-ui/lib/theme-chalk/index.css' 5 | import '@/styles/index.scss' // global css 6 | import App from './App' 7 | import router from './router' 8 | import store from './store' 9 | import i18n from './lang' // Internationalization 10 | import './icons' // icon 11 | import './errorLog'// error log 12 | import * as filters from './filters' // global filters 13 | import './mock' // simulation data 14 | // font-awesome 15 | import 'font-awesome/css/font-awesome.css' 16 | /* 17 | * 注册 - 业务模块 18 | */ 19 | import dashboard from '@/module-dashboard/' // 面板 20 | import base from '@/module-manage/' // 用户管理 21 | import list from '@/module-list/' // 列表页 22 | import form from '@/module-form/' // 表单页 23 | import details from '@/module-details/' // 表单页 24 | 25 | Vue.use(dashboard, store) 26 | Vue.use(base, store) 27 | Vue.use(list, store) 28 | Vue.use(form, store) 29 | Vue.use(details, store) 30 | 31 | /* 32 | * 注册 - 组件 33 | */ 34 | 35 | // 饿了么 36 | Vue.use(Element, { 37 | size: 'medium', // set element-ui default size 38 | i18n: (key, value) => i18n.t(key, value) 39 | }) 40 | // 过滤器 41 | Object.keys(filters).forEach(key => { 42 | Vue.filter(key, filters[key]) 43 | }) 44 | 45 | Vue.config.productionTip = false 46 | 47 | /* eslint-disable */ 48 | new Vue({ 49 | el: '#app', 50 | router, 51 | store, 52 | i18n, 53 | template: '', 54 | components: { App } 55 | }) 56 | -------------------------------------------------------------------------------- /src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/mock/detailTable.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { param2Obj } from '@/utils' 3 | 4 | const List = [] 5 | const count = 100 6 | 7 | for (let i = 0; i < count; i++) { 8 | List.push(Mock.mock({ 9 | id: '@increment', 10 | timestamp: +Mock.Random.date('T'), 11 | author: '@cname', 12 | reviewer: '@cname', 13 | 'name|1': ['HUAWEI', 'apple', 'Lenovo', 'DELL'], 14 | 'barcode|1': ['123456789', '987654321', '456987123', '896532147'], 15 | 'price|1': ['2', '2.5', '3', '8'], 16 | moneyall: '', 17 | 'step|1': ['取货员接单', ' 联系客户', '取货员接单', '申请审批通过', '发起退货申请'], 18 | 'number|1': ['2', '3', '5', '2'], 19 | 'status|1': ['进行中', '成功', '失败'], 20 | display_time: '@datetime', 21 | 'nameId|1': ['取货员 ID6666', '系统', '用户'], 22 | 'time|1': ['1h', '10mins', '5mins'], 23 | 'type|1': ['创建订单', '提交订单', '部门初审', '财务复审'], 24 | 'remarks|1': ['--', '暂无'] 25 | })) 26 | } 27 | 28 | export default { 29 | list: config => { 30 | const { type, title, page = 1, limit = 20, sort } = param2Obj(config.url) 31 | 32 | let mockList = List.filter(item => { 33 | if (type && item.type !== type) return false 34 | if (title && item.title.indexOf(title) < 0) return false 35 | return true 36 | }) 37 | 38 | if (sort === '-id') { 39 | mockList = mockList.reverse() 40 | } 41 | 42 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) 43 | 44 | return { 45 | total: mockList.length, 46 | items: pageList 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | &::-webkit-scrollbar { 14 | width: 6px; 15 | } 16 | &::-webkit-scrollbar-thumb { 17 | background: #99a9bf; 18 | border-radius: 20px; 19 | } 20 | } 21 | 22 | @mixin relative { 23 | position: relative; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | @mixin pct($pct) { 29 | width: #{$pct}; 30 | position: relative; 31 | margin: 0 auto; 32 | } 33 | 34 | @mixin triangle($width, $height, $color, $direction) { 35 | $width: $width/2; 36 | $color-border-style: $height solid $color; 37 | $transparent-border-style: $width solid transparent; 38 | height: 0; 39 | width: 0; 40 | @if $direction==up { 41 | border-bottom: $color-border-style; 42 | border-left: $transparent-border-style; 43 | border-right: $transparent-border-style; 44 | } 45 | @else if $direction==right { 46 | border-left: $color-border-style; 47 | border-top: $transparent-border-style; 48 | border-bottom: $transparent-border-style; 49 | } 50 | @else if $direction==down { 51 | border-top: $color-border-style; 52 | border-left: $transparent-border-style; 53 | border-right: $transparent-border-style; 54 | } 55 | @else if $direction==left { 56 | border-right: $color-border-style; 57 | border-top: $transparent-border-style; 58 | border-bottom: $transparent-border-style; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/icons/svg/copyright.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/pages/layout.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/icons/svg/shoppingCard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 44 | 45 | 57 | -------------------------------------------------------------------------------- /src/components/ScrollBar/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 46 | 47 | 61 | -------------------------------------------------------------------------------- /src/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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.js 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 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/btn.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | @mixin colorBtn($color) { 4 | background: $color; 5 | &:hover { 6 | color: $color; 7 | &:before, 8 | &:after { 9 | background: $color; 10 | } 11 | } 12 | } 13 | 14 | .blue-btn { 15 | @include colorBtn($blue) 16 | } 17 | 18 | .light-blue-btn { 19 | @include colorBtn($light-blue) 20 | } 21 | 22 | .red-btn { 23 | @include colorBtn($red) 24 | } 25 | 26 | .pink-btn { 27 | @include colorBtn($pink) 28 | } 29 | 30 | .green-btn { 31 | @include colorBtn($green) 32 | } 33 | 34 | .tiffany-btn { 35 | @include colorBtn($tiffany) 36 | } 37 | 38 | .yellow-btn { 39 | @include colorBtn($yellow) 40 | } 41 | 42 | .pan-btn { 43 | font-size: 14px; 44 | color: #fff; 45 | padding: 14px 36px; 46 | border-radius: 8px; 47 | border: none; 48 | outline: none; 49 | margin-right: 25px; 50 | transition: 600ms ease all; 51 | position: relative; 52 | display: inline-block; 53 | &:hover { 54 | background: #fff; 55 | &:before, 56 | &:after { 57 | width: 100%; 58 | transition: 600ms ease all; 59 | } 60 | } 61 | &:before, 62 | &:after { 63 | content: ''; 64 | position: absolute; 65 | top: 0; 66 | right: 0; 67 | height: 2px; 68 | width: 0; 69 | transition: 400ms ease all; 70 | } 71 | &::after { 72 | right: inherit; 73 | top: inherit; 74 | left: 0; 75 | bottom: 0; 76 | } 77 | } 78 | 79 | .custom-button { 80 | display: inline-block; 81 | line-height: 1; 82 | white-space: nowrap; 83 | cursor: pointer; 84 | background: #fff; 85 | color: #fff; 86 | -webkit-appearance: none; 87 | text-align: center; 88 | box-sizing: border-box; 89 | outline: 0; 90 | margin: 0; 91 | padding: 10px 15px; 92 | font-size: 14px; 93 | border-radius: 4px; 94 | } 95 | 96 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-form/pages/activePublic/step3.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 64 | 72 | -------------------------------------------------------------------------------- /src/lang/zh.js: -------------------------------------------------------------------------------- 1 | export default { 2 | route: { 3 | dashboard: '首页', 4 | manage: '后台管理', 5 | users: '用户管理', 6 | menus: '菜单管理', 7 | permissions: '权限管理', 8 | logs: '日志管理', 9 | example: '示例', 10 | table: '数据列表', 11 | 12 | list: '列表页', 13 | tableList: '查询表格', 14 | basicList: '标准列表', 15 | cardList: '卡片列表', 16 | 17 | form: '表单页', 18 | basicForm: '基础表单', 19 | stepForm: '分步表单', 20 | advancedList: '高级表单', 21 | step: '步骤', 22 | 23 | details: '详情页', 24 | BasicsDetails: '基础详情页', 25 | seniorDetails: '高级详情页' 26 | }, 27 | navbar: { 28 | search: '站内搜索', 29 | logOut: '退出登录', 30 | dashboard: '首页', 31 | github: '项目地址', 32 | screenfull: '全屏', 33 | theme: '换肤', 34 | lang: '多语言', 35 | error: '错误日志' 36 | }, 37 | login: { 38 | title: '黑马Admin', 39 | logIn: '登录', 40 | username: '账号', 41 | password: '密码', 42 | any: '随便填', 43 | thirdparty: '第三方登录', 44 | thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!' 45 | }, 46 | tagsView: { 47 | close: '关闭', 48 | closeOthers: '关闭其它', 49 | closeAll: '关闭所有' 50 | }, 51 | table: { 52 | title: '请输入用户', 53 | search: '搜索', 54 | add: '添加', 55 | addUser: '新增用户', 56 | id: '序号', 57 | email: '邮箱', 58 | phone: '联系电话', 59 | username: '用户名', 60 | permissionNew: '新增权限组', 61 | permissionUser: '权限组名称', 62 | imdsAi: '高级接口授权', 63 | avatar: '头像', 64 | introduction: '介绍', 65 | paddword: '密码', 66 | powerCode: '权限代码', 67 | powerDistriB: '权限分配', 68 | powerTitle: '权限标题', 69 | powerNav: '主导航', 70 | actions: '操作', 71 | edit: '编辑', 72 | delete: '删除', 73 | cancel: '取 消', 74 | confirm: '确 定', 75 | operationType: '操作类型', 76 | operationDate: '操作时间', 77 | date: '日期', 78 | operator: '操作人', 79 | results: '执行结果', 80 | describe: '描述', 81 | preserve: '保存', 82 | signOut: '退出' 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 55 | 56 | 67 | -------------------------------------------------------------------------------- /src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/components/layoutSidebarItem.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 53 | -------------------------------------------------------------------------------- /src/lang/en.js: -------------------------------------------------------------------------------- 1 | export default { 2 | route: { 3 | dashboard: 'Dashboard', 4 | manage: 'manage', 5 | users: 'users', 6 | menus: 'menus', 7 | permissions: 'permissions', 8 | logs: 'logs', 9 | example: 'example', 10 | table: 'table', 11 | 12 | list: 'list', 13 | tableList: 'table list', 14 | basicList: 'basic list', 15 | cardList: 'card list', 16 | 17 | form: 'form', 18 | basicForm: 'basic form', 19 | stepForm: 'step form', 20 | advancedList: 'advanced form', 21 | step: 'step', 22 | 23 | details: 'details', 24 | BasicsDetails: 'Basic details page', 25 | seniorDetails: 'Advanced details page' 26 | }, 27 | navbar: { 28 | search: 'search', 29 | logOut: 'Log Out', 30 | dashboard: 'Dashboard', 31 | github: 'Github', 32 | screenfull: 'screenfull', 33 | theme: 'theme', 34 | lang: 'i18n', 35 | error: 'error log' 36 | }, 37 | login: { 38 | title: 'itheima login', 39 | logIn: 'Log in', 40 | username: 'Username', 41 | password: 'Password', 42 | any: 'any', 43 | thirdparty: 'Third', 44 | thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !' 45 | }, 46 | tagsView: { 47 | close: 'Close', 48 | closeOthers: 'Close Others', 49 | closeAll: 'Close All' 50 | }, 51 | table: { 52 | title: 'Title', 53 | search: 'Search', 54 | add: 'add', 55 | addUser: 'addUser', 56 | id: 'ID', 57 | email: 'Email', 58 | phone: 'Phone', 59 | username: 'User', 60 | permissionNew: 'permissionNew', 61 | permissionUser: 'Permission', 62 | imdsAi: 'Advanced interface authorization', 63 | avatar: 'Avatar', 64 | introduction: 'Introduction', 65 | paddword: 'paddWord', 66 | powerCode: 'Permission code', 67 | powerTitle: 'Permission title', 68 | actions: 'Actions', 69 | edit: 'Edit', 70 | delete: 'Delete', 71 | cancel: 'Cancel', 72 | confirm: 'Confirm', 73 | operationType: 'operationType', 74 | operationDate: 'operationDate', 75 | date: 'Date', 76 | operator: 'operator', 77 | results: 'results of enforcement', 78 | describe: 'Pedagogical operation', 79 | preserve: 'preserve', 80 | signOut: 'sign out' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/module-dashboard/components/dashboardPieChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 85 | -------------------------------------------------------------------------------- /src/components/ScrollPane/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 67 | 68 | 79 | -------------------------------------------------------------------------------- /src/module-dashboard/store/tagsView.js: -------------------------------------------------------------------------------- 1 | const tagsView = { 2 | state: { 3 | visitedViews: [], 4 | cachedViews: [] 5 | }, 6 | mutations: { 7 | ADD_VISITED_VIEWS: (state, view) => { 8 | if (state.visitedViews.some(v => v.path === view.path)) return 9 | state.visitedViews.push({ 10 | name: view.name, 11 | path: view.path, 12 | title: view.meta.title || 'no-name' 13 | }) 14 | if (!view.meta.noCache) { 15 | state.cachedViews.push(view.name) 16 | } 17 | }, 18 | DEL_VISITED_VIEWS: (state, view) => { 19 | for (const [i, v] of state.visitedViews.entries()) { 20 | if (v.path === view.path) { 21 | state.visitedViews.splice(i, 1) 22 | break 23 | } 24 | } 25 | for (const i of state.cachedViews) { 26 | if (i === view.name) { 27 | const index = state.cachedViews.indexOf(i) 28 | state.cachedViews.splice(index, 1) 29 | break 30 | } 31 | } 32 | }, 33 | DEL_OTHERS_VIEWS: (state, view) => { 34 | for (const [i, v] of state.visitedViews.entries()) { 35 | if (v.path === view.path) { 36 | state.visitedViews = state.visitedViews.slice(i, i + 1) 37 | break 38 | } 39 | } 40 | for (const i of state.cachedViews) { 41 | if (i === view.name) { 42 | const index = state.cachedViews.indexOf(i) 43 | state.cachedViews = state.cachedViews.slice(index, i + 1) 44 | break 45 | } 46 | } 47 | }, 48 | DEL_ALL_VIEWS: (state) => { 49 | state.visitedViews = [] 50 | state.cachedViews = [] 51 | } 52 | }, 53 | actions: { 54 | addVisitedViews({ commit }, view) { 55 | commit('ADD_VISITED_VIEWS', view) 56 | }, 57 | delVisitedViews({ commit, state }, view) { 58 | return new Promise((resolve) => { 59 | commit('DEL_VISITED_VIEWS', view) 60 | resolve([...state.visitedViews]) 61 | }) 62 | }, 63 | delOthersViews({ commit, state }, view) { 64 | return new Promise((resolve) => { 65 | commit('DEL_OTHERS_VIEWS', view) 66 | resolve([...state.visitedViews]) 67 | }) 68 | }, 69 | delAllViews({ commit, state }) { 70 | return new Promise((resolve) => { 71 | commit('DEL_ALL_VIEWS') 72 | resolve([...state.visitedViews]) 73 | }) 74 | } 75 | } 76 | } 77 | 78 | export default tagsView 79 | -------------------------------------------------------------------------------- /src/icons/svg/zip.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/module-dashboard/pages/401.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 52 | 53 | 89 | -------------------------------------------------------------------------------- /src/module-dashboard/components/loginSocialSignin.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 48 | 49 | 82 | -------------------------------------------------------------------------------- /src/icons/svg/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/module-dashboard/components/dashboardBarChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 107 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | // Paths 10 | assetsSubDirectory: 'static', 11 | assetsPublicPath: '', 12 | proxyTable: { 13 | '/api': { 14 | // target: 'https://www.easy-mock.com/mock/5ab213e33666166110a94928/admin', 15 | // target: 'http://172.17.0.58:7999', 16 | // target: 'http://127.0.0.1:7001', 17 | target: 'http://172.17.0.120:8090', 18 | changeOrigin: true, 19 | pathRewrite: { 20 | '^/api': '' 21 | } 22 | } 23 | }, 24 | 25 | // Various Dev Server settings 26 | host: 'localhost', // can be overwritten by process.env.HOST 27 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 28 | autoOpenBrowser: false, 29 | errorOverlay: true, 30 | notifyOnErrors: true, 31 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 32 | 33 | // Use Eslint Loader? 34 | // If true, your code will be linted during bundling and 35 | // linting errors and warnings will be shown in the console. 36 | useEslint: true, 37 | // If true, eslint errors and warnings will also be shown in the error overlay 38 | // in the browser. 39 | showEslintErrorsInOverlay: false, 40 | 41 | /** 42 | * Source Maps 43 | */ 44 | 45 | // https://webpack.js.org/configuration/devtool/#development 46 | devtool: 'cheap-module-eval-source-map', 47 | 48 | // If you have problems debugging vue-files in devtools, 49 | // set this to false - it *may* help 50 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 51 | cacheBusting: true, 52 | 53 | cssSourceMap: true 54 | }, 55 | 56 | build: { 57 | // Template for index.html 58 | index: path.resolve(__dirname, '../dist/index.html'), 59 | 60 | // Paths 61 | assetsRoot: path.resolve(__dirname, '../dist'), 62 | assetsSubDirectory: 'static', 63 | assetsPublicPath: '', 64 | 65 | /** 66 | * Source Maps 67 | */ 68 | 69 | productionSourceMap: true, 70 | // https://webpack.js.org/configuration/devtool/#production 71 | devtool: '#source-map', 72 | 73 | // Gzip off by default as many popular static hosts such as 74 | // Surge or Netlify already gzip all static assets for you. 75 | // Before setting to `true`, make sure to: 76 | // npm install --save-dev compression-webpack-plugin 77 | productionGzip: false, 78 | productionGzipExtensions: ['js', 'css'], 79 | 80 | // Run the build command with an extra argument to 81 | // View the bundle analyzer report after build finishes: 82 | // `npm run build --report` 83 | // Set to `true` or `false` to always turn it on or off 84 | bundleAnalyzerReport: process.env.npm_config_report 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/module-form/pages/step-form.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 95 | 103 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {Message} from 'element-ui' 3 | import store from '@/store' 4 | import {getToken} from '@/utils/auth' 5 | 6 | // create an axios instance 7 | const instance = axios.create({ 8 | baseURL: process.env.BASE_API, // api的base_url 9 | timeout: 5000 // request timeout 10 | }) 11 | 12 | // request interceptor 13 | instance.interceptors.request.use( 14 | config => { 15 | // Do something before request is sent 16 | if (store.getters.token) { 17 | config.headers['Authorization'] = `VEA-ADMIN ${getToken()}` // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改 18 | } 19 | return config 20 | }, 21 | error => { 22 | // Do something with request error 23 | console.log(error) // for debug 24 | Promise.reject(error) 25 | } 26 | ) 27 | 28 | // respone interceptor 29 | instance.interceptors.response.use( 30 | response => response, 31 | /** 32 | * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页 33 | * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中 34 | */ 35 | // const res = response.data; 36 | // if (res.code !== 20000) { 37 | // Message({ 38 | // message: res.message, 39 | // type: 'error', 40 | // duration: 5 * 1000 41 | // }); 42 | // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; 43 | // if (res.code === 50008 || res.code === 50012 || res.code === 50014) { 44 | // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { 45 | // confirmButtonText: '重新登录', 46 | // cancelButtonText: '取消', 47 | // type: 'warning' 48 | // }).then(() => { 49 | // store.dispatch('FedLogOut').then(() => { 50 | // location.reload();// 为了重新实例化vue-router对象 避免bug 51 | // }); 52 | // }) 53 | // } 54 | // return Promise.reject('error'); 55 | // } else { 56 | // return response.data; 57 | // } 58 | error => { 59 | console.log('err' + error) // for debug 60 | Message({ 61 | message: error.message, 62 | type: 'error', 63 | duration: 5 * 1000 64 | }) 65 | return Promise.reject(error) 66 | } 67 | ) 68 | 69 | export const createAPI = (url, method, data) => { 70 | let config = {} 71 | if (method === 'get') { 72 | config.params = data 73 | } else { 74 | config.data = data 75 | } 76 | return instance({ 77 | url, 78 | method, 79 | ...config 80 | }) 81 | } 82 | 83 | export const createFormAPI = (url, method, data) => { 84 | let config = {} 85 | config.data = data 86 | config.headers = { 87 | 'Cache-Control': 'no-cache', 88 | 'Content-Type': 'application/x-www-form-urlencoded' 89 | } 90 | config.responseType = 'json' 91 | config.transformRequest = [ 92 | function(data) { 93 | let ret = '' 94 | for (let it in data) { 95 | ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&' 96 | } 97 | return ret 98 | } 99 | ] 100 | return instance({ 101 | url, 102 | method, 103 | ...config 104 | }) 105 | } 106 | -------------------------------------------------------------------------------- /src/module-form/pages/activePublic/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 89 | 129 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | function pluralize(time, label) { 2 | if (time === 1) { 3 | return time + label 4 | } 5 | return time + label + 's' 6 | } 7 | 8 | export function timeAgo(time) { 9 | const between = Date.now() / 1000 - Number(time) 10 | if (between < 3600) { 11 | return pluralize(~~(between / 60), ' minute') 12 | } else if (between < 86400) { 13 | return pluralize(~~(between / 3600), ' hour') 14 | } else { 15 | return pluralize(~~(between / 86400), ' day') 16 | } 17 | } 18 | 19 | export function parseTime(time, cFormat) { 20 | if (arguments.length === 0) { 21 | return null 22 | } 23 | 24 | if ((time + '').length === 10) { 25 | time = +time * 1000 26 | } 27 | 28 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' 29 | let date 30 | if (typeof time === 'object') { 31 | date = time 32 | } else { 33 | date = new Date(parseInt(time)) 34 | } 35 | const formatObj = { 36 | y: date.getFullYear(), 37 | m: date.getMonth() + 1, 38 | d: date.getDate(), 39 | h: date.getHours(), 40 | i: date.getMinutes(), 41 | s: date.getSeconds(), 42 | a: date.getDay() 43 | } 44 | const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { 45 | let value = formatObj[key] 46 | if (key === 'a') { 47 | return ['一', '二', '三', '四', '五', '六', '日'][value - 1] 48 | } 49 | if (result.length > 0 && value < 10) { 50 | value = '0' + value 51 | } 52 | return value || 0 53 | }) 54 | return timeStr 55 | } 56 | 57 | export function formatTime(time, option) { 58 | time = +time * 1000 59 | const d = new Date(time) 60 | const now = Date.now() 61 | 62 | const diff = (now - d) / 1000 63 | 64 | if (diff < 30) { 65 | return '刚刚' 66 | } else if (diff < 3600) { 67 | // less 1 hour 68 | return Math.ceil(diff / 60) + '分钟前' 69 | } else if (diff < 3600 * 24) { 70 | return Math.ceil(diff / 3600) + '小时前' 71 | } else if (diff < 3600 * 24 * 2) { 72 | return '1天前' 73 | } 74 | if (option) { 75 | return parseTime(time, option) 76 | } else { 77 | return ( 78 | d.getMonth() + 79 | 1 + 80 | '月' + 81 | d.getDate() + 82 | '日' + 83 | d.getHours() + 84 | '时' + 85 | d.getMinutes() + 86 | '分' 87 | ) 88 | } 89 | } 90 | 91 | /* 数字 格式化 */ 92 | export function nFormatter(num, digits) { 93 | const si = [ 94 | {value: 1e18, symbol: 'E'}, 95 | {value: 1e15, symbol: 'P'}, 96 | {value: 1e12, symbol: 'T'}, 97 | {value: 1e9, symbol: 'G'}, 98 | {value: 1e6, symbol: 'M'}, 99 | {value: 1e3, symbol: 'k'} 100 | ] 101 | for (let i = 0; i < si.length; i++) { 102 | if (num >= si[i].value) { 103 | return ( 104 | (num / si[i].value + 0.1) 105 | .toFixed(digits) 106 | .replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol 107 | ) 108 | } 109 | } 110 | return num.toString() 111 | } 112 | 113 | export function html2Text(val) { 114 | const div = document.createElement('div') 115 | div.innerHTML = val 116 | return div.textContent || div.innerText 117 | } 118 | 119 | export function toThousandslsFilter(num) { 120 | return (+num || 0) 121 | .toString() 122 | .replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) 123 | } 124 | -------------------------------------------------------------------------------- /src/module-dashboard/store/user.js: -------------------------------------------------------------------------------- 1 | import { login, logout, profile } from '@/api/base/frame' 2 | import { getToken, setToken, removeToken } from '@/utils/auth' 3 | 4 | const user = { 5 | state: { 6 | user: '', 7 | status: '', 8 | code: '', 9 | token: getToken(), 10 | name: '', 11 | avatar: '', 12 | introduction: '', 13 | roles: [], 14 | setting: { 15 | articlePlatform: [] 16 | } 17 | }, 18 | 19 | mutations: { 20 | SET_CODE: (state, code) => { 21 | state.code = code 22 | }, 23 | SET_TOKEN: (state, token) => { 24 | state.token = token 25 | }, 26 | SET_INTRODUCTION: (state, introduction) => { 27 | state.introduction = introduction 28 | }, 29 | SET_SETTING: (state, setting) => { 30 | state.setting = setting 31 | }, 32 | SET_STATUS: (state, status) => { 33 | state.status = status 34 | }, 35 | SET_NAME: (state, name) => { 36 | state.name = name 37 | }, 38 | SET_AVATAR: (state, avatar) => { 39 | state.avatar = avatar || 'http://or45inefq.bkt.clouddn.com/itheima-avatar.png' 40 | }, 41 | SET_ROLES: (state, roles) => { 42 | state.roles = roles 43 | } 44 | }, 45 | 46 | actions: { 47 | // 用户名登录 48 | LoginByUsername({ commit }, userInfo) { 49 | const username = userInfo.username.trim() 50 | return new Promise((resolve, reject) => { 51 | login({ 52 | username: username, 53 | password: userInfo.password 54 | }).then(response => { 55 | const data = response.data 56 | commit('SET_TOKEN', data.token) 57 | setToken(response.data.token) 58 | resolve() 59 | }).catch(error => { 60 | reject(error) 61 | }) 62 | }) 63 | }, 64 | 65 | // 获取用户信息 66 | GetUserInfo({ commit, state }) { 67 | return new Promise((resolve, reject) => { 68 | profile().then(response => { 69 | const data = response.data 70 | commit('SET_ROLES', data.roles) 71 | commit('SET_NAME', data.name) 72 | commit('SET_AVATAR', data.avatar) 73 | commit('SET_INTRODUCTION', data.introduction) 74 | resolve(response) 75 | }).catch(error => { 76 | reject(error) 77 | }) 78 | }) 79 | }, 80 | 81 | // 第三方验证登录 82 | // LoginByThirdparty({ commit, state }, code) { 83 | // return new Promise((resolve, reject) => { 84 | // commit('SET_CODE', code) 85 | // loginByThirdparty(state.status, state.email, state.code).then(response => { 86 | // commit('SET_TOKEN', response.data.token) 87 | // setToken(response.data.token) 88 | // resolve() 89 | // }).catch(error => { 90 | // reject(error) 91 | // }) 92 | // }) 93 | // }, 94 | 95 | // 登出 96 | LogOut({ commit, state }) { 97 | return new Promise((resolve, reject) => { 98 | logout().then(() => { 99 | commit('SET_TOKEN', '') 100 | commit('SET_ROLES', []) 101 | removeToken() 102 | resolve() 103 | }).catch(error => { 104 | reject(error) 105 | }) 106 | }) 107 | }, 108 | 109 | // 前端 登出 110 | FedLogOut({ commit }) { 111 | return new Promise(resolve => { 112 | commit('SET_TOKEN', '') 113 | removeToken() 114 | resolve() 115 | }) 116 | } 117 | 118 | } 119 | } 120 | 121 | export default user 122 | -------------------------------------------------------------------------------- /src/module-dashboard/components/dashboardRaddarChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 121 | -------------------------------------------------------------------------------- /src/module-form/components/address.vue: -------------------------------------------------------------------------------- 1 | 2 | 47 | 124 | 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "itheima-admin", 3 | "version": "1.0.0", 4 | "description": "黑马Admin 管理系统基础模板", 5 | "author": "taoshiwei ", 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 test/e2e/specs", 14 | "build": "node build/build.js" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0", 18 | "echarts": "^3.8.5", 19 | "element-ui": "^2.2.2", 20 | "font-awesome": "^4.7.0", 21 | "js-cookie": "^2.2.0", 22 | "normalize.css": "^8.0.0", 23 | "nprogress": "^0.2.0", 24 | "screenfull": "^3.3.2", 25 | "sha.js": "^2.4.11", 26 | "vue": "^2.5.2", 27 | "vue-i18n": "^7.6.0", 28 | "vue-router": "^3.0.1", 29 | "vuex": "^3.0.1" 30 | }, 31 | "devDependencies": { 32 | "autoprefixer": "^7.1.2", 33 | "babel-core": "^6.22.1", 34 | "babel-eslint": "^8.2.1", 35 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 36 | "babel-jest": "^21.0.2", 37 | "babel-loader": "^7.1.1", 38 | "babel-plugin-dynamic-import-node": "^1.2.0", 39 | "babel-plugin-syntax-jsx": "^6.18.0", 40 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 41 | "babel-plugin-transform-runtime": "^6.22.0", 42 | "babel-plugin-transform-vue-jsx": "^3.5.0", 43 | "babel-preset-env": "^1.3.2", 44 | "babel-preset-stage-2": "^6.22.0", 45 | "babel-register": "^6.22.0", 46 | "chalk": "^2.0.1", 47 | "chromedriver": "^2.27.2", 48 | "copy-webpack-plugin": "^4.0.1", 49 | "cross-spawn": "^5.0.1", 50 | "css-loader": "^0.28.0", 51 | "eslint": "^4.15.0", 52 | "eslint-config-standard": "^10.2.1", 53 | "eslint-friendly-formatter": "^3.0.0", 54 | "eslint-loader": "^1.7.1", 55 | "eslint-plugin-import": "^2.7.0", 56 | "eslint-plugin-node": "^5.2.0", 57 | "eslint-plugin-promise": "^3.4.0", 58 | "eslint-plugin-standard": "^3.0.1", 59 | "eslint-plugin-vue": "^4.0.0", 60 | "extract-text-webpack-plugin": "^3.0.0", 61 | "file-loader": "^1.1.4", 62 | "font-awesome": "^4.7.0", 63 | "friendly-errors-webpack-plugin": "^1.6.1", 64 | "html-webpack-plugin": "^2.30.1", 65 | "jest": "^22.0.4", 66 | "jest-serializer-vue": "^0.3.0", 67 | "mockjs": "^1.0.1-beta3", 68 | "nightwatch": "^0.9.12", 69 | "node-notifier": "^5.1.2", 70 | "node-sass": "^4.7.2", 71 | "optimize-css-assets-webpack-plugin": "^3.2.0", 72 | "ora": "^1.2.0", 73 | "portfinder": "^1.0.13", 74 | "postcss-import": "^11.0.0", 75 | "postcss-loader": "^2.0.8", 76 | "postcss-url": "^7.2.1", 77 | "rimraf": "^2.6.0", 78 | "sass-loader": "^6.0.7", 79 | "selenium-server": "^3.0.1", 80 | "semver": "^5.3.0", 81 | "shelljs": "^0.7.6", 82 | "svg-sprite-loader": "^3.7.3", 83 | "uglifyjs-webpack-plugin": "^1.1.1", 84 | "url-loader": "^0.5.8", 85 | "vue-jest": "^1.0.2", 86 | "vue-loader": "^13.3.0", 87 | "vue-style-loader": "^3.0.1", 88 | "vue-template-compiler": "^2.5.2", 89 | "webpack": "^3.6.0", 90 | "webpack-bundle-analyzer": "^2.9.0", 91 | "webpack-dev-server": "^2.9.1", 92 | "webpack-merge": "^4.1.0" 93 | }, 94 | "engines": { 95 | "node": ">= 6.0.0", 96 | "npm": ">= 3.0.0" 97 | }, 98 | "browserslist": [ 99 | "> 1%", 100 | "last 2 versions", 101 | "not ie <= 8" 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{name}}", 3 | "version": "1.0.0", 4 | "description": "{{description}}", 5 | "author": "{{author}}", 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 test/e2e/specs", 14 | "build": "node build/build.js", 15 | "art:create": "mkdir src/module-${module} && mkdir src/module-${module}/assets && mkdir src/module-${module}/pages && mkdir src/module-${module}/components && mkdir src/module-${module}/router && mkdir src/module-${module}/store" 16 | }, 17 | "dependencies": { 18 | "axios": "^0.18.0", 19 | "echarts": "^3.8.5", 20 | "element-ui": "^2.2.2", 21 | "font-awesome": "^4.7.0", 22 | "js-cookie": "^2.2.0", 23 | "normalize.css": "^8.0.0", 24 | "nprogress": "^0.2.0", 25 | "screenfull": "^3.3.2", 26 | "sha.js": "^2.4.11", 27 | "vue": "^2.5.2", 28 | "vue-i18n": "^7.6.0", 29 | "vue-router": "^3.0.1", 30 | "vuex": "^3.0.1" 31 | }, 32 | "devDependencies": { 33 | "autoprefixer": "^7.1.2", 34 | "babel-core": "^6.22.1", 35 | "babel-eslint": "^8.2.1", 36 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 37 | "babel-jest": "^21.0.2", 38 | "babel-loader": "^7.1.1", 39 | "babel-plugin-dynamic-import-node": "^1.2.0", 40 | "babel-plugin-syntax-jsx": "^6.18.0", 41 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 42 | "babel-plugin-transform-runtime": "^6.22.0", 43 | "babel-plugin-transform-vue-jsx": "^3.5.0", 44 | "babel-preset-env": "^1.3.2", 45 | "babel-preset-stage-2": "^6.22.0", 46 | "babel-register": "^6.22.0", 47 | "chalk": "^2.0.1", 48 | "chromedriver": "^2.27.2", 49 | "copy-webpack-plugin": "^4.0.1", 50 | "cross-spawn": "^5.0.1", 51 | "css-loader": "^0.28.0", 52 | "eslint": "^4.15.0", 53 | "eslint-config-standard": "^10.2.1", 54 | "eslint-friendly-formatter": "^3.0.0", 55 | "eslint-loader": "^1.7.1", 56 | "eslint-plugin-import": "^2.7.0", 57 | "eslint-plugin-node": "^5.2.0", 58 | "eslint-plugin-promise": "^3.4.0", 59 | "eslint-plugin-standard": "^3.0.1", 60 | "eslint-plugin-vue": "^4.0.0", 61 | "extract-text-webpack-plugin": "^3.0.0", 62 | "file-loader": "^1.1.4", 63 | "friendly-errors-webpack-plugin": "^1.6.1", 64 | "html-webpack-plugin": "^2.30.1", 65 | "jest": "^22.0.4", 66 | "jest-serializer-vue": "^0.3.0", 67 | "nightwatch": "^0.9.12", 68 | "node-notifier": "^5.1.2", 69 | "node-sass": "^4.7.2", 70 | "optimize-css-assets-webpack-plugin": "^3.2.0", 71 | "ora": "^1.2.0", 72 | "portfinder": "^1.0.13", 73 | "postcss-import": "^11.0.0", 74 | "postcss-loader": "^2.0.8", 75 | "postcss-url": "^7.2.1", 76 | "rimraf": "^2.6.0", 77 | "sass-loader": "^6.0.7", 78 | "selenium-server": "^3.0.1", 79 | "semver": "^5.3.0", 80 | "shelljs": "^0.7.6", 81 | "svg-sprite-loader": "^3.7.3", 82 | "uglifyjs-webpack-plugin": "^1.1.1", 83 | "url-loader": "^0.5.8", 84 | "vue-jest": "^1.0.2", 85 | "vue-loader": "^13.3.0", 86 | "vue-style-loader": "^3.0.1", 87 | "vue-template-compiler": "^2.5.2", 88 | "webpack": "^3.6.0", 89 | "webpack-bundle-analyzer": "^2.9.0", 90 | "webpack-dev-server": "^2.9.1", 91 | "webpack-merge": "^4.1.0" 92 | }, 93 | "engines": { 94 | "node": ">= 6.0.0", 95 | "npm": ">= 3.0.0" 96 | }, 97 | "browserslist": [ 98 | "> 1%", 99 | "last 2 versions", 100 | "not ie <= 8" 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /src/components/ErrorLog/index.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 61 | 62 | 80 | -------------------------------------------------------------------------------- /src/components/TreeTable/index.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 102 | 123 | 124 | 155 | -------------------------------------------------------------------------------- /src/api/base/menus.js: -------------------------------------------------------------------------------- 1 | import {createAPI} from '@/utils/request' 2 | 3 | export const list = data => createAPI('/base/menus', 'get', data) 4 | export const add = data => createAPI('/base/menus', 'post', data) 5 | export const update = data => createAPI(`/base/menus/${data.id}`, 'put', data) 6 | export const remove = data => createAPI(`/base/menus/${data.id}`, 'delete', data) 7 | export const detail = data => createAPI(`/base/menus/${data.id}`, 'get', data) 8 | 9 | export const search = data => { 10 | return [ 11 | {value: '三全鲜食(北新泾店)', address: '长宁区新渔路144号'}, 12 | {value: 'Hot honey 首尔炸鸡(仙霞路)', address: '上海市长宁区淞虹路661号'}, 13 | { 14 | value: '新旺角茶餐厅', 15 | address: '上海市普陀区真北路988号创邑金沙谷6号楼113' 16 | }, 17 | {value: '泷千家(天山西路店)', address: '天山西路438号'}, 18 | { 19 | value: '胖仙女纸杯蛋糕(上海凌空店)', 20 | address: '上海市长宁区金钟路968号1幢18号楼一层商铺18-101' 21 | }, 22 | {value: '贡茶', address: '上海市长宁区金钟路633号'}, 23 | { 24 | value: '豪大大香鸡排超级奶爸', 25 | address: '上海市嘉定区曹安公路曹安路1685号' 26 | }, 27 | {value: '茶芝兰(奶茶,手抓饼)', address: '上海市普陀区同普路1435号'}, 28 | {value: '十二泷町', address: '上海市北翟路1444弄81号B幢-107'}, 29 | {value: '星移浓缩咖啡', address: '上海市嘉定区新郁路817号'}, 30 | {value: '阿姨奶茶/豪大大', address: '嘉定区曹安路1611号'}, 31 | {value: '新麦甜四季甜品炸鸡', address: '嘉定区曹安公路2383弄55号'}, 32 | { 33 | value: 'Monica摩托主题咖啡店', 34 | address: '嘉定区江桥镇曹安公路2409号1F,2383弄62号1F' 35 | }, 36 | { 37 | value: '浮生若茶(凌空soho店)', 38 | address: '上海长宁区金钟路968号9号楼地下一层' 39 | }, 40 | {value: 'NONO JUICE 鲜榨果汁', address: '上海市长宁区天山西路119号'}, 41 | {value: 'CoCo都可(北新泾店)', address: '上海市长宁区仙霞西路'}, 42 | { 43 | value: '快乐柠檬(神州智慧店)', 44 | address: '上海市长宁区天山西路567号1层R117号店铺' 45 | }, 46 | { 47 | value: 'Merci Paul cafe', 48 | address: '上海市普陀区光复西路丹巴路28弄6号楼819' 49 | }, 50 | { 51 | value: '猫山王(西郊百联店)', 52 | address: '上海市长宁区仙霞西路88号第一层G05-F01-1-306' 53 | }, 54 | {value: '枪会山', address: '上海市普陀区棕榈路'}, 55 | {value: '纵食', address: '元丰天山花园(东门) 双流路267号'}, 56 | {value: '钱记', address: '上海市长宁区天山西路'}, 57 | {value: '壹杯加', address: '上海市长宁区通协路'}, 58 | { 59 | value: '唦哇嘀咖', 60 | address: '上海市长宁区新泾镇金钟路999号2幢(B幢)第01层第1-02A单元' 61 | }, 62 | {value: '爱茜茜里(西郊百联)', address: '长宁区仙霞西路88号1305室'}, 63 | { 64 | value: '爱茜茜里(近铁广场)', 65 | address: '上海市普陀区真北路818号近铁城市广场北区地下二楼N-B2-O2-C商铺' 66 | }, 67 | { 68 | value: '鲜果榨汁(金沙江路和美广店)', 69 | address: '普陀区金沙江路2239号金沙和美广场B1-10-6' 70 | }, 71 | {value: '开心丽果(缤谷店)', address: '上海市长宁区威宁路天山路341号'}, 72 | {value: '超级鸡车(丰庄路店)', address: '上海市嘉定区丰庄路240号'}, 73 | {value: '妙生活果园(北新泾店)', address: '长宁区新渔路144号'}, 74 | {value: '香宜度麻辣香锅', address: '长宁区淞虹路148号'}, 75 | {value: '凡仔汉堡(老真北路店)', address: '上海市普陀区老真北路160号'}, 76 | {value: '港式小铺', address: '上海市长宁区金钟路968号15楼15-105室'}, 77 | {value: '蜀香源麻辣香锅(剑河路店)', address: '剑河路443-1'}, 78 | {value: '北京饺子馆', address: '长宁区北新泾街道天山西路490-1号'}, 79 | { 80 | value: '饭典*新简餐(凌空SOHO店)', 81 | address: '上海市长宁区金钟路968号9号楼地下一层9-83室' 82 | }, 83 | { 84 | value: '焦耳·川式快餐(金钟路店)', 85 | address: '上海市金钟路633号地下一层甲部' 86 | }, 87 | {value: '动力鸡车', address: '长宁区仙霞西路299弄3号101B'}, 88 | {value: '浏阳蒸菜', address: '天山西路430号'}, 89 | {value: '四海游龙(天山西路店)', address: '上海市长宁区天山西路'}, 90 | { 91 | value: '樱花食堂(凌空店)', 92 | address: '上海市长宁区金钟路968号15楼15-105室' 93 | }, 94 | {value: '壹分米客家传统调制米粉(天山店)', address: '天山西路428号'}, 95 | { 96 | value: '福荣祥烧腊(平溪路店)', 97 | address: '上海市长宁区协和路福泉路255弄57-73号' 98 | }, 99 | { 100 | value: '速记黄焖鸡米饭', 101 | address: '上海市长宁区北新泾街道金钟路180号1层01号摊位' 102 | }, 103 | {value: '红辣椒麻辣烫', address: '上海市长宁区天山西路492号'}, 104 | {value: '(小杨生煎)西郊百联餐厅', address: '长宁区仙霞西路88号百联2楼'}, 105 | {value: '阳阳麻辣烫', address: '天山西路389号'}, 106 | { 107 | value: '南拳妈妈龙虾盖浇饭', 108 | address: '普陀区金沙江路1699号鑫乐惠美食广场A13' 109 | } 110 | ] 111 | } 112 | -------------------------------------------------------------------------------- /src/components/Charts/keyboard.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 153 | -------------------------------------------------------------------------------- /src/module-form/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | /* 测试数据 */ 7 | const date = 'Mon Oct 17 2016 00:00:00 GMT+0800 (中国标准时间)' 8 | const ruleForm = { 9 | name: '活动名称', 10 | fenLeis: [{ 11 | name: '测试1' 12 | }, 13 | { 14 | name: '测试2' 15 | }, 16 | { 17 | name: '测试3' 18 | } 19 | ], 20 | fenLei: '未发布', 21 | tags: [{ 22 | name: '标签1' 23 | }], 24 | activeStartTimeDate: date, 25 | activeStartTimeTime: '00:45', 26 | activeEndTimeDate: date, 27 | activeEndTimeTime: '00:45', 28 | signStartTimeDate: date, 29 | signStartTimeTime: '00:45', 30 | signEndTimeDate: date, 31 | signEndTimeTime: '00:45', 32 | activePerson: '', 33 | activePersonNum: '', 34 | activeDescribe: '', 35 | UseMsgShow: '', 36 | evaluate: '', 37 | adTitle: '', 38 | adContent: '', 39 | adLink: '', 40 | province: '北京市', 41 | city: '北京市', 42 | detail: '海淀区' 43 | } 44 | 45 | const signForm = { 46 | signUpLimit: '无限制', 47 | numLimit: '无限制', 48 | numLimitDetail: '', 49 | cost: '免费', 50 | costDetail: '', 51 | cancel: '不允许', 52 | audit: '不需要', 53 | needName: true, 54 | needTel: true, 55 | sign: '必须报名', 56 | signType: '签到二维码', 57 | secretCode: '', 58 | signFormList: [] 59 | } 60 | 61 | const shareForm = { 62 | title: '分享Allen的祖传链接', 63 | describe: '这个链接很吊!' 64 | } 65 | 66 | const selfForm = { 67 | signUpSuccess: true, // 报名成功 68 | signUpSuccessText: '您已成功报名***(默认活动标题名)活动', 69 | auditSuccess: true, 70 | auditSuccessText: '您报名的***(默认活动标题名)活动,已审核通过', 71 | auditFailed: true, 72 | auditFailedText: '您报名的***(默认活动标题名)活动,审核不通过', 73 | signInSuccess: true, 74 | signInSuccessText: '***(默认用户名)您好!恭喜您签到成功!', 75 | signInFailed: true, 76 | signInFailedText: '抱歉,签到失败', 77 | signInDouble: true, 78 | signInDoubleText: '请勿重复签到!', 79 | remind: '不提醒', 80 | remindTime: '', 81 | remindText: '', 82 | useScore: '不使用', 83 | useScoreNum: '', 84 | signUpScore: '无积分', 85 | signUpScoreNum: '', 86 | shareScore: '无积分', 87 | shareScoreNum: '', 88 | shareReadScore: '无积分', 89 | shareReadScoreNum: '', 90 | shareReadScoreNumMax: '', 91 | shareSignUp: '无积分', 92 | shareSignUpNum: '', 93 | shareSignUpNumMax: '', 94 | afterShare: '', 95 | afterShareLink: '', 96 | afterSingUp: '', 97 | afterSingUpLink: '', 98 | shareImg: '', 99 | shareImgUrl: '' 100 | } 101 | const data = [{ 102 | id: '1111', 103 | name: 'Allen', 104 | type: '测试活动', 105 | status: '未开始', 106 | readNum: 200, 107 | signUpNum: 100, 108 | auditNum: 5, 109 | activeMessage: {} 110 | }, 111 | { 112 | id: '2222', 113 | name: '王小虎', 114 | type: '测试活动', 115 | status: '已结束', 116 | readNum: 200, 117 | signUpNum: 100, 118 | auditNum: 8, 119 | activeMessage: {} 120 | } 121 | ] 122 | /* 活动管理测试数据 */ 123 | /* 124 | * ruleForm 1、活动信息的表单 125 | * signFrom 2、报名的表单 126 | * shareFrom 3、报名的表单 127 | * selfFrom 4、个性设置的表单 128 | * activeList 活动列表 129 | * */ 130 | const state = { 131 | ruleForm: ruleForm, 132 | signForm: {}, 133 | shareForm: {}, 134 | selfForm: {}, 135 | activeList: data 136 | } 137 | 138 | /* 从本地存储读取数据 */ 139 | // for (var item in state) { 140 | // localStorage.getItem(item) 141 | // ? state[item] = JSON.parse(localStorage.getItem(item)) : false 142 | // } 143 | 144 | const mutations = { 145 | setRuleForm(state, payload) { 146 | Object.assign(state.ruleForm, payload) 147 | localStorage.setItem('ruleForm', JSON.stringify(payload)) 148 | }, 149 | setSignForm(state, payload) { 150 | Object.assign(state.signForm, payload) 151 | localStorage.setItem('signForm', JSON.stringify(payload)) 152 | 153 | }, 154 | setShareForm(state, payload) { 155 | Object.assign(state.shareForm, payload) 156 | localStorage.setItem('shareForm', JSON.stringify(payload)) 157 | }, 158 | setSelfForm(state, payload) { 159 | Object.assign(state.selfForm, payload) 160 | localStorage.setItem('selfForm', JSON.stringify(payload)) 161 | } 162 | } 163 | 164 | export default new Vuex.Store({ 165 | state, 166 | mutations 167 | }) 168 | -------------------------------------------------------------------------------- /src/module-dashboard/components/dashboardLineChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 154 | -------------------------------------------------------------------------------- /src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | // 主体区域 3 | .main-container { 4 | min-height: 100%; 5 | transition: margin-left 0.28s; 6 | margin-left: 180px; 7 | } // 侧边栏 8 | .sidebar-container { 9 | -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); 10 | box-shadow: 2px 0 6px rgba(0,21,41,.35); 11 | transition: width 0.28s; 12 | background-color: #001529; 13 | width: 180px!important; 14 | height: 100%; 15 | position: fixed; 16 | top: 0; 17 | bottom: 0; 18 | left: 0; 19 | z-index: 1001; 20 | a { 21 | display: inline-block; 22 | width: 100%; 23 | } 24 | .svg-icon { 25 | margin-right: 16px; 26 | } 27 | .el-menu { 28 | border: none; 29 | width: 100%; 30 | } 31 | } 32 | .hideSidebar { 33 | .sidebar-container,.sidebar-container .el-menu { 34 | width: 36px!important; 35 | overflow: inherit; 36 | } 37 | .main-container { 38 | margin-left: 36px; 39 | } 40 | } 41 | .hideSidebar { 42 | .submenu-title-noDropdown { 43 | padding-left: 10px!important; 44 | position: relative; 45 | span { 46 | height: 0; 47 | width: 0; 48 | overflow: hidden; 49 | visibility: hidden; 50 | transition: opacity .3s cubic-bezier(.55, 0, .1, 1); 51 | opacity: 0; 52 | display: inline-block; 53 | } 54 | .svg-icon { 55 | margin-left: -10px; 56 | } 57 | &:hover { 58 | span { 59 | display: block; 60 | border-radius: 3px; 61 | z-index: 1002; 62 | width: 140px; 63 | height: 56px; 64 | visibility: visible; 65 | position: absolute; 66 | right: -145px; 67 | text-align: left; 68 | text-indent: 20px; 69 | top: 0px; 70 | background-color: $subMenuBg!important; 71 | opacity: 1; 72 | } 73 | } 74 | } 75 | .el-menu-item { 76 | padding-left: 10px!important; 77 | &>.el-tooltip { 78 | padding-left: 10px!important; 79 | } 80 | } 81 | .el-submenu { 82 | &>.el-submenu__title { 83 | padding-left: 10px!important; 84 | &>span { 85 | display: none; 86 | } 87 | .el-submenu__icon-arrow { 88 | display: none; 89 | } 90 | } 91 | .nest-menu { 92 | .el-submenu__icon-arrow { 93 | display: block!important; 94 | } 95 | span { 96 | display: inline-block!important; 97 | } 98 | } 99 | } 100 | } 101 | .nest-menu .el-submenu>.el-submenu__title, 102 | .el-submenu .el-menu-item { 103 | min-width: 180px!important; 104 | background-color: $subMenuBg!important; 105 | &:hover { 106 | background-color: $menuHover!important; 107 | span { 108 | color: #fff; 109 | } 110 | } 111 | } 112 | .el-menu--collapse .el-menu .el-submenu{ 113 | min-width: 180px!important; 114 | } 115 | // logo标志 116 | .sidebar-logo ,.sidebar-logo-mini { 117 | font-size: 20px; 118 | text-align: center; 119 | line-height: 56px; 120 | margin: 0px; 121 | padding: 0px; 122 | background-color: #002140; 123 | color: #fff; 124 | font-weight: 600; 125 | font-family: "Myriad Pro","Helvetica Neue",Arial,Helvetica,sans-serif; 126 | img { 127 | width: 160px; 128 | padding: 0px; 129 | margin: 5px; 130 | } 131 | } 132 | .sidebar-logo { 133 | width:180px; 134 | height:56px; 135 | } 136 | .sidebar-logo-mini { 137 | display: none; 138 | width:36px; 139 | height:56px; 140 | img { 141 | width: 32px; 142 | padding: 0px; 143 | margin: 10px 0px; 144 | } 145 | } 146 | .hideSidebar { 147 | .sidebar-logo { 148 | display: none; 149 | } 150 | .sidebar-logo-mini { 151 | display: block; 152 | } 153 | } 154 | .el-menu-item.is-active { 155 | background-color: #1890ff!important; 156 | &:hover { 157 | background-color: #1890ff!important; 158 | } 159 | } 160 | .el-submenu.is-active .el-submenu__title { 161 | color: #fff!important; 162 | .el-submenu__icon-arrow { 163 | color: #fff!important; 164 | } 165 | } 166 | .el-submenu__icon-arrow { 167 | font-weight: 600; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/module-manage/components/user-add.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 114 | 123 | --------------------------------------------------------------------------------