├── 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 |
2 |
3 |
This is an about page by APP1
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sub-app2/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page by APP2
4 |
5 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
3 |
{{ msg }}
4 |
5 |
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 |
3 |
4 |
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 |
3 |
4 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
3 |
{{ msg }}
4 |
5 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
3 |
4 |
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 |
2 |
3 |
4 |
我是子应用二,sub-app2
5 | 子应用二:首页 |
6 | 子应用二:about页面
7 |
8 |
9 |
10 |
11 |
12 |
33 |
--------------------------------------------------------------------------------
/sub-app1/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
我是子应用一,sub-app1
5 | 子应用一:首页dsds |||
6 | 子应用一:about页面
7 |
8 |
9 |
10 |
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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Update
11 |
12 |
13 |
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 |
2 |
3 |
4 |
5 |
6 | {{ item.title }}
7 | {{ item.content }}
8 |
9 |
10 |
11 |
12 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
19 |
20 |
21 |
37 |
38 |
50 |
--------------------------------------------------------------------------------
/nav-app/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
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: '',
17 | value: 'template',
18 | checked: true
19 | },
20 | {
21 | name: '
56 |
--------------------------------------------------------------------------------
/main-app/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
47 |
48 |
63 |
--------------------------------------------------------------------------------
/sidemenu-app/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
47 |
48 |
63 |
--------------------------------------------------------------------------------
/main-app/src/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix {
2 | &:after {
3 | content: "";
4 | display: table;
5 | clear: both;
6 | }
7 | }
8 |
9 | @mixin scrollBar {
10 | &::-webkit-scrollbar-track-piece {
11 | background: #d3dce6;
12 | }
13 |
14 | &::-webkit-scrollbar {
15 | width: 6px;
16 | }
17 |
18 | &::-webkit-scrollbar-thumb {
19 | background: #99a9bf;
20 | border-radius: 20px;
21 | }
22 | }
23 |
24 | @mixin relative {
25 | position: relative;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | @mixin pct($pct) {
31 | width: #{$pct};
32 | position: relative;
33 | margin: 0 auto;
34 | }
35 |
36 | @mixin triangle($width, $height, $color, $direction) {
37 | $width: $width/2;
38 | $color-border-style: $height solid $color;
39 | $transparent-border-style: $width solid transparent;
40 | height: 0;
41 | width: 0;
42 |
43 | @if $direction==up {
44 | border-bottom: $color-border-style;
45 | border-left: $transparent-border-style;
46 | border-right: $transparent-border-style;
47 | }
48 |
49 | @else if $direction==right {
50 | border-left: $color-border-style;
51 | border-top: $transparent-border-style;
52 | border-bottom: $transparent-border-style;
53 | }
54 |
55 | @else if $direction==down {
56 | border-top: $color-border-style;
57 | border-left: $transparent-border-style;
58 | border-right: $transparent-border-style;
59 | }
60 |
61 | @else if $direction==left {
62 | border-right: $color-border-style;
63 | border-top: $transparent-border-style;
64 | border-bottom: $transparent-border-style;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/main-app/src/components/SizeSelect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{
9 | item.label }}
10 |
11 |
12 |
13 |
14 |
15 |
58 |
--------------------------------------------------------------------------------
/main-app/src/layout/components/AppMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
34 |
35 |
59 |
60 |
68 |
--------------------------------------------------------------------------------
/main-app/src/components/DragSelect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
50 |
51 |
62 |
--------------------------------------------------------------------------------
/main-app/src/permission.js:
--------------------------------------------------------------------------------
1 | import router from './router'
2 | import store from './store'
3 | import { Message } from 'element-ui'
4 | import NProgress from 'nprogress'
5 | import 'nprogress/nprogress.css'
6 | import { getToken } from '@/utils/auth'
7 | import getPageTitle from '@/utils/get-page-title'
8 |
9 | NProgress.configure({ showSpinner: false }) // NProgress Configuration
10 |
11 | const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
12 |
13 | router.beforeEach(async(to, from, next) => {
14 | NProgress.start()
15 | document.title = getPageTitle(to.meta.title)
16 | const hasToken = getToken()
17 | if (hasToken) {
18 | if (to.path === '/login') {
19 | next({ path: '/' })
20 | NProgress.done()
21 | } else {
22 | const hasRoles = store.getters.roles && store.getters.roles.length > 0
23 | if (hasRoles) {
24 | next()
25 | } else {
26 | try {
27 | const { roles } = await store.dispatch('user/getInfo')
28 | const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
29 | router.addRoutes(accessRoutes)
30 | next({ ...to, replace: true })
31 | } catch (error) {
32 | await store.dispatch('user/resetToken')
33 | Message.error(error || 'Has Error')
34 | next(`/login?redirect=${to.path}`)
35 | NProgress.done()
36 | }
37 | }
38 | }
39 | } else {
40 | if (whiteList.indexOf(to.path) !== -1) {
41 | next()
42 | } else {
43 | next(`/login?redirect=${to.path}`)
44 | NProgress.done()
45 | }
46 | }
47 | })
48 |
49 | router.afterEach(() => {
50 | NProgress.done()
51 | })
52 |
--------------------------------------------------------------------------------
/main-app/src/icons/svg/pdf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sidemenu-app/src/icons/svg/pdf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/main-app/src/store/modules/permission.js:
--------------------------------------------------------------------------------
1 | import { asyncRoutes, constantRoutes } from '@/router'
2 |
3 | /**
4 | * Use meta.role to determine if the current user has permission
5 | * @param roles
6 | * @param route
7 | */
8 | function hasPermission(roles, route) {
9 | if (route.meta && route.meta.roles) {
10 | return roles.some(role => route.meta.roles.includes(role))
11 | } else {
12 | return true
13 | }
14 | }
15 |
16 | /**
17 | * Filter asynchronous routing tables by recursion
18 | * @param routes asyncRoutes
19 | * @param roles
20 | */
21 | export function filterAsyncRoutes(routes, roles) {
22 | const res = []
23 |
24 | routes.forEach(route => {
25 | const tmp = { ...route }
26 | if (hasPermission(roles, tmp)) {
27 | if (tmp.children) {
28 | tmp.children = filterAsyncRoutes(tmp.children, roles)
29 | }
30 | res.push(tmp)
31 | }
32 | })
33 |
34 | return res
35 | }
36 |
37 | const state = {
38 | routes: [],
39 | addRoutes: []
40 | }
41 |
42 | const mutations = {
43 | SET_ROUTES: (state, routes) => {
44 | state.addRoutes = routes
45 | state.routes = constantRoutes.concat(routes)
46 | }
47 | }
48 |
49 | const actions = {
50 | generateRoutes({ commit }, roles) {
51 | return new Promise(resolve => {
52 | let accessedRoutes
53 | if (roles.includes('admin')) {
54 | accessedRoutes = asyncRoutes || []
55 | } else {
56 | accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
57 | }
58 | commit('SET_ROUTES', accessedRoutes)
59 | resolve(accessedRoutes)
60 | })
61 | }
62 | }
63 |
64 | export default {
65 | namespaced: true,
66 | state,
67 | mutations,
68 | actions
69 | }
70 |
--------------------------------------------------------------------------------
/main-app/src/directive/clipboard/clipboard.js:
--------------------------------------------------------------------------------
1 | // Inspired by https://github.com/Inndy/vue-clipboard2
2 | const Clipboard = require('clipboard')
3 | if (!Clipboard) {
4 | throw new Error('you should npm install `clipboard` --save at first ')
5 | }
6 |
7 | export default {
8 | bind(el, binding) {
9 | if (binding.arg === 'success') {
10 | el._v_clipboard_success = binding.value
11 | } else if (binding.arg === 'error') {
12 | el._v_clipboard_error = binding.value
13 | } else {
14 | const clipboard = new Clipboard(el, {
15 | text() { return binding.value },
16 | action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
17 | })
18 | clipboard.on('success', e => {
19 | const callback = el._v_clipboard_success
20 | callback && callback(e) // eslint-disable-line
21 | })
22 | clipboard.on('error', e => {
23 | const callback = el._v_clipboard_error
24 | callback && callback(e) // eslint-disable-line
25 | })
26 | el._v_clipboard = clipboard
27 | }
28 | },
29 | update(el, binding) {
30 | if (binding.arg === 'success') {
31 | el._v_clipboard_success = binding.value
32 | } else if (binding.arg === 'error') {
33 | el._v_clipboard_error = binding.value
34 | } else {
35 | el._v_clipboard.text = function() { return binding.value }
36 | el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
37 | }
38 | },
39 | unbind(el, binding) {
40 | if (binding.arg === 'success') {
41 | delete el._v_clipboard_success
42 | } else if (binding.arg === 'error') {
43 | delete el._v_clipboard_error
44 | } else {
45 | el._v_clipboard.destroy()
46 | delete el._v_clipboard
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/main-app/src/icons/svg/exit-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/main-app/src/icons/svg/tree.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sidemenu-app/src/icons/svg/exit-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sidemenu-app/src/icons/svg/tree.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/main-app/src/filters/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Show plural label if time is plural number
3 | * @param {number} time
4 | * @param {string} label
5 | * @return {string}
6 | */
7 | function pluralize(time, label) {
8 | if (time === 1) {
9 | return time + label
10 | }
11 | return time + label + 's'
12 | }
13 |
14 | /**
15 | * @param {number} time
16 | */
17 | export function timeAgo(time) {
18 | const between = Date.now() / 1000 - Number(time)
19 | if (between < 3600) {
20 | return pluralize(~~(between / 60), ' minute')
21 | } else if (between < 86400) {
22 | return pluralize(~~(between / 3600), ' hour')
23 | } else {
24 | return pluralize(~~(between / 86400), ' day')
25 | }
26 | }
27 |
28 | /**
29 | * Number formatting
30 | * like 10000 => 10k
31 | * @param {number} num
32 | * @param {number} digits
33 | */
34 | export function numberFormatter(num, digits) {
35 | const si = [
36 | { value: 1E18, symbol: 'E' },
37 | { value: 1E15, symbol: 'P' },
38 | { value: 1E12, symbol: 'T' },
39 | { value: 1E9, symbol: 'G' },
40 | { value: 1E6, symbol: 'M' },
41 | { value: 1E3, symbol: 'k' }
42 | ]
43 | for (let i = 0; i < si.length; i++) {
44 | if (num >= si[i].value) {
45 | return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
46 | }
47 | }
48 | return num.toString()
49 | }
50 |
51 | /**
52 | * 10000 => "10,000"
53 | * @param {number} num
54 | */
55 | export function toThousandFilter(num) {
56 | return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
57 | }
58 |
59 | /**
60 | * Upper case first char
61 | * @param {String} string
62 | */
63 | export function uppercaseFirst(string) {
64 | return string.charAt(0).toUpperCase() + string.slice(1)
65 | }
66 |
--------------------------------------------------------------------------------
/main-app/src/views/profile/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
69 |
--------------------------------------------------------------------------------
/sidemenu-app/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
59 |
--------------------------------------------------------------------------------
/main-app/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { MessageBox, Message } from 'element-ui'
3 | import store from '@/store'
4 | import { getToken } from '@/utils/auth'
5 |
6 | // create an axios instance
7 | const service = axios.create({
8 | baseURL: process.env.VUE_APP_BASE_API,
9 | timeout: 5000
10 | })
11 |
12 | // request interceptor
13 | service.interceptors.request.use(
14 | config => {
15 | if (store.getters.token) {
16 | config.headers['X-Token'] = getToken()
17 | }
18 | return config
19 | },
20 | error => {
21 | console.log(error) // for debug
22 | return Promise.reject(error)
23 | }
24 | )
25 |
26 | // response interceptor
27 | service.interceptors.response.use(
28 | response => {
29 | const res = response.data
30 | if (res.code !== 20000) {
31 | Message({
32 | message: res.message || 'Error',
33 | type: 'error',
34 | duration: 5 * 1000
35 | })
36 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
37 | MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
38 | confirmButtonText: 'Re-Login',
39 | cancelButtonText: 'Cancel',
40 | type: 'warning'
41 | }).then(() => {
42 | store.dispatch('user/resetToken').then(() => {
43 | location.reload()
44 | })
45 | })
46 | }
47 | return Promise.reject(new Error(res.message || 'Error'))
48 | } else {
49 | return res
50 | }
51 | },
52 | error => {
53 | console.log('err' + error) // for debug
54 | Message({
55 | message: error.message,
56 | type: 'error',
57 | duration: 5 * 1000
58 | })
59 | return Promise.reject(error)
60 | }
61 | )
62 |
63 | export default service
64 |
--------------------------------------------------------------------------------
/main-app/src/views/dashboard/admin/components/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.$_initResizeEvent()
11 | this.$_initSidebarResizeEvent()
12 | },
13 | beforeDestroy() {
14 | this.$_destroyResizeEvent()
15 | this.$_destroySidebarResizeEvent()
16 | },
17 | // to fixed bug when cached by keep-alive
18 | // https://github.com/PanJiaChen/vue-element-admin/issues/2116
19 | activated() {
20 | this.$_initResizeEvent()
21 | this.$_initSidebarResizeEvent()
22 | },
23 | deactivated() {
24 | this.$_destroyResizeEvent()
25 | this.$_destroySidebarResizeEvent()
26 | },
27 | methods: {
28 | // use $_ for mixins properties
29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
30 | $_resizeHandler() {
31 | return debounce(() => {
32 | if (this.chart) {
33 | this.chart.resize()
34 | }
35 | }, 100)()
36 | },
37 | $_initResizeEvent() {
38 | window.addEventListener('resize', this.$_resizeHandler)
39 | },
40 | $_destroyResizeEvent() {
41 | window.removeEventListener('resize', this.$_resizeHandler)
42 | },
43 | $_sidebarResizeHandler(e) {
44 | if (e.propertyName === 'width') {
45 | this.$_resizeHandler()
46 | }
47 | },
48 | $_initSidebarResizeEvent() {
49 | this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
50 | this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
51 | },
52 | $_destroySidebarResizeEvent() {
53 | this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/main-app/src/components/Tinymce/dynamicLoadScript.js:
--------------------------------------------------------------------------------
1 | let callbacks = []
2 |
3 | function loadedTinymce() {
4 | // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
5 | // check is successfully downloaded script
6 | return window.tinymce
7 | }
8 |
9 | const dynamicLoadScript = (src, callback) => {
10 | const existingScript = document.getElementById(src)
11 | const cb = callback || function() {}
12 |
13 | if (!existingScript) {
14 | const script = document.createElement('script')
15 | script.src = src // src url for the third-party library being loaded.
16 | script.id = src
17 | document.body.appendChild(script)
18 | callbacks.push(cb)
19 | const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
20 | onEnd(script)
21 | }
22 |
23 | if (existingScript && cb) {
24 | if (loadedTinymce()) {
25 | cb(null, existingScript)
26 | } else {
27 | callbacks.push(cb)
28 | }
29 | }
30 |
31 | function stdOnEnd(script) {
32 | script.onload = function() {
33 | // this.onload = null here is necessary
34 | // because even IE9 works not like others
35 | this.onerror = this.onload = null
36 | for (const cb of callbacks) {
37 | cb(null, script)
38 | }
39 | callbacks = null
40 | }
41 | script.onerror = function() {
42 | this.onerror = this.onload = null
43 | cb(new Error('Failed to load ' + src), script)
44 | }
45 | }
46 |
47 | function ieOnEnd(script) {
48 | script.onreadystatechange = function() {
49 | if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
50 | this.onreadystatechange = null
51 | for (const cb of callbacks) {
52 | cb(null, script) // there is no way to catch loading errors in IE8
53 | }
54 | callbacks = null
55 | }
56 | }
57 | }
58 |
59 | export default dynamicLoadScript
60 |
--------------------------------------------------------------------------------
/main-app/mock/user.js:
--------------------------------------------------------------------------------
1 | const tokens = {
2 | admin: {
3 | token: 'admin-token'
4 | },
5 | editor: {
6 | token: 'editor-token'
7 | }
8 | }
9 |
10 | const users = {
11 | 'admin-token': {
12 | roles: ['admin'],
13 | introduction: 'I am a super administrator',
14 | avatar:
15 | 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
16 | name: 'Super Admin'
17 | },
18 | 'editor-token': {
19 | roles: ['editor'],
20 | introduction: 'I am an editor',
21 | avatar:
22 | 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
23 | name: 'Normal Editor'
24 | }
25 | }
26 |
27 | export default [
28 | // user login
29 | {
30 | url: '/user/login',
31 | type: 'get',
32 | response: config => {
33 | const { username } = config.query
34 | const token = tokens[username]
35 |
36 | // mock error
37 | if (!token) {
38 | return {
39 | code: 60204,
40 | message: 'Account and password are incorrect.'
41 | }
42 | }
43 |
44 | return {
45 | code: 20000,
46 | data: token
47 | }
48 | }
49 | },
50 |
51 | // get user info
52 | {
53 | url: '/user/info.*',
54 | type: 'get',
55 | response: config => {
56 | const { token } = config.query
57 | const info = users[token]
58 |
59 | // mock error
60 | if (!info) {
61 | return {
62 | code: 50008,
63 | message: 'Login failed, unable to get user details.'
64 | }
65 | }
66 |
67 | return {
68 | code: 20000,
69 | data: info
70 | }
71 | }
72 | },
73 |
74 | // user logout
75 | {
76 | url: '/user/logout',
77 | type: 'get',
78 | response: _ => {
79 | return {
80 | code: 20000,
81 | data: 'success'
82 | }
83 | }
84 | }
85 | ]
86 |
--------------------------------------------------------------------------------
/main-app/src/styles/btn.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 |
3 | @mixin colorBtn($color) {
4 | background: $color;
5 |
6 | &:hover {
7 | color: $color;
8 |
9 | &:before,
10 | &:after {
11 | background: $color;
12 | }
13 | }
14 | }
15 |
16 | .blue-btn {
17 | @include colorBtn($blue)
18 | }
19 |
20 | .light-blue-btn {
21 | @include colorBtn($light-blue)
22 | }
23 |
24 | .red-btn {
25 | @include colorBtn($red)
26 | }
27 |
28 | .pink-btn {
29 | @include colorBtn($pink)
30 | }
31 |
32 | .green-btn {
33 | @include colorBtn($green)
34 | }
35 |
36 | .tiffany-btn {
37 | @include colorBtn($tiffany)
38 | }
39 |
40 | .yellow-btn {
41 | @include colorBtn($yellow)
42 | }
43 |
44 | .pan-btn {
45 | font-size: 14px;
46 | color: #fff;
47 | padding: 14px 36px;
48 | border-radius: 8px;
49 | border: none;
50 | outline: none;
51 | transition: 600ms ease all;
52 | position: relative;
53 | display: inline-block;
54 |
55 | &:hover {
56 | background: #fff;
57 |
58 | &:before,
59 | &:after {
60 | width: 100%;
61 | transition: 600ms ease all;
62 | }
63 | }
64 |
65 | &:before,
66 | &:after {
67 | content: '';
68 | position: absolute;
69 | top: 0;
70 | right: 0;
71 | height: 2px;
72 | width: 0;
73 | transition: 400ms ease all;
74 | }
75 |
76 | &::after {
77 | right: inherit;
78 | top: inherit;
79 | left: 0;
80 | bottom: 0;
81 | }
82 | }
83 |
84 | .custom-button {
85 | display: inline-block;
86 | line-height: 1;
87 | white-space: nowrap;
88 | cursor: pointer;
89 | background: #fff;
90 | color: #fff;
91 | -webkit-appearance: none;
92 | text-align: center;
93 | box-sizing: border-box;
94 | outline: 0;
95 | margin: 0;
96 | padding: 10px 15px;
97 | font-size: 14px;
98 | border-radius: 4px;
99 | }
100 |
--------------------------------------------------------------------------------
/main-app/src/views/dashboard/admin/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
25 |
26 |
27 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
55 |
56 |
82 |
--------------------------------------------------------------------------------
/main-app/src/utils/scroll-to.js:
--------------------------------------------------------------------------------
1 | Math.easeInOutQuad = function(t, b, c, d) {
2 | t /= d / 2
3 | if (t < 1) {
4 | return c / 2 * t * t + b
5 | }
6 | t--
7 | return -c / 2 * (t * (t - 2) - 1) + b
8 | }
9 |
10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
11 | var requestAnimFrame = (function() {
12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
13 | })()
14 |
15 | /**
16 | * Because it's so fucking difficult to detect the scrolling element, just move them all
17 | * @param {number} amount
18 | */
19 | function move(amount) {
20 | document.documentElement.scrollTop = amount
21 | document.body.parentNode.scrollTop = amount
22 | document.body.scrollTop = amount
23 | }
24 |
25 | function position() {
26 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
27 | }
28 |
29 | /**
30 | * @param {number} to
31 | * @param {number} duration
32 | * @param {Function} callback
33 | */
34 | export function scrollTo(to, duration, callback) {
35 | const start = position()
36 | const change = to - start
37 | const increment = 20
38 | let currentTime = 0
39 | duration = (typeof (duration) === 'undefined') ? 500 : duration
40 | var animateScroll = function() {
41 | // increment the time
42 | currentTime += increment
43 | // find the value with the quadratic in-out easing function
44 | var val = Math.easeInOutQuad(currentTime, start, change, duration)
45 | // move the document.body
46 | move(val)
47 | // do the animation unless its over
48 | if (currentTime < duration) {
49 | requestAnimFrame(animateScroll)
50 | } else {
51 | if (callback && typeof (callback) === 'function') {
52 | // the animation is done so lets callback
53 | callback()
54 | }
55 | }
56 | }
57 | animateScroll()
58 | }
59 |
--------------------------------------------------------------------------------
/main-app/src/views/dashboard/admin/components/TodoList/Todo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
82 |
--------------------------------------------------------------------------------
/main-app/src/components/GithubCorner/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
24 |
25 |
26 |
55 |
--------------------------------------------------------------------------------
/main-app/src/views/dashboard/editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Your roles:
6 | {{ item }}
7 |
8 |
9 |
10 | {{ name }}
11 | Editor's Dashboard
12 |
13 |
14 |
15 |
![]()
16 |
17 |
18 |
19 |
20 |
42 |
43 |
75 |
--------------------------------------------------------------------------------