├── nav-app ├── .browserslistrc ├── postcss.config.js ├── babel.config.js ├── src │ ├── assets │ │ └── logo.png │ ├── store │ │ └── index.js │ ├── router │ │ └── index.js │ ├── main.js │ └── components │ │ └── Hamburger │ │ └── index.vue ├── .gitignore ├── README.md ├── public │ └── index.html ├── package.json └── vue.config.js ├── sub-app1 ├── .browserslistrc ├── postcss.config.js ├── babel.config.js ├── src │ ├── assets │ │ └── logo.png │ ├── views │ │ ├── About.vue │ │ └── Home.vue │ ├── store │ │ └── index.js │ ├── components │ │ └── HelloWorld.vue │ ├── router │ │ └── index.js │ ├── App.vue │ └── main.js ├── .gitignore ├── README.md ├── public │ └── index.html ├── package.json └── vue.config.js ├── sub-app2 ├── .browserslistrc ├── postcss.config.js ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ └── logo.png │ ├── views │ │ ├── About.vue │ │ └── Home.vue │ ├── store │ │ └── index.js │ ├── components │ │ └── HelloWorld.vue │ ├── router │ │ └── index.js │ ├── App.vue │ └── main.js ├── .gitignore ├── README.md ├── package.json └── vue.config.js ├── sidemenu-app ├── .browserslistrc ├── postcss.config.js ├── src │ ├── assets │ │ └── logo.png │ ├── utils │ │ ├── validate.js │ │ └── FixiOSBug.js │ ├── icons │ │ ├── svg │ │ │ ├── chart.svg │ │ │ ├── size.svg │ │ │ ├── link.svg │ │ │ ├── guide.svg │ │ │ ├── component.svg │ │ │ ├── money.svg │ │ │ ├── email.svg │ │ │ ├── drag.svg │ │ │ ├── documentation.svg │ │ │ ├── fullscreen.svg │ │ │ ├── user.svg │ │ │ ├── lock.svg │ │ │ ├── excel.svg │ │ │ ├── example.svg │ │ │ ├── star.svg │ │ │ ├── table.svg │ │ │ ├── search.svg │ │ │ ├── password.svg │ │ │ ├── education.svg │ │ │ ├── tab.svg │ │ │ ├── message.svg │ │ │ ├── theme.svg │ │ │ ├── peoples.svg │ │ │ ├── edit.svg │ │ │ ├── nested.svg │ │ │ ├── tree-table.svg │ │ │ ├── eye.svg │ │ │ ├── clipboard.svg │ │ │ ├── list.svg │ │ │ ├── icon.svg │ │ │ ├── international.svg │ │ │ ├── wechat.svg │ │ │ ├── skill.svg │ │ │ ├── people.svg │ │ │ ├── language.svg │ │ │ ├── eye-open.svg │ │ │ ├── 404.svg │ │ │ ├── zip.svg │ │ │ ├── bug.svg │ │ │ ├── pdf.svg │ │ │ ├── exit-fullscreen.svg │ │ │ └── tree.svg │ │ ├── index.js │ │ └── svgo.yml │ ├── router │ │ └── index.js │ ├── store │ │ └── index.js │ ├── styles │ │ └── variables.scss │ ├── components │ │ ├── Item.vue │ │ ├── Link.vue │ │ └── SvgIcon │ │ │ └── index.vue │ ├── main.js │ └── App.vue ├── babel.config.js ├── .gitignore ├── README.md ├── public │ └── index.html ├── package.json └── vue.config.js ├── main-app ├── .eslintignore ├── babel.config.js ├── tests │ └── unit │ │ ├── .eslintrc.js │ │ ├── components │ │ ├── SvgIcon.spec.js │ │ └── Hamburger.spec.js │ │ └── utils │ │ ├── parseTime.spec.js │ │ ├── formatTime.spec.js │ │ └── validate.spec.js ├── public │ ├── favicon.ico │ └── index.html ├── .npmrc ├── .travis.yml ├── .env.production ├── src │ ├── assets │ │ ├── 401_images │ │ │ └── 401.gif │ │ ├── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ │ └── custom-theme │ │ │ └── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ ├── layout │ │ ├── components │ │ │ ├── index.js │ │ │ └── AppMain.vue │ │ └── mixin │ │ │ └── ResizeHandler.js │ ├── api │ │ ├── qiniu.js │ │ ├── remote-search.js │ │ ├── user.js │ │ ├── role.js │ │ └── article.js │ ├── icons │ │ ├── svg │ │ │ ├── chart.svg │ │ │ ├── size.svg │ │ │ ├── link.svg │ │ │ ├── guide.svg │ │ │ ├── component.svg │ │ │ ├── money.svg │ │ │ ├── email.svg │ │ │ ├── drag.svg │ │ │ ├── documentation.svg │ │ │ ├── fullscreen.svg │ │ │ ├── user.svg │ │ │ ├── lock.svg │ │ │ ├── excel.svg │ │ │ ├── example.svg │ │ │ ├── star.svg │ │ │ ├── search.svg │ │ │ ├── table.svg │ │ │ ├── password.svg │ │ │ ├── education.svg │ │ │ ├── tab.svg │ │ │ ├── message.svg │ │ │ ├── theme.svg │ │ │ ├── peoples.svg │ │ │ ├── edit.svg │ │ │ ├── nested.svg │ │ │ ├── tree-table.svg │ │ │ ├── eye.svg │ │ │ ├── clipboard.svg │ │ │ ├── list.svg │ │ │ ├── icon.svg │ │ │ ├── international.svg │ │ │ ├── wechat.svg │ │ │ ├── skill.svg │ │ │ ├── people.svg │ │ │ ├── language.svg │ │ │ ├── eye-open.svg │ │ │ ├── 404.svg │ │ │ ├── zip.svg │ │ │ ├── bug.svg │ │ │ ├── pdf.svg │ │ │ ├── exit-fullscreen.svg │ │ │ └── tree.svg │ │ ├── index.js │ │ └── svgo.yml │ ├── components │ │ ├── ImageCropper │ │ │ └── utils │ │ │ │ ├── mimes.js │ │ │ │ ├── data2blob.js │ │ │ │ └── effectRipple.js │ │ ├── Tinymce │ │ │ ├── toolbar.js │ │ │ ├── plugins.js │ │ │ └── dynamicLoadScript.js │ │ ├── MarkdownEditor │ │ │ └── default-options.js │ │ ├── Charts │ │ │ └── mixins │ │ │ │ └── resize.js │ │ ├── Screenfull │ │ │ └── index.vue │ │ ├── Hamburger │ │ │ └── index.vue │ │ ├── SvgIcon │ │ │ └── index.vue │ │ ├── SizeSelect │ │ │ └── index.vue │ │ ├── DragSelect │ │ │ └── index.vue │ │ └── GithubCorner │ │ │ └── index.vue │ ├── App.vue │ ├── utils │ │ ├── get-page-title.js │ │ ├── auth.js │ │ ├── permission.js │ │ ├── clipboard.js │ │ ├── error-log.js │ │ ├── open-window.js │ │ ├── request.js │ │ └── scroll-to.js │ ├── directive │ │ ├── waves │ │ │ ├── index.js │ │ │ └── waves.css │ │ ├── el-drag-dialog │ │ │ └── index.js │ │ ├── clipboard │ │ │ ├── index.js │ │ │ └── clipboard.js │ │ ├── permission │ │ │ ├── index.js │ │ │ └── permission.js │ │ └── el-table │ │ │ ├── index.js │ │ │ └── adaptive.js │ ├── views │ │ ├── redirect │ │ │ └── index.vue │ │ ├── login │ │ │ └── auth-redirect.vue │ │ ├── dashboard │ │ │ ├── index.vue │ │ │ ├── admin │ │ │ │ ├── components │ │ │ │ │ ├── TransactionTable.vue │ │ │ │ │ ├── mixins │ │ │ │ │ │ └── resize.js │ │ │ │ │ └── TodoList │ │ │ │ │ │ └── Todo.vue │ │ │ │ └── index.vue │ │ │ └── editor │ │ │ │ └── index.vue │ │ └── profile │ │ │ ├── components │ │ │ ├── Account.vue │ │ │ └── Timeline.vue │ │ │ └── index.vue │ ├── store │ │ ├── modules │ │ │ ├── errorLog.js │ │ │ ├── workSubApp.js │ │ │ ├── navbarSubapp.js │ │ │ ├── sidemenuSubapp.js │ │ │ ├── settings.js │ │ │ ├── app.js │ │ │ └── permission.js │ │ ├── index.js │ │ └── getters.js │ ├── router │ │ └── modules │ │ │ └── subapp.js │ ├── main.js │ ├── styles │ │ ├── variables.scss │ │ ├── element-variables.scss │ │ ├── transition.scss │ │ ├── element-ui.scss │ │ ├── mixin.scss │ │ └── btn.scss │ ├── settings.js │ ├── permission.js │ └── filters │ │ └── index.js ├── .env.staging ├── .env.development ├── plop-templates │ ├── utils.js │ ├── view │ │ ├── index.hbs │ │ └── prompt.js │ └── component │ │ ├── index.hbs │ │ └── prompt.js ├── plopfile.js ├── .editorconfig ├── postcss.config.js ├── jest.config.js ├── build │ └── index.js └── mock │ ├── remote-search.js │ └── user.js └── .gitignore /nav-app/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /sub-app1/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /sub-app2/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /sidemenu-app/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /main-app/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /main-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /main-app/tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /nav-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sub-app1/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sub-app2/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sidemenu-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /main-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/public/favicon.ico -------------------------------------------------------------------------------- /nav-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /nav-app/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/nav-app/src/assets/logo.png -------------------------------------------------------------------------------- /sub-app1/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /sub-app2/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /sub-app2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/sub-app2/public/favicon.ico -------------------------------------------------------------------------------- /main-app/.npmrc: -------------------------------------------------------------------------------- 1 | sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ 2 | registry=https://registry.npm.taobao.org 3 | -------------------------------------------------------------------------------- /main-app/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /sub-app1/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/sub-app1/src/assets/logo.png -------------------------------------------------------------------------------- /sub-app2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/sub-app2/src/assets/logo.png -------------------------------------------------------------------------------- /main-app/.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/prod-api' 6 | 7 | -------------------------------------------------------------------------------- /sidemenu-app/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/sidemenu-app/src/assets/logo.png -------------------------------------------------------------------------------- /main-app/src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /main-app/src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/src/assets/404_images/404.png -------------------------------------------------------------------------------- /sub-app1/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /sub-app2/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /main-app/.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /main-app/src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /main-app/src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /main-app/src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiSong/vue-element-qiankun/HEAD/main-app/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /sidemenu-app/src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} path 3 | * @returns {Boolean} 4 | */ 5 | export function isExternal(path) { 6 | return /^(https?:|mailto:|tel:)/.test(path); 7 | } 8 | -------------------------------------------------------------------------------- /main-app/src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as AppMain } from './AppMain' 2 | export { default as Settings } from './Settings' 3 | export { default as TagsView } from './TagsView/index.vue' 4 | -------------------------------------------------------------------------------- /main-app/src/api/qiniu.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getToken() { 4 | return request({ 5 | url: '/qiniu/upload/token', // 假地址 自行替换 6 | method: 'get' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/components/ImageCropper/utils/mimes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | } 8 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/dev-api' 6 | 7 | SUB_APP1 = 'app1' 8 | SUB_APP2 = 'app2' 9 | 10 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 11 | -------------------------------------------------------------------------------- /main-app/plop-templates/utils.js: -------------------------------------------------------------------------------- 1 | exports.notEmpty = name => { 2 | return v => { 3 | if (!v || v.trim === '') { 4 | return `${name} is required` 5 | } else { 6 | return true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /nav-app/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | }, 9 | mutations: { 10 | }, 11 | actions: { 12 | }, 13 | modules: { 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /sub-app1/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | }, 9 | mutations: { 10 | }, 11 | actions: { 12 | }, 13 | modules: { 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /sub-app2/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | }, 9 | mutations: { 10 | }, 11 | actions: { 12 | }, 13 | modules: { 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /nav-app/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | Vue.use(VueRouter); 4 | 5 | const routes = []; 6 | 7 | const router = new VueRouter({ 8 | mode: "history", 9 | routes 10 | }); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /sidemenu-app/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | Vue.use(VueRouter); 4 | 5 | const routes = []; 6 | 7 | const router = new VueRouter({ 8 | mode: "history", 9 | routes 10 | }); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /sidemenu-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | plugins: [ 4 | [ 5 | "component", 6 | { 7 | libraryName: "element-ui", 8 | styleLibraryName: "theme-chalk" 9 | } 10 | ] 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /main-app/plopfile.js: -------------------------------------------------------------------------------- 1 | const viewGenerator = require('./plop-templates/view/prompt') 2 | const componentGenerator = require('./plop-templates/component/prompt') 3 | 4 | module.exports = function(plop) { 5 | plop.setGenerator('view', viewGenerator) 6 | plop.setGenerator('component', componentGenerator) 7 | } 8 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Vue Element Admin' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/directive/waves/index.js: -------------------------------------------------------------------------------- 1 | import waves from './waves' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('waves', waves) 5 | } 6 | 7 | if (window.Vue) { 8 | window.waves = waves 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | waves.install = install 13 | export default waves 14 | -------------------------------------------------------------------------------- /main-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /nav-app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /sidemenu-app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /sub-app1/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /sub-app2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /main-app/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/directive/el-drag-dialog/index.js: -------------------------------------------------------------------------------- 1 | import drag from './drag' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-drag-dialog', drag) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-drag-dialog'] = drag 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | drag.install = install 13 | export default drag 14 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/directive/clipboard/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('Clipboard', Clipboard) 5 | } 6 | 7 | if (window.Vue) { 8 | window.clipboard = Clipboard 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | Clipboard.install = install 13 | export default Clipboard 14 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /sidemenu-app/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | Vue.use(Vuex); 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | settings: { 9 | sidebarLogo: true 10 | }, 11 | app: { 12 | device: "" 13 | } 14 | }, 15 | mutations: {}, 16 | actions: {}, 17 | modules: {} 18 | }); 19 | -------------------------------------------------------------------------------- /main-app/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Admin-Token' 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 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | // 'postcss-selector-namespace': { 5 | // namespace(css) { 6 | // // element-ui的样式不需要添加命名空间 7 | // if (css.includes('element-variables.scss')) return '' 8 | // return '.fa-spa' // 返回要添加的类名 9 | // } 10 | // } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /main-app/src/directive/permission/index.js: -------------------------------------------------------------------------------- 1 | import permission from './permission' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('permission', permission) 5 | } 6 | 7 | if (window.Vue) { 8 | window['permission'] = permission 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | permission.install = install 13 | export default permission 14 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /sub-app2/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /main-app/src/directive/el-table/index.js: -------------------------------------------------------------------------------- 1 | import adaptive from './adaptive' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-height-adaptive-table', adaptive) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-height-adaptive-table'] = adaptive 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | adaptive.install = install 13 | export default adaptive 14 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /sub-app1/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /main-app/src/api/remote-search.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function searchUser(name) { 4 | return request({ 5 | url: '/search/user', 6 | method: 'get', 7 | params: { name } 8 | }) 9 | } 10 | 11 | export function transactionList(query) { 12 | return request({ 13 | url: '/transaction/list', 14 | method: 'get', 15 | params: query 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /main-app/src/views/login/auth-redirect.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/plop-templates/view/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/plop-templates/component/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nav-app/README.md: -------------------------------------------------------------------------------- 1 | # sub-app1 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /sub-app1/README.md: -------------------------------------------------------------------------------- 1 | # sub-app1 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /sub-app2/README.md: -------------------------------------------------------------------------------- 1 | # sub-app2 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /main-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= webpackConfig.name %> 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/README.md: -------------------------------------------------------------------------------- 1 | # sub-app1 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sub-app1/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /main-app/src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function login(data) { 4 | return request({ 5 | url: '/user/login', 6 | method: 'get', 7 | params: data 8 | }) 9 | } 10 | 11 | export function getInfo(token) { 12 | return request({ 13 | url: '/user/info', 14 | method: 'get', 15 | params: { token } 16 | }) 17 | } 18 | 19 | export function logout() { 20 | console.log(123) 21 | return request({ 22 | url: '/user/logout', 23 | method: 'get' 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /main-app/src/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /sidemenu-app/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // sidebar 2 | $menuText: #bfcbd9; 3 | $menuActiveText: #409eff; 4 | $subMenuActiveText: #f4f4f5; 5 | $menuBg: #304156; 6 | $menuHover: #263445; 7 | $subMenuBg: #1f2d3d; 8 | $subMenuHover: #001528; 9 | $sideBarWidth: 210px; 10 | 11 | :export { 12 | menuText: $menuText; 13 | menuActiveText: $menuActiveText; 14 | subMenuActiveText: $subMenuActiveText; 15 | menuBg: $menuBg; 16 | menuHover: $menuHover; 17 | subMenuBg: $subMenuBg; 18 | subMenuHover: $subMenuHover; 19 | sideBarWidth: $sideBarWidth; 20 | } 21 | -------------------------------------------------------------------------------- /main-app/src/store/modules/errorLog.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | logs: [] 3 | } 4 | 5 | const mutations = { 6 | ADD_ERROR_LOG: (state, log) => { 7 | state.logs.push(log) 8 | }, 9 | CLEAR_ERROR_LOG: (state) => { 10 | state.logs.splice(0) 11 | } 12 | } 13 | 14 | const actions = { 15 | addErrorLog({ commit }, log) { 16 | commit('ADD_ERROR_LOG', log) 17 | }, 18 | clearErrorLog({ commit }) { 19 | commit('CLEAR_ERROR_LOG') 20 | } 21 | } 22 | 23 | export default { 24 | namespaced: true, 25 | state, 26 | mutations, 27 | actions 28 | } 29 | -------------------------------------------------------------------------------- /sub-app2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-app2", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "vue-cli-service serve", 6 | "build": "vue-cli-service build" 7 | }, 8 | "dependencies": { 9 | "core-js": "^3.3.2", 10 | "vue": "^2.6.10", 11 | "vue-router": "^3.1.3", 12 | "vuex": "^3.0.1" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "^4.0.0", 16 | "@vue/cli-service": "^4.0.0", 17 | "node-sass": "^4.12.0", 18 | "sass-loader": "^8.0.0", 19 | "vue-template-compiler": "^2.6.10" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sidemenu-app 7 | 8 | 9 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /main-app/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | 5 | Vue.use(Vuex) 6 | 7 | const modulesFiles = require.context('./modules', true, /\.js$/) 8 | 9 | const modules = modulesFiles.keys().reduce((modules, modulePath) => { 10 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') 11 | const value = modulesFiles(modulePath) 12 | modules[moduleName] = value.default 13 | return modules 14 | }, {}) 15 | 16 | const store = new Vuex.Store({ 17 | modules, 18 | getters 19 | }) 20 | 21 | export default store 22 | -------------------------------------------------------------------------------- /main-app/src/store/modules/workSubApp.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | appContent: '', 3 | loading: false 4 | } 5 | 6 | const mutations = { 7 | SET_APP_CONTENT: (state, { appContent, loading }) => { 8 | state.appContent = appContent 9 | state.loading = loading 10 | }, 11 | SET_APP_LOADING: (state, status) => { 12 | state.loading = status 13 | } 14 | } 15 | 16 | const actions = { 17 | setAppContent({ commit }, payload) { 18 | commit('SET_APP_CONTENT', payload) 19 | } 20 | } 21 | 22 | export default { 23 | namespaced: true, 24 | state, 25 | mutations, 26 | actions 27 | } 28 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /sidemenu-app/src/components/Item.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | size: state => state.app.size, 4 | device: state => state.app.device, 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 | roles: state => state.user.roles, 12 | permission_routes: state => state.permission.routes, 13 | errorLogs: state => state.errorLog.logs 14 | } 15 | export default getters 16 | -------------------------------------------------------------------------------- /nav-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | nav-app 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sub-app1/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | sub-app1 8 | 9 | 10 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sub-app1/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import Home from "../views/Home.vue"; 4 | 5 | Vue.use(VueRouter); 6 | 7 | // const baseUrl = "/subapp/app1"; 8 | const baseUrl = "/"; 9 | 10 | const routes = [ 11 | { 12 | path: baseUrl, 13 | name: "home", 14 | component: Home 15 | }, 16 | { 17 | path: "/about", 18 | name: "about", 19 | component: () => 20 | import(/* webpackChunkName: "about" */ "../views/About.vue") 21 | } 22 | ]; 23 | 24 | const router = new VueRouter({ 25 | mode: "hash", 26 | routes 27 | }); 28 | 29 | export default router; 30 | -------------------------------------------------------------------------------- /main-app/src/components/ImageCropper/utils/data2blob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * database64文件格式转换为2进制 3 | * 4 | * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 5 | * @param {[String]} mime [description] 6 | * @return {[blob]} [description] 7 | */ 8 | export default function(data, mime) { 9 | data = data.split(',')[1] 10 | data = window.atob(data) 11 | var ia = new Uint8Array(data.length) 12 | for (var i = 0; i < data.length; i++) { 13 | ia[i] = data.charCodeAt(i) 14 | } 15 | // canvas.toDataURL 返回的默认格式就是 image/png 16 | return new Blob([ia], { 17 | type: mime 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/router/modules/subapp.js: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules **/ 2 | 3 | import Layout from '@/layout' 4 | 5 | const subRouter = { 6 | path: '/subapp', 7 | component: Layout, 8 | redirect: '/subapp/app1', 9 | name: 'sub', 10 | meta: { 11 | title: 'sub-app', 12 | icon: 'table' 13 | }, 14 | children: [ 15 | { 16 | path: 'app1', 17 | name: 'sub-app1', 18 | meta: { title: 'sub-app1' } 19 | }, 20 | { 21 | path: 'app2', 22 | name: 'sub-app2', 23 | meta: { title: 'sub-app2' } 24 | } 25 | ] 26 | } 27 | export default subRouter 28 | -------------------------------------------------------------------------------- /sub-app2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | sub-app2 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nav-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-app1", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "vue-cli-service serve", 6 | "build": "vue-cli-service build", 7 | "build:stage": "vue-cli-service build --mode staging" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.3.2", 11 | "element-ui": "^2.12.0", 12 | "vue": "^2.6.10", 13 | "vue-router": "^3.1.3", 14 | "vuex": "^3.0.1" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "^4.0.0", 18 | "@vue/cli-service": "^4.0.0", 19 | "node-sass": "^4.12.0", 20 | "sass-loader": "^8.0.0", 21 | "vue-template-compiler": "^2.6.10" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sub-app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-app1", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "vue-cli-service serve", 6 | "build": "vue-cli-service build", 7 | "build:stage": "vue-cli-service build --mode staging" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.3.2", 11 | "element-ui": "^2.12.0", 12 | "vue": "^2.6.10", 13 | "vue-router": "^3.1.3", 14 | "vuex": "^3.0.1" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "^4.0.0", 18 | "@vue/cli-service": "^4.0.0", 19 | "node-sass": "^4.12.0", 20 | "sass-loader": "^8.0.0", 21 | "vue-template-compiler": "^2.6.10" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sub-app2/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 17 | 38 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/directive/permission/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | export default { 4 | inserted(el, binding, vnode) { 5 | const { value } = binding 6 | const roles = store.getters && store.getters.roles 7 | 8 | if (value && value instanceof Array && value.length > 0) { 9 | const permissionRoles = value 10 | 11 | const hasPermission = roles.some(role => { 12 | return permissionRoles.includes(role) 13 | }) 14 | 15 | if (!hasPermission) { 16 | el.parentNode && el.parentNode.removeChild(el) 17 | } 18 | } else { 19 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/utils/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug 9 | this.fixBugIniOS() 10 | }, 11 | methods: { 12 | fixBugIniOS() { 13 | const $subMenu = this.$refs.subMenu 14 | if ($subMenu) { 15 | const handleMouseleave = $subMenu.handleMouseleave 16 | $subMenu.handleMouseleave = (e) => { 17 | if (this.device === 'mobile') { 18 | return 19 | } 20 | handleMouseleave(e) 21 | } 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sidemenu-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-app1", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "vue-cli-service serve", 6 | "build": "vue-cli-service build", 7 | "build:stage": "vue-cli-service build --mode staging" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.3.2", 11 | "element-ui": "^2.12.0", 12 | "vue": "^2.6.10", 13 | "vue-router": "^3.1.3", 14 | "vuex": "^3.0.1" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "^4.0.0", 18 | "@vue/cli-service": "^4.0.0", 19 | "babel-plugin-component": "^1.1.1", 20 | "node-sass": "^4.12.0", 21 | "sass-loader": "^8.0.0", 22 | "vue-template-compiler": "^2.6.10" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /main-app/src/utils/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | /** 4 | * @param {Array} value 5 | * @returns {Boolean} 6 | * @example see @/views/permission/directive.vue 7 | */ 8 | export default function checkPermission(value) { 9 | if (value && value instanceof Array && value.length > 0) { 10 | const roles = store.getters && store.getters.roles 11 | const permissionRoles = value 12 | 13 | const hasPermission = roles.some(role => { 14 | return permissionRoles.includes(role) 15 | }) 16 | 17 | if (!hasPermission) { 18 | return false 19 | } 20 | return true 21 | } else { 22 | console.error(`need roles! Like v-permission="['admin','editor']"`) 23 | return false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /main-app/src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/api/role.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getRoutes() { 4 | return request({ 5 | url: '/routes', 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export function getRoles() { 11 | return request({ 12 | url: '/roles', 13 | method: 'get' 14 | }) 15 | } 16 | 17 | export function addRole(data) { 18 | return request({ 19 | url: '/role', 20 | method: 'post', 21 | data 22 | }) 23 | } 24 | 25 | export function updateRole(id, data) { 26 | return request({ 27 | url: `/role/${id}`, 28 | method: 'put', 29 | data 30 | }) 31 | } 32 | 33 | export function deleteRole(id) { 34 | return request({ 35 | url: `/role/${id}`, 36 | method: 'delete' 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /main-app/src/components/MarkdownEditor/default-options.js: -------------------------------------------------------------------------------- 1 | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor 2 | export default { 3 | minHeight: '200px', 4 | previewStyle: 'vertical', 5 | useCommandShortcut: true, 6 | useDefaultHTMLSanitizer: true, 7 | usageStatistics: false, 8 | hideModeSwitch: false, 9 | toolbarItems: [ 10 | 'heading', 11 | 'bold', 12 | 'italic', 13 | 'strike', 14 | 'divider', 15 | 'hr', 16 | 'quote', 17 | 'divider', 18 | 'ul', 19 | 'ol', 20 | 'task', 21 | 'indent', 22 | 'outdent', 23 | 'divider', 24 | 'table', 25 | 'image', 26 | 'link', 27 | 'divider', 28 | 'code', 29 | 'codeblock' 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/tests/unit/components/SvgIcon.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import SvgIcon from '@/components/SvgIcon/index.vue' 3 | describe('SvgIcon.vue', () => { 4 | it('iconClass', () => { 5 | const wrapper = shallowMount(SvgIcon, { 6 | propsData: { 7 | iconClass: 'test' 8 | } 9 | }) 10 | expect(wrapper.find('use').attributes().href).toBe('#icon-test') 11 | }) 12 | it('className', () => { 13 | const wrapper = shallowMount(SvgIcon, { 14 | propsData: { 15 | iconClass: 'test' 16 | } 17 | }) 18 | expect(wrapper.classes().length).toBe(1) 19 | wrapper.setProps({ className: 'test' }) 20 | expect(wrapper.classes().includes('test')).toBe(true) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /sidemenu-app/src/components/Link.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 34 | -------------------------------------------------------------------------------- /main-app/tests/unit/components/Hamburger.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Hamburger from '@/components/Hamburger/index.vue' 3 | describe('Hamburger.vue', () => { 4 | it('toggle click', () => { 5 | const wrapper = shallowMount(Hamburger) 6 | const mockFn = jest.fn() 7 | wrapper.vm.$on('toggleClick', mockFn) 8 | wrapper.find('.hamburger').trigger('click') 9 | expect(mockFn).toBeCalled() 10 | }) 11 | it('prop isActive', () => { 12 | const wrapper = shallowMount(Hamburger) 13 | wrapper.setProps({ isActive: true }) 14 | expect(wrapper.contains('.is-active')).toBe(true) 15 | wrapper.setProps({ isActive: false }) 16 | expect(wrapper.contains('.is-active')).toBe(false) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /main-app/src/store/modules/navbarSubapp.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | appContent: '', 3 | loading: false 4 | } 5 | 6 | const mutations = { 7 | SET_APP_CONTENT: (state, { appContent, loading }) => { 8 | state.appContent = appContent 9 | state.loading = loading 10 | }, 11 | SET_APP_LOADING: (state, status) => { 12 | state.loading = status 13 | } 14 | } 15 | 16 | const actions = { 17 | setAppContent({ commit, state }, { appContent, loading }) { 18 | if (state.appContent !== appContent) { 19 | commit('SET_APP_CONTENT', { appContent, loading }) 20 | } else { 21 | commit('SET_APP_LOADING', loading) 22 | } 23 | } 24 | } 25 | 26 | export default { 27 | namespaced: true, 28 | state, 29 | mutations, 30 | actions 31 | } 32 | -------------------------------------------------------------------------------- /main-app/src/store/modules/sidemenuSubapp.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | appContent: '', 3 | loading: false 4 | } 5 | 6 | const mutations = { 7 | SET_APP_CONTENT: (state, { appContent, loading }) => { 8 | state.appContent = appContent 9 | state.loading = loading 10 | }, 11 | SET_APP_LOADING: (state, status) => { 12 | state.loading = status 13 | } 14 | } 15 | 16 | const actions = { 17 | setAppContent({ commit, state }, { appContent, loading }) { 18 | if (state.appContent !== appContent) { 19 | commit('SET_APP_CONTENT', { appContent, loading }) 20 | } else { 21 | commit('SET_APP_LOADING', loading) 22 | } 23 | } 24 | } 25 | 26 | export default { 27 | namespaced: true, 28 | state, 29 | mutations, 30 | actions 31 | } 32 | -------------------------------------------------------------------------------- /sub-app2/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import Home from "../views/Home.vue"; 4 | 5 | Vue.use(VueRouter); 6 | 7 | const baseUrl = "/"; 8 | 9 | const routes = [ 10 | { 11 | path: baseUrl, 12 | name: "home", 13 | component: Home 14 | }, 15 | { 16 | path: "/about", 17 | name: "about", 18 | // route level code-splitting 19 | // this generates a separate chunk (about.[hash].js) for this route 20 | // which is lazy-loaded when the route is visited. 21 | component: () => 22 | import(/* webpackChunkName: "about" */ "../views/About.vue") 23 | } 24 | ]; 25 | 26 | const router = new VueRouter({ 27 | mode: "hash", 28 | routes 29 | }); 30 | 31 | export default router; 32 | -------------------------------------------------------------------------------- /sub-app2/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 33 | -------------------------------------------------------------------------------- /sub-app1/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /main-app/node_modules 3 | /sub-app1/node_modules 4 | /sub-app2/node_modules 5 | /main-app/dist 6 | /sub-app1/dist 7 | /sub-app2/dist 8 | /main-app/package-lock.json 9 | /sub-app1/package-lock.json 10 | /sub-app2/package-lock.json 11 | /main-app/yarn.lock 12 | /sub-app1/yarn.lock 13 | /sub-app2/yarn.lock 14 | 15 | # local env files 16 | .env.local 17 | .env.*.local 18 | .idea 19 | 20 | # Log files 21 | /main-app/npm-debug.log* 22 | /sub-app1/npm-debug.log* 23 | /sub-app2/npm-debug.log* 24 | /main-app/yarn-debug.log* 25 | /sub-app1/yarn-debug.log* 26 | /sub-app2/yarn-debug.log* 27 | /main-app/yarn-error.log* 28 | /sub-app1/yarn-error.log* 29 | /sub-app2/yarn-error.log* 30 | 31 | # Editor directories and files 32 | /.idea 33 | .vscode 34 | *.suo 35 | *.ntvs* 36 | *.njsproj 37 | *.sln 38 | *.sw? 39 | -------------------------------------------------------------------------------- /main-app/src/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import variables from '@/styles/element-variables.scss' 2 | import defaultSettings from '@/settings' 3 | 4 | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings 5 | 6 | const state = { 7 | theme: variables.theme, 8 | showSettings: showSettings, 9 | tagsView: tagsView, 10 | fixedHeader: fixedHeader, 11 | sidebarLogo: sidebarLogo 12 | } 13 | 14 | const mutations = { 15 | CHANGE_SETTING: (state, { key, value }) => { 16 | if (state.hasOwnProperty(key)) { 17 | state[key] = value 18 | } 19 | } 20 | } 21 | 22 | const actions = { 23 | changeSetting({ commit }, data) { 24 | commit('CHANGE_SETTING', data) 25 | } 26 | } 27 | 28 | export default { 29 | namespaced: true, 30 | state, 31 | mutations, 32 | actions 33 | } 34 | 35 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/api/article.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function fetchList(query) { 4 | return request({ 5 | url: '/article/list', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function fetchArticle(id) { 12 | return request({ 13 | url: '/article/detail', 14 | method: 'get', 15 | params: { id } 16 | }) 17 | } 18 | 19 | export function fetchPv(pv) { 20 | return request({ 21 | url: '/article/pv', 22 | method: 'get', 23 | params: { pv } 24 | }) 25 | } 26 | 27 | export function createArticle(data) { 28 | return request({ 29 | url: '/article/create', 30 | method: 'post', 31 | data 32 | }) 33 | } 34 | 35 | export function updateArticle(data) { 36 | return request({ 37 | url: '/article/update', 38 | method: 'post', 39 | data 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /main-app/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Cookies from 'js-cookie' 3 | import 'normalize.css/normalize.css' 4 | import Element from 'element-ui' 5 | import '@/styles/element-variables.scss' 6 | import '@/styles/index.scss' 7 | import App from './App' 8 | import store from './store' 9 | import router from './router' 10 | import './icons' 11 | import './permission' 12 | import './utils/error-log' 13 | import * as filters from './filters' 14 | import { mockXHR } from '../mock' 15 | if (process.env.NODE_ENV === 'production') { 16 | mockXHR() 17 | } 18 | 19 | Vue.use(Element, { 20 | size: Cookies.get('size') || 'medium' 21 | }) 22 | 23 | Object.keys(filters).forEach(key => { 24 | Vue.filter(key, filters[key]) 25 | }) 26 | 27 | Vue.config.productionTip = false 28 | 29 | window.$store = store 30 | 31 | // 初始化主应用 32 | new Vue({ 33 | router, 34 | store, 35 | render: h => h(App) 36 | }).$mount('#app') 37 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sub-app2/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from "./store"; 5 | 6 | Vue.config.productionTip = false; 7 | 8 | Vue.prototype.$test = "sub"; 9 | 10 | let instance = null; 11 | 12 | const vueOptions = { 13 | el: "#subapp2", 14 | router, 15 | store 16 | }; 17 | 18 | if (!window.singleSpaNavigate) { 19 | // 检测是否是single-spa状态, 不是则独立运行 20 | delete vueOptions.el; 21 | new Vue({ ...vueOptions, render: h => h(App) }).$mount("#subapp2"); 22 | } 23 | 24 | export async function bootstrap() { 25 | console.log("subapp2 app bootstraped"); 26 | } 27 | 28 | export async function mount(props) { 29 | instance = new Vue({ 30 | ...vueOptions, 31 | render: h => h(App) 32 | }); 33 | } 34 | 35 | export async function unmount() { 36 | console.log("subapp2 app unmount"); 37 | instance.$destroy(); 38 | instance = null; 39 | } 40 | -------------------------------------------------------------------------------- /main-app/src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.off('error') 26 | clipboard.off('success') 27 | clipboard.destroy() 28 | }) 29 | clipboard.on('error', () => { 30 | clipboardError() 31 | clipboard.off('error') 32 | clipboard.off('success') 33 | clipboard.destroy() 34 | }) 35 | clipboard.onClick(event) 36 | } 37 | -------------------------------------------------------------------------------- /main-app/src/views/profile/components/Account.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 39 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // base color 2 | $blue:#324157; 3 | $light-blue:#3A71A8; 4 | $red:#C03639; 5 | $pink: #E65D6E; 6 | $green: #30B08F; 7 | $tiffany: #4AB7BD; 8 | $yellow:#FEC171; 9 | $panGreen: #30B08F; 10 | 11 | // sidebar 12 | $menuText:#bfcbd9; 13 | $menuActiveText:#409EFF; 14 | $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 15 | 16 | $menuBg:#304156; 17 | $menuHover:#263445; 18 | 19 | $subMenuBg:#1f2d3d; 20 | $subMenuHover:#001528; 21 | 22 | $sideBarWidth: 210px; 23 | 24 | // the :export directive is the magic sauce for webpack 25 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 26 | :export { 27 | menuText: $menuText; 28 | menuActiveText: $menuActiveText; 29 | subMenuActiveText: $subMenuActiveText; 30 | menuBg: $menuBg; 31 | menuHover: $menuHover; 32 | subMenuBg: $subMenuBg; 33 | subMenuHover: $subMenuHover; 34 | sideBarWidth: $sideBarWidth; 35 | } 36 | -------------------------------------------------------------------------------- /main-app/src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #1890ff; 8 | $--color-success: #13ce66; 9 | $--color-warning: #FFBA00; 10 | $--color-danger: #ff4949; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border:1px solid#dfe6ec; 21 | 22 | /* icon font path, required */ 23 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 24 | 25 | @import "~element-ui/packages/theme-chalk/src/index"; 26 | 27 | // the :export directive is the magic sauce for webpack 28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 29 | :export { 30 | theme: $--color-primary; 31 | } 32 | -------------------------------------------------------------------------------- /main-app/src/directive/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } -------------------------------------------------------------------------------- /main-app/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global 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-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /nav-app/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir); 5 | } 6 | 7 | const port = 8083; 8 | 9 | module.exports = { 10 | publicPath: `//localhost:${port}`, 11 | outputDir: "dist", 12 | assetsDir: "static", 13 | indexPath: "index.html", 14 | filenameHashing: true, 15 | lintOnSave: true, 16 | devServer: { 17 | host: "0.0.0.0", 18 | hot: true, 19 | disableHostCheck: true, 20 | port, 21 | overlay: { 22 | warnings: false, 23 | errors: true 24 | }, 25 | headers: { 26 | "Access-Control-Allow-Origin": "*" 27 | } 28 | }, 29 | // 自定义webpack配置 30 | configureWebpack: { 31 | resolve: { 32 | alias: { 33 | "@": resolve("src") 34 | } 35 | }, 36 | output: { 37 | library: "navapp", 38 | filename: "[name].[hash:8].js", 39 | libraryTarget: "umd", 40 | globalObject: "this" 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Vue Element Admin', 3 | 4 | /** 5 | * @type {boolean} true | false 6 | * @description Whether show the settings right-panel 7 | */ 8 | showSettings: true, 9 | 10 | /** 11 | * @type {boolean} true | false 12 | * @description Whether need tagsView 13 | */ 14 | tagsView: true, 15 | 16 | /** 17 | * @type {boolean} true | false 18 | * @description Whether fix the header 19 | */ 20 | fixedHeader: false, 21 | 22 | /** 23 | * @type {boolean} true | false 24 | * @description Whether show the logo in sidebar 25 | */ 26 | sidebarLogo: false, 27 | 28 | /** 29 | * @type {string | array} 'production' | ['production', 'development'] 30 | * @description Need show err logs component. 31 | * The default is only used in the production env 32 | * If you want to also use it in dev, you can pass ['production', 'development'] 33 | */ 34 | errorLog: 'production' 35 | } 36 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sub-app2/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir); 5 | } 6 | 7 | const port = 8082; 8 | 9 | module.exports = { 10 | publicPath: `//localhost:${port}`, 11 | outputDir: "dist", 12 | assetsDir: "static", 13 | indexPath: "index.html", 14 | filenameHashing: true, 15 | lintOnSave: true, 16 | devServer: { 17 | host: "0.0.0.0", 18 | hot: true, 19 | disableHostCheck: true, 20 | port, 21 | overlay: { 22 | warnings: false, 23 | errors: true 24 | }, 25 | headers: { 26 | "Access-Control-Allow-Origin": "*" 27 | } 28 | }, 29 | // 自定义webpack配置 30 | configureWebpack: { 31 | // name: name, 32 | resolve: { 33 | alias: { 34 | "@": resolve("src") 35 | } 36 | }, 37 | output: { 38 | library: "subapp2", 39 | filename: "[name].[hash:8].js", 40 | libraryTarget: "umd", 41 | globalObject: "this" 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /sidemenu-app/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir); 5 | } 6 | 7 | const port = 8084; 8 | 9 | module.exports = { 10 | publicPath: `//localhost:${port}`, 11 | outputDir: "dist", 12 | assetsDir: "static", 13 | indexPath: "index.html", 14 | filenameHashing: true, 15 | lintOnSave: true, 16 | devServer: { 17 | host: "0.0.0.0", 18 | hot: true, 19 | disableHostCheck: true, 20 | port, 21 | overlay: { 22 | warnings: false, 23 | errors: true 24 | }, 25 | headers: { 26 | "Access-Control-Allow-Origin": "*" 27 | } 28 | }, 29 | // 自定义webpack配置 30 | configureWebpack: { 31 | // name: name, 32 | resolve: { 33 | alias: { 34 | "@": resolve("src") 35 | } 36 | }, 37 | output: { 38 | library: "sidemenuapp", 39 | filename: "[name].[hash:8].js", 40 | libraryTarget: "umd", 41 | globalObject: "this" 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sub-app1/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir); 5 | } 6 | 7 | const port = 8081; 8 | var name = 'sub-app2' 9 | 10 | module.exports = { 11 | publicPath: `//localhost:${port}`, 12 | outputDir: "dist", 13 | assetsDir: "static", 14 | indexPath: "index.html", 15 | filenameHashing: true, 16 | lintOnSave: true, 17 | devServer: { 18 | host: "0.0.0.0", 19 | hot: true, 20 | disableHostCheck: true, 21 | port, 22 | overlay: { 23 | warnings: false, 24 | errors: true 25 | }, 26 | headers: { 27 | "Access-Control-Allow-Origin": "*" 28 | } 29 | }, 30 | // 自定义webpack配置 31 | configureWebpack: { 32 | name: name, 33 | resolve: { 34 | alias: { 35 | "@": resolve("src") 36 | } 37 | }, 38 | output: { 39 | library: "subapp1", 40 | filename: "[name].[hash:8].js", 41 | libraryTarget: "umd", 42 | globalObject: "this" 43 | } 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /main-app/build/index.js: -------------------------------------------------------------------------------- 1 | const { run } = require('runjs') 2 | const chalk = require('chalk') 3 | const config = require('../vue.config.js') 4 | const rawArgv = process.argv.slice(2) 5 | const args = rawArgv.join(' ') 6 | 7 | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { 8 | const report = rawArgv.includes('--report') 9 | 10 | run(`vue-cli-service build ${args}`) 11 | 12 | const port = 9526 13 | const publicPath = config.publicPath 14 | 15 | var connect = require('connect') 16 | var serveStatic = require('serve-static') 17 | const app = connect() 18 | 19 | app.use( 20 | publicPath, 21 | serveStatic('./dist', { 22 | index: ['index.html', '/'] 23 | }) 24 | ) 25 | 26 | app.listen(port, function () { 27 | console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) 28 | if (report) { 29 | console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) 30 | } 31 | 32 | }) 33 | } else { 34 | run(`vue-cli-service build ${args}`) 35 | } 36 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/utils/error-log.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '@/store' 3 | import { isString, isArray } from '@/utils/validate' 4 | import settings from '@/settings' 5 | 6 | // you can set in settings.js 7 | // errorLog:'production' | ['production', 'development'] 8 | const { errorLog: needErrorLog } = settings 9 | 10 | function checkNeed() { 11 | const env = process.env.NODE_ENV 12 | if (isString(needErrorLog)) { 13 | return env === needErrorLog 14 | } 15 | if (isArray(needErrorLog)) { 16 | return needErrorLog.includes(env) 17 | } 18 | return false 19 | } 20 | 21 | if (checkNeed()) { 22 | Vue.config.errorHandler = function(err, vm, info, a) { 23 | // Don't ask me why I use Vue.nextTick, it just a hack. 24 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 25 | Vue.nextTick(() => { 26 | store.dispatch('errorLog/addErrorLog', { 27 | err, 28 | vm, 29 | info, 30 | url: window.location.href 31 | }) 32 | console.error(err, info) 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /main-app/tests/unit/utils/parseTime.spec.js: -------------------------------------------------------------------------------- 1 | import { parseTime } from '@/utils/index.js' 2 | describe('Utils:parseTime', () => { 3 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 4 | it('timestamp', () => { 5 | expect(parseTime(d)).toBe('2018-07-13 17:54:01') 6 | }) 7 | it('ten digits timestamp', () => { 8 | expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') 9 | }) 10 | it('new Date', () => { 11 | expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') 12 | }) 13 | it('format', () => { 14 | expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 15 | expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 16 | expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 17 | }) 18 | it('get the day of the week', () => { 19 | expect(parseTime(d, '{a}')).toBe('五') // 星期五 20 | }) 21 | it('get the day of the week', () => { 22 | expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 23 | }) 24 | it('empty argument', () => { 25 | expect(parseTime()).toBeNull() 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/components/Charts/mixins/resize.js: -------------------------------------------------------------------------------- 1 | import { debounce } from '@/utils' 2 | 3 | export default { 4 | data() { 5 | return { 6 | $_sidebarElm: null 7 | } 8 | }, 9 | mounted() { 10 | this.__resizeHandler = debounce(() => { 11 | if (this.chart) { 12 | this.chart.resize() 13 | } 14 | }, 100) 15 | window.addEventListener('resize', this.__resizeHandler) 16 | 17 | this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] 18 | this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) 19 | }, 20 | beforeDestroy() { 21 | window.removeEventListener('resize', this.__resizeHandler) 22 | 23 | this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) 24 | }, 25 | methods: { 26 | // use $_ for mixins properties 27 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential 28 | $_sidebarResizeHandler(e) { 29 | if (e.propertyName === 'width') { 30 | this.__resizeHandler() 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sidemenu-app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import { Scrollbar, Menu, MenuItem, Submenu } from "element-ui"; 3 | import App from "./App.vue"; 4 | import router from "./router"; 5 | import store from "./store"; 6 | import "./icons"; 7 | 8 | if (!window.$store) { 9 | window.$store = { 10 | state: {}, 11 | mutations: {}, 12 | actions: {}, 13 | getters: {} 14 | }; 15 | window.$store.dispatch = function() { 16 | return null; 17 | }; 18 | } 19 | 20 | Vue.use(Scrollbar); 21 | Vue.use(Menu); 22 | Vue.use(Submenu); 23 | Vue.use(MenuItem); 24 | 25 | Vue.config.productionTip = false; 26 | 27 | let instance = null; 28 | 29 | function render() { 30 | instance = new Vue({ 31 | store, 32 | router, 33 | render: h => h(App) 34 | }).$mount("#sidemenuApp"); 35 | } 36 | 37 | if (!window.singleSpaNavigate) { 38 | render(); 39 | } 40 | 41 | export async function bootstrap() { 42 | console.log("sidemenuApp app bootstraped"); 43 | } 44 | 45 | export async function mount(props) { 46 | render(); 47 | } 48 | 49 | export async function unmount() { 50 | instance.$destroy(); 51 | instance = null; 52 | } 53 | -------------------------------------------------------------------------------- /sub-app1/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import ElementUI from "element-ui"; 3 | import "element-ui/lib/theme-chalk/index.css"; 4 | import App from "./App.vue"; 5 | import router from "./router"; 6 | import store from "./store"; 7 | 8 | if(!window.$store){ 9 | window.$store = {} 10 | window.$store.dispatch = function(){return null} 11 | } 12 | 13 | Vue.config.productionTip = false; 14 | 15 | Vue.use(ElementUI); 16 | 17 | let instance = null; 18 | 19 | const vueOptions = { 20 | el: "#subapp1", 21 | router, 22 | store 23 | }; 24 | 25 | if (!window.singleSpaNavigate) { 26 | // 检测是否是single-spa状态, 不是则独立运行 27 | delete vueOptions.el; 28 | new Vue({ ...vueOptions, render: h => h(App) }).$mount("#subapp1"); 29 | } 30 | 31 | export async function bootstrap() { 32 | console.log("subapp1 app bootstraped"); 33 | } 34 | 35 | export async function mount(props) { 36 | vueOptions.el = "#subapp1" 37 | instance = new Vue({ 38 | ...vueOptions, 39 | render: h => h(App) 40 | }); 41 | } 42 | 43 | export async function unmount() { 44 | // console.log("subapp1 app unmount"); 45 | instance.$destroy(); 46 | instance = null; 47 | } 48 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nav-app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import ElementUI from "element-ui"; 3 | import "element-ui/lib/theme-chalk/index.css"; 4 | import App from "./App.vue"; 5 | import router from "./router"; 6 | import store from "./store"; //nav自己的状态, 可以不用 7 | 8 | if (!window.$store) { 9 | window.$store = { 10 | state: {}, 11 | mutations: {}, 12 | actions: {}, 13 | getters: {} 14 | }; 15 | window.$store.dispatch = function() { 16 | return null; 17 | }; 18 | } 19 | 20 | Vue.config.productionTip = false; 21 | 22 | Vue.use(ElementUI); 23 | 24 | let instance = null; 25 | 26 | const vueOptions = { 27 | router, 28 | store 29 | }; 30 | 31 | if (!window.singleSpaNavigate) { 32 | new Vue({ ...vueOptions, render: h => h(App) }).$mount("#navapp"); 33 | } 34 | 35 | export async function bootstrap() { 36 | console.log("navapp app bootstraped"); 37 | } 38 | 39 | export async function mount(props) { 40 | instance = new Vue({ 41 | ...vueOptions, 42 | render: h => h(App) 43 | }).$mount("#navapp"); 44 | } 45 | 46 | export async function unmount() { 47 | console.log("navapp app unmount"); 48 | instance.$destroy(); 49 | instance = null; 50 | } 51 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/tests/unit/utils/formatTime.spec.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '@/utils/index.js' 2 | describe('Utils:formatTime', () => { 3 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 4 | const retrofit = 5 * 1000 5 | 6 | it('ten digits timestamp', () => { 7 | expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') 8 | }) 9 | it('test now', () => { 10 | expect(formatTime(+new Date() - 1)).toBe('刚刚') 11 | }) 12 | it('less two minute', () => { 13 | expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') 14 | }) 15 | it('less two hour', () => { 16 | expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') 17 | }) 18 | it('less one day', () => { 19 | expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') 20 | }) 21 | it('more than one day', () => { 22 | expect(formatTime(d)).toBe('7月13日17时54分') 23 | }) 24 | it('format', () => { 25 | expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 26 | expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 27 | expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/tests/unit/utils/validate.spec.js: -------------------------------------------------------------------------------- 1 | import { validUsername, validURL, validLowerCase, validUpperCase, validAlphabets } from '@/utils/validate.js' 2 | describe('Utils:validate', () => { 3 | it('validUsername', () => { 4 | expect(validUsername('admin')).toBe(true) 5 | expect(validUsername('editor')).toBe(true) 6 | expect(validUsername('xxxx')).toBe(false) 7 | }) 8 | it('validURL', () => { 9 | expect(validURL('https://github.com/PanJiaChen/vue-element-admin')).toBe(true) 10 | expect(validURL('http://github.com/PanJiaChen/vue-element-admin')).toBe(true) 11 | expect(validURL('github.com/PanJiaChen/vue-element-admin')).toBe(false) 12 | }) 13 | it('validLowerCase', () => { 14 | expect(validLowerCase('abc')).toBe(true) 15 | expect(validLowerCase('Abc')).toBe(false) 16 | expect(validLowerCase('123abc')).toBe(false) 17 | }) 18 | it('validUpperCase', () => { 19 | expect(validUpperCase('ABC')).toBe(true) 20 | expect(validUpperCase('Abc')).toBe(false) 21 | expect(validUpperCase('123ABC')).toBe(false) 22 | }) 23 | it('validAlphabets', () => { 24 | expect(validAlphabets('ABC')).toBe(true) 25 | expect(validAlphabets('Abc')).toBe(true) 26 | expect(validAlphabets('123aBC')).toBe(false) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /main-app/src/views/profile/components/Timeline.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 44 | -------------------------------------------------------------------------------- /main-app/src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/src/utils/open-window.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by PanJiaChen on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | export default function openWindow(url, title, w, h) { 9 | // Fixes dual-screen position Most browsers Firefox 10 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left 11 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top 12 | 13 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width 14 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height 15 | 16 | const left = ((width / 2) - (w / 2)) + dualScreenLeft 17 | const top = ((height / 2) - (h / 2)) + dualScreenTop 18 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) 19 | 20 | // Puts focus on the newWindow 21 | if (window.focus) { 22 | newWindow.focus() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /sidemenu-app/src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main-app/mock/remote-search.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | 3 | const NameList = [] 4 | const count = 100 5 | 6 | for (let i = 0; i < count; i++) { 7 | NameList.push(Mock.mock({ 8 | name: '@first' 9 | })) 10 | } 11 | NameList.push({ name: 'mock-Pan' }) 12 | 13 | export default [ 14 | // username search 15 | { 16 | url: '/search/user', 17 | type: 'get', 18 | response: config => { 19 | const { name } = config.query 20 | const mockNameList = NameList.filter(item => { 21 | const lowerCaseName = item.name.toLowerCase() 22 | return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0) 23 | }) 24 | return { 25 | code: 20000, 26 | data: { items: mockNameList } 27 | } 28 | } 29 | }, 30 | 31 | // transaction list 32 | { 33 | url: '/transaction/list', 34 | type: 'get', 35 | response: _ => { 36 | return { 37 | code: 20000, 38 | data: { 39 | total: 20, 40 | 'items|20': [{ 41 | order_no: '@guid()', 42 | timestamp: +Mock.Random.date('T'), 43 | username: '@name()', 44 | price: '@float(1000, 15000, 0, 2)', 45 | 'status|1': ['success', 'pending'] 46 | }] 47 | } 48 | } 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /main-app/src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 61 | -------------------------------------------------------------------------------- /main-app/src/layout/mixin/ResizeHandler.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | const { body } = document 4 | const WIDTH = 992 // refer to Bootstrap's responsive design 5 | 6 | export default { 7 | watch: { 8 | $route(route) { 9 | if (this.device === 'mobile' && this.sidebar.opened) { 10 | store.dispatch('app/closeSideBar', { withoutAnimation: false }) 11 | } 12 | } 13 | }, 14 | beforeMount() { 15 | window.addEventListener('resize', this.$_resizeHandler) 16 | }, 17 | beforeDestroy() { 18 | window.removeEventListener('resize', this.$_resizeHandler) 19 | }, 20 | mounted() { 21 | const isMobile = this.$_isMobile() 22 | if (isMobile) { 23 | store.dispatch('app/toggleDevice', 'mobile') 24 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 25 | } 26 | }, 27 | methods: { 28 | // use $_ for mixins properties 29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential 30 | $_isMobile() { 31 | const rect = body.getBoundingClientRect() 32 | return rect.width - 1 < WIDTH 33 | }, 34 | $_resizeHandler() { 35 | if (!document.hidden) { 36 | const isMobile = this.$_isMobile() 37 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') 38 | 39 | if (isMobile) { 40 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /main-app/src/directive/el-table/adaptive.js: -------------------------------------------------------------------------------- 1 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' 2 | 3 | /** 4 | * How to use 5 | * ... 6 | * el-table height is must be set 7 | * bottomOffset: 30(default) // The height of the table from the bottom of the page. 8 | */ 9 | 10 | const doResize = (el, binding, vnode) => { 11 | const { componentInstance: $table } = vnode 12 | 13 | const { value } = binding 14 | 15 | if (!$table.height) { 16 | throw new Error(`el-$table must set the height. Such as height='100px'`) 17 | } 18 | const bottomOffset = (value && value.bottomOffset) || 30 19 | 20 | if (!$table) return 21 | 22 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset 23 | $table.layout.setHeight(height) 24 | $table.doLayout() 25 | } 26 | 27 | export default { 28 | bind(el, binding, vnode) { 29 | el.resizeListener = () => { 30 | doResize(el, binding, vnode) 31 | } 32 | // parameter 1 is must be "Element" type 33 | addResizeListener(window.document.body, el.resizeListener) 34 | }, 35 | inserted(el, binding, vnode) { 36 | doResize(el, binding, vnode) 37 | }, 38 | unbind(el) { 39 | removeResizeListener(window.document.body, el.resizeListener) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /main-app/src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 37 | 38 | 50 | -------------------------------------------------------------------------------- /nav-app/src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 37 | 38 | 50 | -------------------------------------------------------------------------------- /main-app/plop-templates/view/prompt.js: -------------------------------------------------------------------------------- 1 | const { notEmpty } = require('../utils.js') 2 | 3 | module.exports = { 4 | description: 'generate a view', 5 | prompts: [{ 6 | type: 'input', 7 | name: 'name', 8 | message: 'view name please', 9 | validate: notEmpty('name') 10 | }, 11 | { 12 | type: 'checkbox', 13 | name: 'blocks', 14 | message: 'Blocks:', 15 | choices: [{ 16 | name: '