├── .editorconfig ├── .env.development ├── .env.production ├── .env.staging ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── build └── index.js ├── dist ├── index.html └── static │ ├── css │ ├── app.cda1ed7c.css │ ├── chunk-4028b836.64c5c8b4.css │ ├── chunk-768ebd2f.a1c9ad1b.css │ ├── chunk-cbb61e6a.6e6cbd15.css │ ├── chunk-d7a7d948.5719ffb1.css │ ├── chunk-elementUI.18b11d0e.css │ ├── chunk-f1a6febe.2f3c92ef.css │ ├── chunk-f8cee08e.305e1e16.css │ └── chunk-libs.6d232b10.css │ ├── fonts │ ├── element-icons.2fad952a.woff │ ├── element-icons.6f0a7632.ttf │ ├── fontawesome-webfont.674f50d2.eot │ ├── fontawesome-webfont.af7ae505.woff2 │ ├── fontawesome-webfont.b06871f2.ttf │ └── fontawesome-webfont.fee66e71.woff │ ├── img │ ├── favicon.ico │ ├── fontawesome-webfont.912ec66d.svg │ └── logo.d5e2a9f1.png │ └── js │ ├── app.cd0f044f.js │ ├── chunk-2d230fe7.1db2b25c.js │ ├── chunk-4028b836.b239e86a.js │ ├── chunk-73e109b0.c2b18a1b.js │ ├── chunk-768ebd2f.9c519e42.js │ ├── chunk-cbb61e6a.921b9935.js │ ├── chunk-d7a7d948.57e2af3f.js │ ├── chunk-elementUI.03eb003b.js │ ├── chunk-f1a6febe.52c245c4.js │ ├── chunk-f8cee08e.78f2e4d8.js │ └── chunk-libs.951c1a79.js ├── jest.config.js ├── jsconfig.json ├── mock ├── article.js ├── finance.js ├── index.js ├── mock-server.js ├── table.js └── user.js ├── package.json ├── postcss.config.js ├── public ├── index.html └── static │ └── img │ └── favicon.ico ├── src ├── App.vue ├── api │ ├── article.js │ ├── table.js │ └── user.js ├── assets │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ ├── custom-theme │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ └── index.css │ └── logo.png ├── components │ ├── Breadcrumb │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── Pagination │ │ └── index.vue │ ├── Screenfull │ │ └── index.vue │ └── SvgIcon │ │ └── index.vue ├── directive │ └── waves │ │ ├── index.js │ │ ├── waves.css │ │ └── waves.js ├── icons │ ├── index.js │ ├── svg │ │ ├── 404.svg │ │ ├── bug.svg │ │ ├── chart.svg │ │ ├── clipboard.svg │ │ ├── component.svg │ │ ├── dashboard.svg │ │ ├── documentation.svg │ │ ├── drag.svg │ │ ├── edit.svg │ │ ├── education.svg │ │ ├── email.svg │ │ ├── example.svg │ │ ├── excel.svg │ │ ├── exit-fullscreen.svg │ │ ├── eye-open.svg │ │ ├── eye.svg │ │ ├── form.svg │ │ ├── fullscreen.svg │ │ ├── guide.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── language.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── lock.svg │ │ ├── message.svg │ │ ├── money.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── pdf.svg │ │ ├── people.svg │ │ ├── peoples.svg │ │ ├── qq.svg │ │ ├── search.svg │ │ ├── shopping.svg │ │ ├── size.svg │ │ ├── skill.svg │ │ ├── star.svg │ │ ├── tab.svg │ │ ├── table.svg │ │ ├── theme.svg │ │ ├── tree-table.svg │ │ ├── tree.svg │ │ ├── user.svg │ │ ├── wechat.svg │ │ └── zip.svg │ └── svgo.yml ├── layout │ ├── components │ │ ├── AppMain.vue │ │ ├── Navbar.vue │ │ ├── Sidebar │ │ │ ├── FixiOSBug.js │ │ │ ├── Item.vue │ │ │ ├── Link.vue │ │ │ ├── Logo.vue │ │ │ ├── SidebarItem.vue │ │ │ └── index.vue │ │ ├── TagsView │ │ │ ├── ScrollPane.vue │ │ │ └── index.vue │ │ └── index.js │ ├── index.vue │ └── mixin │ │ └── ResizeHandler.js ├── main.js ├── permission.js ├── router │ └── index.js ├── settings.js ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── app.js │ │ ├── permission.js │ │ ├── settings.js │ │ ├── tagsView.js │ │ └── user.js ├── styles │ ├── element-ui.scss │ ├── index.scss │ ├── mixin.scss │ ├── sidebar.scss │ ├── transition.scss │ └── variables.scss ├── utils │ ├── auth.js │ ├── get-page-title.js │ ├── index.js │ ├── request.js │ ├── scroll-to.js │ └── validate.js ├── vendor │ ├── Export2Excel.js │ └── Export2Zip.js └── views │ ├── 404.vue │ ├── dashboard │ └── index.vue │ ├── documentation │ ├── details.vue │ └── index.vue │ ├── editor │ ├── index.scss │ ├── index.vue │ └── mixin.js │ ├── financialQuery │ └── balance.vue │ ├── login │ └── index.vue │ ├── nested │ ├── menu1 │ │ ├── index.vue │ │ ├── menu1-1 │ │ │ └── index.vue │ │ ├── menu1-2 │ │ │ ├── index.vue │ │ │ ├── menu1-2-1 │ │ │ │ └── index.vue │ │ │ └── menu1-2-2 │ │ │ │ └── index.vue │ │ └── menu1-3 │ │ │ └── index.vue │ └── menu2 │ │ └── index.vue │ └── redirect │ └── index.vue ├── static ├── css │ ├── app.7c98ca2f.css │ ├── chunk-575555d9.96b835c7.css │ ├── chunk-768ebd2f.a1c9ad1b.css │ ├── chunk-cbb61e6a.6e6cbd15.css │ ├── chunk-d7a7d948.5719ffb1.css │ ├── chunk-elementUI.18b11d0e.css │ ├── chunk-f1a6febe.2f3c92ef.css │ └── chunk-libs.6d232b10.css ├── fonts │ ├── element-icons.2fad952a.woff │ ├── element-icons.6f0a7632.ttf │ ├── fontawesome-webfont.674f50d2.eot │ ├── fontawesome-webfont.af7ae505.woff2 │ ├── fontawesome-webfont.b06871f2.ttf │ └── fontawesome-webfont.fee66e71.woff ├── img │ ├── favicon.ico │ ├── fontawesome-webfont.912ec66d.svg │ └── logo.7ec6731e.png └── js │ ├── app.3c8504dc.js │ ├── chunk-2d0da8f7.37c90bb0.js │ ├── chunk-2d21446b.65936c26.js │ ├── chunk-575555d9.a6bafad6.js │ ├── chunk-73e109b0.c2b18a1b.js │ ├── chunk-768ebd2f.3a755ac3.js │ ├── chunk-cbb61e6a.eb74048e.js │ ├── chunk-d7a7d948.06e0cdfa.js │ ├── chunk-elementUI.03eb003b.js │ ├── chunk-f1a6febe.6af28b13.js │ └── chunk-libs.983c21dd.js ├── tests └── unit │ ├── .eslintrc.js │ ├── components │ ├── Breadcrumb.spec.js │ ├── Hamburger.spec.js │ └── SvgIcon.spec.js │ └── utils │ ├── formatTime.spec.js │ ├── parseTime.spec.js │ └── validate.spec.js └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://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 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | # VUE_APP_BASE_API = '/dev-api' 6 | VUE_APP_BASE_API = '/dev-api' 7 | 8 | # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, 9 | # to control whether the babel-plugin-dynamic-import-node plugin is enabled. 10 | # It only does one thing by converting all import() to require(). 11 | # This configuration can significantly increase the speed of hot updates, 12 | # when you have a large number of pages. 13 | # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js 14 | 15 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 16 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/prod-api' 6 | 7 | -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/dev-api' 8 | 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | package-lock.json 7 | tests/**/coverage/ 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-element-admin-g6-editor 2 | 基于vue-element-admin框架模板集成了g6-editor,保留了vue-element-admin模板,没有多余的组件代码 3 | 4 | # 克隆项目 5 | git clone https://github.com/zhangwenwu/vue-element-admin-g6-editor.git 6 | 7 | # 进入项目目录 8 | cd vue-element-admin-g6-editor 9 | 10 | # 安装依赖 11 | npm install 12 | 13 | # 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 14 | npm install --registry=https://registry.npm.taobao.org 15 | 16 | # 启动服务 17 | npm run dev 18 | ``` 19 | 20 | 浏览器访问 [http://localhost:8888](http://localhost:8888) 21 | 22 | ## 发布 23 | 24 | ```bash 25 | # 构建测试环境 26 | npm run build:stage 27 | 28 | # 构建生产环境 29 | npm run build:prod -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 资源统筹系统
-------------------------------------------------------------------------------- /dist/static/css/chunk-4028b836.64c5c8b4.css: -------------------------------------------------------------------------------- 1 | .pagination-container[data-v-6af373ef]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-6af373ef]{display:none}.createPost-main-container{padding:40px 45px 20px 50px}.balance .el-table .cell{white-space:nowrap!important} -------------------------------------------------------------------------------- /dist/static/css/chunk-768ebd2f.a1c9ad1b.css: -------------------------------------------------------------------------------- 1 | #vue-g6-editor .database{overflow:hidden}#vue-g6-editor .el-menu{background-color:#fbfbfb}#vue-g6-editor .el-submenu__title{height:35px;line-height:35px}#vue-g6-editor header:first-of-type{background:#fbfbfb;line-height:40px;padding-left:20px;border-bottom:1px solid #dadce0;-webkit-box-sizing:border-box;box-sizing:border-box}#vue-g6-editor #toolbar{background:#fbfbfb;border-bottom:1px solid #dadce0;padding:4px 14px}#vue-g6-editor #toolbar i{font-size:15px;padding:4px;margin-right:8px;color:#333}#vue-g6-editor #toolbar i:hover{cursor:pointer;background-color:#eee;color:#5cb6ff}#vue-g6-editor #itempannel{-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#f7f9fb;border-right:1px solid #dadce0;height:calc(100vh - 78px);padding-top:10px;overflow:hidden;display:inherit;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:distribute;justify-content:space-around;-ms-flex-line-pack:start;align-content:flex-start}#vue-g6-editor #itempannel .getItem{color:rgba(0,0,0,.65);border-radius:4px;height:28px;line-height:26px;padding-left:8px;list-style-type:none;font-size:12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#vue-g6-editor #page{height:calc(71vh - 78px)}#vue-g6-editor #page canvas{display:block;width:100%}#vue-g6-editor .right-part{height:calc(100vh - 78px);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}#vue-g6-editor #detailpannel{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;background-color:#fbfbfb;border-left:1px solid #dadce0;overflow-y:scroll}#vue-g6-editor #detailpannel #canvasAttributeBar .title,#vue-g6-editor #detailpannel #edgeAttributeBar .title,#vue-g6-editor #detailpannel #groupAttributeBar .title,#vue-g6-editor #detailpannel #multiAttributeBar .title,#vue-g6-editor #detailpannel #nodeAttributeBar .title{height:34px;line-height:34px;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;font-weight:700;font-size:13px;border-width:0 0 1px 0;border-style:solid;border-color:#dadce0}#vue-g6-editor #detailpannel #canvasAttributeBar .main,#vue-g6-editor #detailpannel #edgeAttributeBar .main,#vue-g6-editor #detailpannel #groupAttributeBar .main,#vue-g6-editor #detailpannel #multiAttributeBar .main,#vue-g6-editor #detailpannel #nodeAttributeBar .main{padding:10px}#vue-g6-editor #minimap{background-color:#fbfbfb;border-top:1px solid #ccc;border-left:1px solid #ccc}#vue-g6-editor #minimap .title{height:34px;line-height:34px;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;font-weight:700;font-size:13px;border-width:0 0 1px 0;border-style:solid;border-color:#dadce0}#vue-g6-editor #contextmenu{display:none}#vue-g6-editor #contextmenu .menu .el-button{width:100%;display:block;margin-left:0;border-radius:0!important;border-bottom:none}#vue-g6-editor #contextmenu .menu .el-button:last-of-type{border-bottom:1px solid #dcdfe6}#vue-g6-editor .save-as-image-dialog .el-select{display:block}#vue-g6-editor .el-pagination{text-align:center}#vue-g6-editor .el-table--medium td,#vue-g6-editor .el-table--medium th{padding:5px 0}#vue-g6-editor .el-table{height:calc(40vh - 78px)!important}.itempannel ul{padding:0;padding-left:16px;border-bottom:1px solid #ccc;padding-bottom:20px}.itempannel li{color:rgba(0,0,0,.65);border-radius:4px;height:28px;line-height:26px;padding-left:8px;border:1px solid transparent;list-style-type:none;font-size:12px}.itempannel li,.itempannel li:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.itempannel li:hover{width:160px}.itempanel li:hover,.itempannel li:hover{background:#fff;border:1px solid #ced4d9;cursor:move}.itempannel .icon{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-right:8px;font-size:14px;background:url(https://gw.alipayobjects.com/zos/rmsportal/czNEJAmyDpclFaSucYWB.svg)}.itempannel h6{font-size:15px;text-align:center;padding:0;margin:0;margin-bottom:14px}.itempannel i{margin-right:10px;margin-left:-10px} -------------------------------------------------------------------------------- /dist/static/css/chunk-cbb61e6a.6e6cbd15.css: -------------------------------------------------------------------------------- 1 | .knowledge[data-v-74c84609]{padding-top:50px}.knowledge h3[data-v-74c84609]{text-align:center}.knowledge h3 span[data-v-74c84609]{font-size:20px}.knowledge .el-row[data-v-74c84609]{margin-bottom:20px}.knowledge .el-button--primary[data-v-74c84609]{background-color:#0d5391;border-color:#0d5391} -------------------------------------------------------------------------------- /dist/static/css/chunk-d7a7d948.5719ffb1.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-682337c0]{margin:30px}.dashboard-text[data-v-682337c0]{font-size:30px;line-height:46px} -------------------------------------------------------------------------------- /dist/static/css/chunk-f1a6febe.2f3c92ef.css: -------------------------------------------------------------------------------- 1 | .knowledgeDetails[data-v-2636a9b4]{padding-top:10px}.knowledgeDetails .fa-fw[data-v-2636a9b4]{margin-right:5px}.knowledgeDetails .grid-content[data-v-2636a9b4]{height:calc(100vh - 80px)}.knowledgeDetails .grid-content .article[data-v-2636a9b4]{text-indent:30px;line-height:25px;font-size:14px}.knowledgeDetails .grid-content .el-card[data-v-2636a9b4]{height:100%}.knowledgeDetails .grid-content .el-tree[data-v-2636a9b4],.knowledgeDetails .grid-content .text[data-v-2636a9b4]{font-size:14px}.knowledgeDetails .grid-content .item[data-v-2636a9b4]{margin-bottom:18px}.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:after,.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:before{display:table;content:""}.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:after{clear:both}.knowledgeDetails .lists li[data-v-2636a9b4]{list-style:none;margin-bottom:10px}.knowledgeDetails h3[data-v-2636a9b4]{text-align:center}.knowledgeDetails h3 span[data-v-2636a9b4]{font-size:20px}.knowledgeDetails .el-row[data-v-2636a9b4]{margin-bottom:20px} -------------------------------------------------------------------------------- /dist/static/css/chunk-f8cee08e.305e1e16.css: -------------------------------------------------------------------------------- 1 | @supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-9ea6a6a6]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-9ea6a6a6]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-9ea6a6a6]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-9ea6a6a6]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-9ea6a6a6]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-9ea6a6a6]{position:relative}.login-container .title-container .title[data-v-9ea6a6a6]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-9ea6a6a6]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} -------------------------------------------------------------------------------- /dist/static/fonts/element-icons.2fad952a.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/element-icons.2fad952a.woff -------------------------------------------------------------------------------- /dist/static/fonts/element-icons.6f0a7632.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/element-icons.6f0a7632.ttf -------------------------------------------------------------------------------- /dist/static/fonts/fontawesome-webfont.674f50d2.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/fontawesome-webfont.674f50d2.eot -------------------------------------------------------------------------------- /dist/static/fonts/fontawesome-webfont.af7ae505.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/fontawesome-webfont.af7ae505.woff2 -------------------------------------------------------------------------------- /dist/static/fonts/fontawesome-webfont.b06871f2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/fontawesome-webfont.b06871f2.ttf -------------------------------------------------------------------------------- /dist/static/fonts/fontawesome-webfont.fee66e71.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/fonts/fontawesome-webfont.fee66e71.woff -------------------------------------------------------------------------------- /dist/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/img/favicon.ico -------------------------------------------------------------------------------- /dist/static/img/logo.d5e2a9f1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/dist/static/img/logo.d5e2a9f1.png -------------------------------------------------------------------------------- /dist/static/js/chunk-2d230fe7.1db2b25c.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230fe7"],{ef3c:function(e,r,n){"use strict";n.r(r);n("a481");var t,u,a={created:function(){var e=this.$route,r=e.params,n=e.query,t=r.path;this.$router.replace({path:"/"+t,query:n})},render:function(e){return e()}},c=a,o=n("2877"),p=Object(o["a"])(c,t,u,!1,null,null,null);r["default"]=p.exports}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-cbb61e6a.921b9935.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-cbb61e6a"],{"25fa":function(t,e,a){"use strict";var i=a("d005"),s=a.n(i);s.a},"3c34":function(t,e,a){"use strict";a.r(e);var i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"knowledge"},[a("el-row",[a("el-col",{attrs:{span:24}},[a("div",{staticClass:"grid-content bg-purple-dark"},[a("h3",{staticClass:"title"},[t._v("数据"),a("span",[t._v("·")]),t._v("知识"),a("span",[t._v("·")]),t._v("智能")])])])],1),t._v(" "),a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:12,offset:6}},[a("div",{staticClass:"grid-content bg-purple"},[a("div",[a("el-input",{staticClass:"input-with-select",attrs:{placeholder:"请输入内容"},model:{value:t.search,callback:function(e){t.search=e},expression:"search"}},[a("el-button",{attrs:{slot:"append",icon:"el-icon-search"},on:{click:t.enterSearch},slot:"append"})],1)],1)])])],1),t._v(" "),a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:12,offset:6}},[a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("法律")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("法规")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("审计")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("工商")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("公司")])],1)],1),t._v(" "),a("el-dialog",{attrs:{visible:t.dialogPvVisible,title:"title"},on:{"update:visible":function(e){t.dialogPvVisible=e}}},[a("el-table",{staticStyle:{width:"100%"},attrs:{data:t.pvData,border:"",fit:"","highlight-current-row":""}},[a("el-table-column",{attrs:{prop:"key",label:"Channel"}}),t._v(" "),a("el-table-column",{attrs:{prop:"pv",label:"Pv"}})],1),t._v(" "),a("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary"},on:{click:function(e){t.dialogPvVisible=!1}}},[t._v("取消")])],1)],1)],1)},s=[],l={data:function(){return{pvData:"",search:""}},created:function(){this.dialogPvVisible=!1},methods:{enterSearch:function(){this.$router.push({path:"/documentation/details/1"})}}},r=l,n=(a("25fa"),a("2877")),o=Object(n["a"])(r,i,s,!1,null,"74c84609",null);e["default"]=o.exports},d005:function(t,e,a){}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-d7a7d948.57e2af3f.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-d7a7d948"],{9406:function(t,a,s){"use strict";s.r(a);var n=function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"dashboard-container"},[s("div",{staticClass:"dashboard-text"},[t._v("用户名: "+t._s(t.name))]),t._v(" "),s("div",{staticClass:"dashboard-text"},[t._v("权限: "),t._l(t.roles,(function(a){return s("span",{key:a},[t._v(t._s(a))])}))],2)])},e=[],c=s("5530"),r=s("2f62"),d={name:"Dashboard",computed:Object(c["a"])({},Object(r["b"])(["name","roles"]))},o=d,i=(s("d117"),s("2877")),u=Object(i["a"])(o,n,e,!1,null,"682337c0",null);a["default"]=u.exports},d117:function(t,a,s){"use strict";var n=s("f250"),e=s.n(n);e.a},f250:function(t,a,s){}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-f1a6febe.52c245c4.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-f1a6febe"],{bd9d:function(t,a,e){"use strict";var l=e("cbfa"),s=e.n(l);s.a},cbfa:function(t,a,e){},dc0f:function(t,a,e){"use strict";e.r(a);var l=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"knowledgeDetails"},[e("el-row",[e("el-col",{attrs:{span:4}},[e("div",{staticClass:"grid-content bg-purple-dark"},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("------")])]),t._v(" "),e("el-tree",{attrs:{data:t.data,props:t.defaultProps,"default-expand-all":"true"},on:{"node-click":t.handleNodeClick},scopedSlots:t._u([{key:"default",fn:function(a){var l=a.node;return e("span",{staticClass:"custom-tree-node"},[e("span",[e("i",{staticClass:"fa fa-file-o fa-fw"}),t._v(t._s(l.label)+"\n ")])])}}])})],1)],1)]),t._v(" "),e("el-col",{attrs:{span:4}},[e("div",{staticClass:"grid-content bg-purple-dark",staticStyle:{"border-left":"0","border-right":"0"}},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("关键字")])]),t._v(" "),t._l(4,(function(a){return e("div",{key:a,staticClass:"text item"},[t._v("\n "+t._s("列表内容 "+a)+"\n ")])}))],2)],1)]),t._v(" "),e("el-col",{attrs:{span:16}},[e("div",{staticClass:"grid-content bg-purple-dark"},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("文章标题")])]),t._v(" "),e("div",{staticClass:"article"},[t._v("\n 中赢智数(广东)科技有限公司成立于2015年,以为政府及国有企业进行数据治理及大数据分析、数据咨询服务为核心业务及发展目标,为审计机关、检察院、纪委、财政、政府办公厅、工商等政府部门及国资委企业客户提供数据治理及数据分析产品及服务整体解决方案.公司成立产品研发中心、数据分析服务中心、实施交付中心等技术部门。用精湛的技术服务于广大客户并不断累积丰富的经验,为审计机关、教育从审计产品开发、实施及数据服务提供全方位的解决方案。\n "),e("br"),t._v("中赢智数(广东)科技有限公司成立于2015年,以为政府及国有企业进行数据治理及大数据分析、数据咨询服务为核心业务及发展目标,为审计机关、检察院、纪委、财政、政府办公厅、工商等政府部门及国资委企业客户提供数据治理及数据分析产品及服务整体解决方案。\n ")])])],1)])],1),t._v(" "),e("el-dialog",{attrs:{visible:t.dialogPvVisible,title:"title"},on:{"update:visible":function(a){t.dialogPvVisible=a}}},[e("el-table",{staticStyle:{width:"100%"},attrs:{data:t.pvData,border:"",fit:"","highlight-current-row":""}},[e("el-table-column",{attrs:{prop:"key",label:"Channel"}}),t._v(" "),e("el-table-column",{attrs:{prop:"pv",label:"Pv"}})],1),t._v(" "),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{attrs:{type:"primary"},on:{click:function(a){t.dialogPvVisible=!1}}},[t._v("取消")])],1)],1)],1)},s=[],i={data:function(){return{pvData:"",search:"",data:[{label:"法规库",children:[{label:"审计法规"}]},{label:"案例库",children:[{label:"审计案例"},{label:"审计案例3"}]},{label:"刑法库",children:[{label:"审计案例"},{label:"审计案例"}]}],defaultProps:{children:"children",label:"label"}}},created:function(){this.dialogPvVisible=!1},methods:{enterSearch:function(){this.$router.push({path:"/documentation/details/1"})},handleNodeClick:function(t){console.log(t)}}},r=i,n=(e("bd9d"),e("2877")),o=Object(n["a"])(r,l,s,!1,null,"2636a9b4",null);a["default"]=o.exports}}]); -------------------------------------------------------------------------------- /dist/static/js/chunk-f8cee08e.78f2e4d8.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-f8cee08e"],{1469:function(e,t,s){},2017:function(e,t,s){"use strict";var n=s("b12d"),o=s.n(n);o.a},"9ed6":function(e,t,s){"use strict";s.r(t);var n=function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"login-container"},[s("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on","label-position":"left"}},[s("div",{staticClass:"title-container"},[s("h3",{staticClass:"title"},[e._v("系统登录")])]),e._v(" "),s("el-form-item",{attrs:{prop:"username"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"user"}})],1),e._v(" "),s("el-input",{ref:"username",attrs:{placeholder:"用户名",name:"username",type:"text",tabindex:"1","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),e._v(" "),s("el-form-item",{attrs:{prop:"password"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"password"}})],1),e._v(" "),s("el-input",{key:e.passwordType,ref:"password",attrs:{type:e.passwordType,placeholder:"密码",name:"password",tabindex:"2","auto-complete":"on"},nativeOn:{keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.handleLogin(t)}},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}}),e._v(" "),s("span",{staticClass:"show-pwd",on:{click:e.showPwd}},[s("svg-icon",{attrs:{"icon-class":"password"===e.passwordType?"eye":"eye-open"}})],1)],1),e._v(" "),s("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:e.loading,type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),e.handleLogin(t)}}},[e._v("登录")]),e._v(" "),s("div",{staticStyle:{position:"relative"}},[s("div",{staticClass:"tips"},[s("span",[e._v("用户名 : admin")]),e._v(" "),s("span",[e._v("密码 : admin")])])])],1)],1)},o=[],r={name:"Login",data:function(){var e=function(e,t,s){""===t?s(new Error("请输入用户名")):s()},t=function(e,t,s){t.length<4?s(new Error("密码不能少于4位")):s()};return{loginForm:{username:"admin",password:"admin"},loginRules:{username:[{required:!0,trigger:"blur",validator:e}],password:[{required:!0,trigger:"blur",validator:t}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:{showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){e.$refs.password.focus()}))},handleLogin:function(){var e=this;this.$store.dispatch("user/logout"),this.$refs.loginForm.validate((function(t){if(!t)return console.log("error submit!!"),!1;console.log(e.loginForm),e.loading=!0,e.$store.dispatch("user/login",e.loginForm).then((function(){e.$router.push({path:e.redirect||"/",query:e.otherQuery}),e.loading=!1})).catch((function(){e.loading=!1}))}))}}},a=r,i=(s("2017"),s("b5f7"),s("2877")),l=Object(i["a"])(a,n,o,!1,null,"9ea6a6a6",null);t["default"]=l.exports},b12d:function(e,t,s){},b5f7:function(e,t,s){"use strict";var n=s("1469"),o=s.n(n);o.a}}]); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } 10 | -------------------------------------------------------------------------------- /mock/index.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { param2Obj } from '../src/utils' 3 | 4 | import user from './user' 5 | import table from './table' 6 | import article from './article' 7 | import finance from './finance' 8 | 9 | const mocks = [ 10 | ...user, 11 | ...table, 12 | ...article, 13 | ...finance 14 | ] 15 | 16 | // for front mock 17 | // please use it cautiously, it will redefine XMLHttpRequest, 18 | // which will cause many of your third-party libraries to be invalidated(like progress event). 19 | export function mockXHR() { 20 | // mock patch 21 | // https://github.com/nuysoft/Mock/issues/300 22 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 23 | Mock.XHR.prototype.send = function() { 24 | if (this.custom.xhr) { 25 | this.custom.xhr.withCredentials = this.withCredentials || false 26 | 27 | if (this.responseType) { 28 | this.custom.xhr.responseType = this.responseType 29 | } 30 | } 31 | this.proxy_send(...arguments) 32 | } 33 | 34 | function XHR2ExpressReqWrap(respond) { 35 | return function(options) { 36 | let result = null 37 | if (respond instanceof Function) { 38 | const { body, type, url } = options 39 | // https://expressjs.com/en/4x/api.html#req 40 | result = respond({ 41 | method: type, 42 | body: JSON.parse(body), 43 | query: param2Obj(url) 44 | }) 45 | } else { 46 | result = respond 47 | } 48 | return Mock.mock(result) 49 | } 50 | } 51 | 52 | for (const i of mocks) { 53 | Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) 54 | } 55 | } 56 | 57 | // for mock server 58 | const responseFake = (url, type, respond) => { 59 | return { 60 | url: new RegExp(`/mock${url}`), 61 | type: type || 'get', 62 | response(req, res) { 63 | res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) 64 | } 65 | } 66 | } 67 | 68 | export default mocks.map(route => { 69 | return responseFake(route.url, route.type, route.response) 70 | }) 71 | -------------------------------------------------------------------------------- /mock/mock-server.js: -------------------------------------------------------------------------------- 1 | const chokidar = require('chokidar') 2 | const bodyParser = require('body-parser') 3 | const chalk = require('chalk') 4 | const path = require('path') 5 | 6 | const mockDir = path.join(process.cwd(), 'mock') 7 | 8 | function registerRoutes(app) { 9 | let mockLastIndex 10 | const { default: mocks } = require('./index.js') 11 | for (const mock of mocks) { 12 | app[mock.type](mock.url, mock.response) 13 | mockLastIndex = app._router.stack.length 14 | } 15 | const mockRoutesLength = Object.keys(mocks).length 16 | return { 17 | mockRoutesLength: mockRoutesLength, 18 | mockStartIndex: mockLastIndex - mockRoutesLength 19 | } 20 | } 21 | 22 | function unregisterRoutes() { 23 | Object.keys(require.cache).forEach(i => { 24 | if (i.includes(mockDir)) { 25 | delete require.cache[require.resolve(i)] 26 | } 27 | }) 28 | } 29 | 30 | module.exports = app => { 31 | // es6 polyfill 32 | require('@babel/register') 33 | 34 | // parse app.body 35 | // https://expressjs.com/en/4x/api.html#req.body 36 | app.use(bodyParser.json()) 37 | app.use(bodyParser.urlencoded({ 38 | extended: true 39 | })) 40 | 41 | const mockRoutes = registerRoutes(app) 42 | var mockRoutesLength = mockRoutes.mockRoutesLength 43 | var mockStartIndex = mockRoutes.mockStartIndex 44 | 45 | // watch files, hot reload mock server 46 | chokidar.watch(mockDir, { 47 | ignored: /mock-server/, 48 | ignoreInitial: true 49 | }).on('all', (event, path) => { 50 | if (event === 'change' || event === 'add') { 51 | try { 52 | // remove mock routes stack 53 | app._router.stack.splice(mockStartIndex, mockRoutesLength) 54 | 55 | // clear routes cache 56 | unregisterRoutes() 57 | 58 | const mockRoutes = registerRoutes(app) 59 | mockRoutesLength = mockRoutes.mockRoutesLength 60 | mockStartIndex = mockRoutes.mockStartIndex 61 | 62 | console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)) 63 | } catch (error) { 64 | console.log(chalk.redBright(error)) 65 | } 66 | } 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /mock/table.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | 3 | const data = Mock.mock({ 4 | 'items|30': [{ 5 | id: '@id', 6 | title: '@sentence(10, 20)', 7 | 'status|1': ['published', 'draft', 'deleted'], 8 | author: 'name', 9 | display_time: '@datetime', 10 | pageviews: '@integer(300, 5000)' 11 | }] 12 | }) 13 | 14 | export default [ 15 | { 16 | url: '/table/list', 17 | type: 'get', 18 | response: config => { 19 | const items = data.items 20 | return { 21 | code: 20000, 22 | data: { 23 | total: items.length, 24 | items: items 25 | } 26 | } 27 | } 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /mock/user.js: -------------------------------------------------------------------------------- 1 | 2 | const tokens = { 3 | admin: { 4 | token: 'admin-token' 5 | }, 6 | editor: { 7 | token: 'editor-token' 8 | } 9 | } 10 | 11 | const users = { 12 | 'admin-token': { 13 | roles: ['admin'], 14 | introduction: 'I am a super administrator', 15 | avatar: '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: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 22 | name: 'Normal Editor' 23 | } 24 | } 25 | 26 | export default [ 27 | // user login 28 | { 29 | url: '/jwt_auth/', 30 | type: 'post', 31 | response: config => { 32 | const { username } = config.body 33 | const token = tokens[username] 34 | 35 | // mock error 36 | if (!token) { 37 | return { 38 | code: 60204, 39 | message: 'Account and password are incorrect.' 40 | } 41 | } 42 | 43 | return { 44 | code: 20000, 45 | data: token 46 | } 47 | } 48 | }, 49 | 50 | // get user info 51 | { 52 | url: '/user/info\.*', 53 | type: 'get', 54 | response: config => { 55 | const { token } = config.query 56 | const info = users[token] 57 | 58 | // mock error 59 | if (!info) { 60 | return { 61 | code: 50008, 62 | message: 'Login failed, unable to get user details.' 63 | } 64 | } 65 | 66 | return { 67 | code: 20000, 68 | data: info 69 | } 70 | } 71 | }, 72 | 73 | // user logout 74 | { 75 | url: '/user/logout', 76 | type: 'post', 77 | response: _ => { 78 | return { 79 | code: 20000, 80 | data: 'success' 81 | } 82 | } 83 | } 84 | ] 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-permission-basetemplate", 3 | "version": "4.2.1", 4 | "description": "A vue admin template with Element UI & axios & iconfont & permission control & lint", 5 | "author": "Pan ", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "vue-cli-service serve", 9 | "build:prod": "vue-cli-service build", 10 | "build:stage": "vue-cli-service build --mode staging", 11 | "preview": "node build/index.js --preview", 12 | "lint": "eslint --ext .js,.vue src", 13 | "test:unit": "jest --clearCache && vue-cli-service test:unit", 14 | "test:ci": "npm run lint && npm run test:unit", 15 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" 16 | }, 17 | "dependencies": { 18 | "@antv/g6-editor": "^1.2.0", 19 | "axios": "0.18.1", 20 | "element-ui": "2.7.2", 21 | "font-awesome": "^4.7.0", 22 | "js-cookie": "2.2.0", 23 | "netflix-conductor-json-tree": "^0.0.4", 24 | "normalize.css": "7.0.0", 25 | "nprogress": "0.2.0", 26 | "path-to-regexp": "2.4.0", 27 | "screenfull": "^4.2.0", 28 | "vue": "2.6.10", 29 | "vue-router": "3.0.6", 30 | "vuex": "3.1.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "7.0.0", 34 | "@babel/register": "7.0.0", 35 | "@vue/cli-plugin-babel": "3.6.0", 36 | "@vue/cli-plugin-eslint": "^3.9.1", 37 | "@vue/cli-plugin-unit-jest": "3.6.3", 38 | "@vue/cli-service": "3.6.0", 39 | "@vue/test-utils": "1.0.0-beta.29", 40 | "autoprefixer": "^9.5.1", 41 | "babel-core": "7.0.0-bridge.0", 42 | "babel-eslint": "10.0.1", 43 | "babel-jest": "23.6.0", 44 | "chalk": "2.4.2", 45 | "connect": "3.6.6", 46 | "eslint": "5.15.3", 47 | "eslint-plugin-vue": "5.2.2", 48 | "html-webpack-plugin": "3.2.0", 49 | "mockjs": "1.0.1-beta3", 50 | "node-sass": "^4.9.0", 51 | "runjs": "^4.3.2", 52 | "sass-loader": "^7.1.0", 53 | "script-ext-html-webpack-plugin": "2.1.3", 54 | "script-loader": "0.7.2", 55 | "serve-static": "^1.13.2", 56 | "svg-sprite-loader": "4.1.3", 57 | "svgo": "1.2.2", 58 | "vue-template-compiler": "2.6.10" 59 | }, 60 | "engines": { 61 | "node": ">=8.9", 62 | "npm": ">= 3.0.0" 63 | }, 64 | "browserslist": [ 65 | "> 1%", 66 | "last 2 versions" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | 'plugins': { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | 'autoprefixer': {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= webpackConfig.name %> 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/public/static/img/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/api/article.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function fetchList(query) { 4 | return request({ 5 | url: '/vue-element-admin/article/list', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function fetchArticle(id) { 12 | return request({ 13 | url: '/vue-element-admin/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 | 43 | export function getDzzb(data) { 44 | return request({ 45 | url: '/finance/dzzb/', 46 | method: 'get', 47 | data 48 | }) 49 | } 50 | 51 | export function getKmbm(data) { 52 | return request({ 53 | url: '/finance/kjkm/', 54 | method: 'post', 55 | data 56 | }) 57 | } 58 | 59 | export function getTables(data) { 60 | return request({ 61 | url: '/finance/kmyeb/', 62 | method: 'post', 63 | data 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /src/api/table.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getList(params) { 4 | return request({ 5 | url: '/table/list', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function login(data) { 4 | return request({ 5 | url: '/jwt_auth/', 6 | // url: '/user/login/', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | export function getInfo(token) { 13 | return request({ 14 | url: '/user/info/', 15 | method: 'get', 16 | params: { token } 17 | }) 18 | } 19 | 20 | export function logout() { 21 | return request({ 22 | url: '/user/logout/', 23 | method: 'post' 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 65 | 66 | 79 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /src/components/Pagination/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 92 | 93 | 102 | -------------------------------------------------------------------------------- /src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 61 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 47 | 48 | 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /src/directive/waves/waves.js: -------------------------------------------------------------------------------- 1 | import './waves.css' 2 | 3 | const context = '@@wavesContext' 4 | 5 | function handleClick(el, binding) { 6 | function handle(e) { 7 | const customOpts = Object.assign({}, binding.value) 8 | const opts = Object.assign({ 9 | ele: el, // 波纹作用元素 10 | type: 'hit', // hit 点击位置扩散 center中心点扩展 11 | color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 12 | }, 13 | customOpts 14 | ) 15 | const target = opts.ele 16 | if (target) { 17 | target.style.position = 'relative' 18 | target.style.overflow = 'hidden' 19 | const rect = target.getBoundingClientRect() 20 | let ripple = target.querySelector('.waves-ripple') 21 | if (!ripple) { 22 | ripple = document.createElement('span') 23 | ripple.className = 'waves-ripple' 24 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' 25 | target.appendChild(ripple) 26 | } else { 27 | ripple.className = 'waves-ripple' 28 | } 29 | switch (opts.type) { 30 | case 'center': 31 | ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px' 32 | ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px' 33 | break 34 | default: 35 | ripple.style.top = 36 | (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || 37 | document.body.scrollTop) + 'px' 38 | ripple.style.left = 39 | (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || 40 | document.body.scrollLeft) + 'px' 41 | } 42 | ripple.style.backgroundColor = opts.color 43 | ripple.className = 'waves-ripple z-active' 44 | return false 45 | } 46 | } 47 | 48 | if (!el[context]) { 49 | el[context] = { 50 | removeHandle: handle 51 | } 52 | } else { 53 | el[context].removeHandle = handle 54 | } 55 | 56 | return handle 57 | } 58 | 59 | export default { 60 | bind(el, binding) { 61 | el.addEventListener('click', handleClick(el, binding), false) 62 | }, 63 | update(el, binding) { 64 | el.removeEventListener('click', el[context].removeHandle, false) 65 | el.addEventListener('click', handleClick(el, binding), false) 66 | }, 67 | unbind(el) { 68 | el.removeEventListener('click', el[context].removeHandle, false) 69 | el[context] = null 70 | delete el[context] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/qq.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/shopping.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 48 | 49 | 57 | -------------------------------------------------------------------------------- /src/layout/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 62 | 63 | 141 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/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 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $subMenu = this.$refs.subMenu 15 | if ($subMenu) { 16 | const handleMouseleave = $subMenu.handleMouseleave 17 | $subMenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 37 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Logo.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | 34 | 83 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 96 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 55 | -------------------------------------------------------------------------------- /src/layout/components/TagsView/ScrollPane.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 69 | 70 | 86 | -------------------------------------------------------------------------------- /src/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as Sidebar } from './Sidebar' 3 | export { default as AppMain } from './AppMain' 4 | export { default as TagsView } from './TagsView' 5 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 54 | 55 | 99 | -------------------------------------------------------------------------------- /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 | } 46 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets 4 | 5 | import ElementUI from 'element-ui' 6 | import 'element-ui/lib/theme-chalk/index.css' 7 | import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n 8 | import '@/styles/index.scss' // global css 9 | // 字体图标 10 | import 'font-awesome/css/font-awesome.min.css' 11 | 12 | 13 | import App from './App' 14 | import store from './store' 15 | import router from './router' 16 | 17 | import '@/icons' // icon 18 | import '@/permission' // permission control 19 | 20 | /** 21 | * If you don't want to use mock-server 22 | * you want to use MockJs for mock api 23 | * you can execute: mockXHR() 24 | * 25 | * Currently MockJs will be used in the production environment, 26 | * please remove it before going online! ! ! 27 | */ 28 | console.log('~~9898989899665656') 29 | import { mockXHR } from '../mock' 30 | if (process.env.NODE_ENV === 'production') { 31 | mockXHR() 32 | } 33 | 34 | // set ElementUI lang to EN 35 | Vue.use(ElementUI, { locale }) 36 | 37 | Vue.config.productionTip = false 38 | 39 | new Vue({ 40 | el: '#app', 41 | router, 42 | store, 43 | render: h => h(App) 44 | }) 45 | -------------------------------------------------------------------------------- /src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import store from './store' 3 | import { Message } from 'element-ui' 4 | import NProgress from 'nprogress' // progress bar 5 | import 'nprogress/nprogress.css' // progress bar style 6 | import { getToken } from '@/utils/auth' // get token from cookie 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 | // start progress bar 15 | NProgress.start() 16 | 17 | // set page title 18 | document.title = getPageTitle(to.meta.title) 19 | 20 | // determine whether the user has logged in 21 | const hasToken = getToken() 22 | 23 | if (hasToken) { 24 | if (to.path === '/login') { 25 | // if is logged in, redirect to the home page 26 | next({ path: '/' }) 27 | NProgress.done() 28 | } else { 29 | // determine whether the user has obtained his permission roles through getInfo 30 | const hasRoles = store.getters.roles && store.getters.roles.length > 0 31 | if (hasRoles) { 32 | next() 33 | } else { 34 | try { 35 | // get user info 36 | // note: roles must be a object array! such as: ['admin'] or ,['developer','editor'] 37 | const { roles } = await store.dispatch('user/getInfo') 38 | 39 | // generate accessible routes map based on roles 40 | const accessRoutes = await store.dispatch('permission/generateRoutes', roles) 41 | 42 | // dynamically add accessible routes 43 | router.addRoutes(accessRoutes) 44 | 45 | // hack method to ensure that addRoutes is complete 46 | // set the replace: true, so the navigation will not leave a history record 47 | next({ ...to, replace: true }) 48 | } catch (error) { 49 | // remove token and go to login page to re-login 50 | await store.dispatch('user/resetToken') 51 | Message.error(error || 'Has Error') 52 | next(`/login?redirect=${to.path}`) 53 | NProgress.done() 54 | } 55 | } 56 | } 57 | } else { 58 | /* has no token*/ 59 | 60 | if (whiteList.indexOf(to.path) !== -1) { 61 | // in the free login whitelist, go directly 62 | next() 63 | } else { 64 | // other pages that do not have permission to access are redirected to the login page. 65 | next(`/login?redirect=${to.path}`) 66 | NProgress.done() 67 | } 68 | } 69 | }) 70 | 71 | router.afterEach(() => { 72 | // finish progress bar 73 | NProgress.done() 74 | }) 75 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | /* Layout */ 7 | import Layout from '@/layout' 8 | 9 | /** 10 | * Note: sub-menu only appear when route children.length >= 1 11 | * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html 12 | * 13 | * hidden: true if set true, item will not show in the sidebar(default is false) 14 | * alwaysShow: true if set true, will always show the root menu 15 | * if not set alwaysShow, when item has more than one children route, 16 | * it will becomes nested mode, otherwise not show the root menu 17 | * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb 18 | * name:'router-name' the name is used by (must set!!!) 19 | * meta : { 20 | roles: ['admin','editor'] control the page roles (you can set multiple roles) 21 | title: 'title' the name show in sidebar and breadcrumb (recommend set) 22 | icon: 'svg-name' the icon show in the sidebar 23 | noCache: true if set true, the page will no be cached(default is false) 24 | affix: true if set true, the tag will affix in the tags-view 25 | breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) 26 | activeMenu: '/example/list' if set path, the sidebar will highlight the path you set 27 | } 28 | */ 29 | 30 | /** 31 | * constantRoutes 32 | * a base page that does not have permission requirements 33 | * all roles can be accessed 34 | */ 35 | export const constantRoutes = [ 36 | { 37 | path: '/redirect', 38 | component: Layout, 39 | hidden: true, 40 | children: [ 41 | { 42 | path: '/redirect/:path*', 43 | component: () => import('@/views/redirect/index') 44 | } 45 | ] 46 | }, 47 | { 48 | path: '/login', 49 | component: () => import('@/views/login/index'), 50 | hidden: true 51 | }, 52 | { 53 | path: '/', 54 | component: Layout, 55 | redirect: '/dashboard', 56 | children: [ 57 | { 58 | path: 'dashboard', 59 | component: () => import('@/views/dashboard/index'), 60 | name: 'Dashboard', 61 | meta: { title: '首页', icon: 'user', affix: true } 62 | } 63 | ] 64 | }, 65 | { 66 | path: '/editor', 67 | component: Layout, 68 | children: [ 69 | { 70 | path: 'index', 71 | component: () => import('@/views/editor/index'), 72 | name: '可视化查询', 73 | meta: { title: '可视化查询', icon: 'dashboard', affix: false } 74 | } 75 | ] 76 | }, 77 | { 78 | path: '/financialQuery', 79 | component: Layout, 80 | name: 'financialQuery', 81 | redirect: '/financialQuery', 82 | meta: { 83 | title: '财务查询', 84 | icon: 'money' 85 | }, 86 | children: [ 87 | { 88 | path: 'balance', 89 | component: () => import('@/views/financialQuery/balance'), 90 | name: 'balance', 91 | meta: { title: '余额表查询', icon: 'documentation' } 92 | } 93 | ] 94 | }, 95 | { 96 | path: '/documentation', 97 | component: Layout, 98 | children: [ 99 | { 100 | path: 'index', 101 | component: () => import('@/views/documentation/index'), 102 | name: 'Documentation', 103 | meta: { title: '知识库', icon: 'education', affix: false } 104 | }, 105 | { 106 | path: 'details/:id', 107 | hidden: true, 108 | component: () => import('@/views/documentation/details'), 109 | name: '详细内容', 110 | meta: { title: '详细内容', icon: 'documentation', affix: false} 111 | } 112 | ] 113 | } 114 | ] 115 | 116 | /** 117 | * asyncRoutes 118 | * the routes that need to be dynamically loaded based on user roles 119 | */ 120 | const createRouter = () => new Router({ 121 | // mode: 'history', // require service support 122 | scrollBehavior: () => ({ y: 0 }), 123 | routes: constantRoutes 124 | }) 125 | 126 | const router = createRouter() 127 | 128 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 129 | export function resetRouter() { 130 | const newRouter = createRouter() 131 | router.matcher = newRouter.matcher // reset router 132 | } 133 | 134 | export default router 135 | -------------------------------------------------------------------------------- /src/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | title: '资源统筹系统', 4 | 5 | tagsView: true, 6 | 7 | /** 8 | * @type {boolean} true | false 9 | * @description Whether fix the header 10 | */ 11 | fixedHeader: false, 12 | 13 | /** 14 | * @type {boolean} true | false 15 | * @description Whether show the logo in sidebar 16 | */ 17 | sidebarLogo: true 18 | } 19 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | device: state => state.app.device, 4 | token: state => state.user.token, 5 | avatar: state => state.user.avatar, 6 | name: state => state.user.name, 7 | roles: state => state.user.roles, 8 | permission_routes: state => state.permission.routes, 9 | visitedViews: state => state.tagsView.visitedViews, 10 | cachedViews: state => state.tagsView.cachedViews 11 | } 12 | export default getters 13 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | import app from './modules/app' 5 | import permission from './modules/permission' 6 | import settings from './modules/settings' 7 | import user from './modules/user' 8 | import tagsView from './modules/tagsView' 9 | 10 | Vue.use(Vuex) 11 | 12 | const store = new Vuex.Store({ 13 | modules: { 14 | app, 15 | permission, 16 | settings, 17 | tagsView, 18 | user 19 | }, 20 | getters 21 | }) 22 | 23 | export default store 24 | -------------------------------------------------------------------------------- /src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const state = { 4 | sidebar: { 5 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, 6 | withoutAnimation: false 7 | }, 8 | device: 'desktop', 9 | size: Cookies.get('size') || 'medium' 10 | } 11 | 12 | const mutations = { 13 | TOGGLE_SIDEBAR: state => { 14 | state.sidebar.opened = !state.sidebar.opened 15 | state.sidebar.withoutAnimation = false 16 | if (state.sidebar.opened) { 17 | Cookies.set('sidebarStatus', 1) 18 | } else { 19 | Cookies.set('sidebarStatus', 0) 20 | } 21 | }, 22 | CLOSE_SIDEBAR: (state, withoutAnimation) => { 23 | Cookies.set('sidebarStatus', 0) 24 | state.sidebar.opened = false 25 | state.sidebar.withoutAnimation = withoutAnimation 26 | }, 27 | TOGGLE_DEVICE: (state, device) => { 28 | state.device = device 29 | } 30 | } 31 | 32 | const actions = { 33 | toggleSideBar({ commit }) { 34 | commit('TOGGLE_SIDEBAR') 35 | }, 36 | closeSideBar({ commit }, { withoutAnimation }) { 37 | commit('CLOSE_SIDEBAR', withoutAnimation) 38 | }, 39 | toggleDevice({ commit }, device) { 40 | commit('TOGGLE_DEVICE', device) 41 | } 42 | } 43 | 44 | export default { 45 | namespaced: true, 46 | state, 47 | mutations, 48 | actions 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings 4 | 5 | const state = { 6 | showSettings: showSettings, 7 | fixedHeader: fixedHeader, 8 | sidebarLogo: sidebarLogo, 9 | tagsView: tagsView 10 | } 11 | 12 | const mutations = { 13 | CHANGE_SETTING: (state, { key, value }) => { 14 | if (state.hasOwnProperty(key)) { 15 | state[key] = value 16 | } 17 | } 18 | } 19 | 20 | const actions = { 21 | changeSetting({ commit }, data) { 22 | commit('CHANGE_SETTING', data) 23 | } 24 | } 25 | 26 | export default { 27 | namespaced: true, 28 | state, 29 | mutations, 30 | actions 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/store/modules/tagsView.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | visitedViews: [], 3 | cachedViews: [] 4 | } 5 | 6 | const mutations = { 7 | ADD_VISITED_VIEW: (state, view) => { 8 | if (state.visitedViews.some(v => v.path === view.path)) return 9 | state.visitedViews.push( 10 | Object.assign({}, view, { 11 | title: view.meta.title || 'no-name' 12 | }) 13 | ) 14 | }, 15 | ADD_CACHED_VIEW: (state, view) => { 16 | if (state.cachedViews.includes(view.name)) return 17 | if (!view.meta.noCache) { 18 | state.cachedViews.push(view.name) 19 | } 20 | }, 21 | 22 | DEL_VISITED_VIEW: (state, view) => { 23 | for (const [i, v] of state.visitedViews.entries()) { 24 | if (v.path === view.path) { 25 | state.visitedViews.splice(i, 1) 26 | break 27 | } 28 | } 29 | }, 30 | DEL_CACHED_VIEW: (state, view) => { 31 | const index = state.cachedViews.indexOf(view.name) 32 | index > -1 && state.cachedViews.splice(index, 1) 33 | }, 34 | 35 | DEL_OTHERS_VISITED_VIEWS: (state, view) => { 36 | state.visitedViews = state.visitedViews.filter(v => { 37 | return v.meta.affix || v.path === view.path 38 | }) 39 | }, 40 | DEL_OTHERS_CACHED_VIEWS: (state, view) => { 41 | const index = state.cachedViews.indexOf(view.name) 42 | if (index > -1) { 43 | state.cachedViews = state.cachedViews.slice(index, index + 1) 44 | } else { 45 | // if index = -1, there is no cached tags 46 | state.cachedViews = [] 47 | } 48 | }, 49 | 50 | DEL_ALL_VISITED_VIEWS: state => { 51 | // keep affix tags 52 | const affixTags = state.visitedViews.filter(tag => tag.meta.affix) 53 | state.visitedViews = affixTags 54 | }, 55 | DEL_ALL_CACHED_VIEWS: state => { 56 | state.cachedViews = [] 57 | }, 58 | 59 | UPDATE_VISITED_VIEW: (state, view) => { 60 | for (let v of state.visitedViews) { 61 | if (v.path === view.path) { 62 | v = Object.assign(v, view) 63 | break 64 | } 65 | } 66 | } 67 | } 68 | 69 | const actions = { 70 | addView({ dispatch }, view) { 71 | dispatch('addVisitedView', view) 72 | dispatch('addCachedView', view) 73 | }, 74 | addVisitedView({ commit }, view) { 75 | commit('ADD_VISITED_VIEW', view) 76 | }, 77 | addCachedView({ commit }, view) { 78 | commit('ADD_CACHED_VIEW', view) 79 | }, 80 | 81 | delView({ dispatch, state }, view) { 82 | return new Promise(resolve => { 83 | dispatch('delVisitedView', view) 84 | dispatch('delCachedView', view) 85 | resolve({ 86 | visitedViews: [...state.visitedViews], 87 | cachedViews: [...state.cachedViews] 88 | }) 89 | }) 90 | }, 91 | delVisitedView({ commit, state }, view) { 92 | return new Promise(resolve => { 93 | commit('DEL_VISITED_VIEW', view) 94 | resolve([...state.visitedViews]) 95 | }) 96 | }, 97 | delCachedView({ commit, state }, view) { 98 | return new Promise(resolve => { 99 | commit('DEL_CACHED_VIEW', view) 100 | resolve([...state.cachedViews]) 101 | }) 102 | }, 103 | 104 | delOthersViews({ dispatch, state }, view) { 105 | return new Promise(resolve => { 106 | dispatch('delOthersVisitedViews', view) 107 | dispatch('delOthersCachedViews', view) 108 | resolve({ 109 | visitedViews: [...state.visitedViews], 110 | cachedViews: [...state.cachedViews] 111 | }) 112 | }) 113 | }, 114 | delOthersVisitedViews({ commit, state }, view) { 115 | return new Promise(resolve => { 116 | commit('DEL_OTHERS_VISITED_VIEWS', view) 117 | resolve([...state.visitedViews]) 118 | }) 119 | }, 120 | delOthersCachedViews({ commit, state }, view) { 121 | return new Promise(resolve => { 122 | commit('DEL_OTHERS_CACHED_VIEWS', view) 123 | resolve([...state.cachedViews]) 124 | }) 125 | }, 126 | 127 | delAllViews({ dispatch, state }, view) { 128 | return new Promise(resolve => { 129 | dispatch('delAllVisitedViews', view) 130 | dispatch('delAllCachedViews', view) 131 | resolve({ 132 | visitedViews: [...state.visitedViews], 133 | cachedViews: [...state.cachedViews] 134 | }) 135 | }) 136 | }, 137 | delAllVisitedViews({ commit, state }) { 138 | return new Promise(resolve => { 139 | commit('DEL_ALL_VISITED_VIEWS') 140 | resolve([...state.visitedViews]) 141 | }) 142 | }, 143 | delAllCachedViews({ commit, state }) { 144 | return new Promise(resolve => { 145 | commit('DEL_ALL_CACHED_VIEWS') 146 | resolve([...state.cachedViews]) 147 | }) 148 | }, 149 | 150 | updateVisitedView({ commit }, view) { 151 | commit('UPDATE_VISITED_VIEW', view) 152 | } 153 | } 154 | 155 | export default { 156 | namespaced: true, 157 | state, 158 | mutations, 159 | actions 160 | } 161 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import { login, logout, getInfo } from '@/api/user' 2 | import { getToken, setToken, removeToken } from '@/utils/auth' 3 | import router, { resetRouter } from '@/router' 4 | 5 | const state = { 6 | token: getToken(), 7 | name: '', 8 | avatar: require('../../assets/logo.png'), 9 | introduction: '', 10 | roles: [] 11 | } 12 | 13 | const mutations = { 14 | SET_TOKEN: (state, token) => { 15 | state.token = token 16 | }, 17 | SET_INTRODUCTION: (state, introduction) => { 18 | state.introduction = introduction 19 | }, 20 | SET_NAME: (state, name) => { 21 | state.name = name 22 | }, 23 | SET_AVATAR: (state, avatar) => { 24 | state.avatar = avatar 25 | }, 26 | SET_ROLES: (state, roles) => { 27 | state.roles = roles 28 | } 29 | } 30 | 31 | const actions = { 32 | // user login 33 | login({ commit }, userInfo) { 34 | const { username, password } = userInfo 35 | let a = { username: username.trim(), password: password }; 36 | console.log(a) 37 | console.log(typeof(a)) 38 | return new Promise((resolve, reject) => { 39 | login({ username: username.trim(), password: password }).then(response => { 40 | const { data } = response 41 | commit('SET_TOKEN', data.token) 42 | setToken(data.token) 43 | resolve() 44 | }).catch(error => { 45 | reject(error) 46 | }) 47 | }) 48 | }, 49 | 50 | // get user info 51 | getInfo({ commit, state }) { 52 | return new Promise((resolve, reject) => { 53 | getInfo(state.token).then(response => { 54 | const { data } = response 55 | if (!data) { 56 | reject('验证失败,请重新登录.') 57 | } 58 | const { roles, name, avatar, introduction } = data 59 | if (avatar !== '') { 60 | commit('SET_AVATAR', avatar) 61 | } 62 | // roles must be a non-empty array 63 | if (!roles || roles.length <= 0) { 64 | reject('角色不能为空!') 65 | } 66 | commit('SET_ROLES', roles) 67 | commit('SET_NAME', name) 68 | resolve(data) 69 | }).catch(error => { 70 | reject(error) 71 | }) 72 | }) 73 | }, 74 | 75 | // user logout 76 | logout({ commit, state, dispatch }) { 77 | return new Promise((resolve, reject) => { 78 | logout(state.token).then(() => { 79 | commit('SET_TOKEN', '') 80 | commit('SET_ROLES', []) 81 | removeToken() 82 | resetRouter() 83 | 84 | // reset visited views and cached views 85 | // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 86 | dispatch('tagsView/delAllViews', null, { root: true }) 87 | 88 | resolve() 89 | }).catch(error => { 90 | reject(error) 91 | }) 92 | }) 93 | }, 94 | 95 | // remove token 96 | resetToken({ commit }) { 97 | return new Promise(resolve => { 98 | commit('SET_TOKEN', '') 99 | commit('SET_ROLES', []) 100 | removeToken() 101 | resolve() 102 | }) 103 | }, 104 | 105 | // dynamically modify permissions 106 | changeRoles({ commit, dispatch }, role) { 107 | return new Promise(async resolve => { 108 | const token = role + '-token' 109 | 110 | commit('SET_TOKEN', token) 111 | setToken(token) 112 | 113 | const { roles } = await dispatch('getInfo') 114 | 115 | resetRouter() 116 | 117 | // generate accessible routes map based on roles 118 | const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) 119 | 120 | // dynamically add accessible routes 121 | router.addRoutes(accessRoutes) 122 | 123 | // reset visited views and cached views 124 | dispatch('tagsView/delAllViews', null, { root: true }) 125 | 126 | resolve() 127 | }) 128 | } 129 | } 130 | 131 | export default { 132 | namespaced: true, 133 | state, 134 | mutations, 135 | actions 136 | } 137 | -------------------------------------------------------------------------------- /src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | // cover some element-ui styles 2 | 3 | .el-breadcrumb__inner, 4 | .el-breadcrumb__inner a { 5 | font-weight: 400 !important; 6 | } 7 | 8 | .el-upload { 9 | input[type="file"] { 10 | display: none !important; 11 | } 12 | } 13 | 14 | .el-upload__input { 15 | display: none; 16 | } 17 | 18 | 19 | // to fixed https://github.com/ElemeFE/element/issues/2461 20 | .el-dialog { 21 | transform: none; 22 | left: 0; 23 | position: relative; 24 | margin: 0 auto; 25 | } 26 | 27 | // refine element ui upload 28 | .upload-container { 29 | .el-upload { 30 | width: 100%; 31 | 32 | .el-upload-dragger { 33 | width: 100%; 34 | height: 200px; 35 | } 36 | } 37 | } 38 | 39 | // dropdown 40 | .el-dropdown-menu { 41 | a { 42 | display: block 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | 2 | @import './variables.scss'; 3 | @import './mixin.scss'; 4 | @import './transition.scss'; 5 | @import './element-ui.scss'; 6 | @import './sidebar.scss'; 7 | 8 | body { 9 | height: 100%; 10 | -moz-osx-font-smoothing: grayscale; 11 | -webkit-font-smoothing: antialiased; 12 | text-rendering: optimizeLegibility; 13 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 14 | } 15 | 16 | label { 17 | font-weight: 700; 18 | } 19 | 20 | html { 21 | height: 100%; 22 | box-sizing: border-box; 23 | } 24 | 25 | #app { 26 | height: 100%; 27 | } 28 | 29 | *, 30 | *:before, 31 | *:after { 32 | box-sizing: inherit; 33 | } 34 | 35 | a:focus, 36 | a:active { 37 | outline: none; 38 | } 39 | 40 | a, 41 | a:focus, 42 | a:hover { 43 | cursor: pointer; 44 | color: inherit; 45 | text-decoration: none; 46 | } 47 | 48 | div:focus { 49 | outline: none; 50 | } 51 | 52 | .clearfix { 53 | &:after { 54 | visibility: hidden; 55 | display: block; 56 | font-size: 0; 57 | content: " "; 58 | clear: both; 59 | height: 0; 60 | } 61 | } 62 | 63 | // main-container global css 64 | .app-container { 65 | padding: 20px; 66 | } 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | .el-menu-item.is-active{ 3 | background-color: #999 !important; 4 | } 5 | .main-container { 6 | min-height: 100%; 7 | transition: margin-left .28s; 8 | margin-left: $sideBarWidth; 9 | position: relative; 10 | } 11 | 12 | .sidebar-container { 13 | transition: width 0.28s; 14 | width: $sideBarWidth !important; 15 | background-color: $menuBg; 16 | height: 100%; 17 | position: fixed; 18 | font-size: 0px; 19 | top: 0; 20 | bottom: 0; 21 | left: 0; 22 | z-index: 1001; 23 | overflow: hidden; 24 | 25 | // reset element-ui css 26 | .horizontal-collapse-transition { 27 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; 28 | } 29 | 30 | .scrollbar-wrapper { 31 | overflow-x: hidden !important; 32 | } 33 | 34 | .el-scrollbar__bar.is-vertical { 35 | right: 0px; 36 | } 37 | 38 | .el-scrollbar { 39 | height: 100%; 40 | } 41 | 42 | &.has-logo { 43 | .el-scrollbar { 44 | height: calc(100% - 60px); 45 | } 46 | } 47 | 48 | .is-horizontal { 49 | display: none; 50 | } 51 | 52 | a { 53 | display: inline-block; 54 | width: 100%; 55 | overflow: hidden; 56 | } 57 | 58 | .svg-icon { 59 | margin-right: 16px; 60 | } 61 | 62 | .el-menu { 63 | border: none; 64 | height: 100%; 65 | width: 100% !important; 66 | } 67 | 68 | // menu hover 69 | .submenu-title-noDropdown, 70 | .el-submenu__title { 71 | &:hover { 72 | background-color: $menuHover !important; 73 | } 74 | } 75 | 76 | .is-active>.el-submenu__title { 77 | color: $subMenuActiveText !important; 78 | } 79 | 80 | & .nest-menu .el-submenu>.el-submenu__title, 81 | & .el-submenu .el-menu-item { 82 | min-width: $sideBarWidth !important; 83 | background-color: $subMenuBg !important; 84 | 85 | &:hover { 86 | background-color: $subMenuHover !important; 87 | } 88 | } 89 | } 90 | 91 | .hideSidebar { 92 | .sidebar-container { 93 | width: 54px !important; 94 | } 95 | 96 | .main-container { 97 | margin-left: 54px; 98 | } 99 | 100 | .submenu-title-noDropdown { 101 | padding: 0 !important; 102 | position: relative; 103 | 104 | .el-tooltip { 105 | padding: 0 !important; 106 | 107 | .svg-icon { 108 | margin-left: 20px; 109 | } 110 | } 111 | } 112 | 113 | .el-submenu { 114 | overflow: hidden; 115 | 116 | &>.el-submenu__title { 117 | padding: 0 !important; 118 | 119 | .svg-icon { 120 | margin-left: 20px; 121 | } 122 | 123 | .el-submenu__icon-arrow { 124 | display: none; 125 | } 126 | } 127 | } 128 | 129 | .el-menu--collapse { 130 | .el-submenu { 131 | &>.el-submenu__title { 132 | &>span { 133 | height: 0; 134 | width: 0; 135 | overflow: hidden; 136 | visibility: hidden; 137 | display: inline-block; 138 | } 139 | } 140 | } 141 | } 142 | } 143 | 144 | .el-menu--collapse .el-menu .el-submenu { 145 | min-width: $sideBarWidth !important; 146 | } 147 | 148 | // mobile responsive 149 | .mobile { 150 | .main-container { 151 | margin-left: 0px; 152 | } 153 | 154 | .sidebar-container { 155 | transition: transform .28s; 156 | width: $sideBarWidth !important; 157 | } 158 | 159 | &.hideSidebar { 160 | .sidebar-container { 161 | pointer-events: none; 162 | transition-duration: 0.3s; 163 | transform: translate3d(-$sideBarWidth, 0, 0); 164 | } 165 | } 166 | } 167 | 168 | .withoutAnimation { 169 | 170 | .main-container, 171 | .sidebar-container { 172 | transition: none; 173 | } 174 | } 175 | } 176 | 177 | // when menu collapsed 178 | .el-menu--vertical { 179 | &>.el-menu { 180 | .svg-icon { 181 | margin-right: 16px; 182 | } 183 | } 184 | 185 | .nest-menu .el-submenu>.el-submenu__title, 186 | .el-menu-item { 187 | &:hover { 188 | // you can use $subMenuHover 189 | background-color: $menuHover !important; 190 | } 191 | } 192 | 193 | // the scroll bar appears when the subMenu is too long 194 | >.el-menu--popup { 195 | max-height: 100vh; 196 | overflow-y: auto; 197 | 198 | &::-webkit-scrollbar-track-piece { 199 | background: #d3dce6; 200 | } 201 | 202 | &::-webkit-scrollbar { 203 | width: 6px; 204 | } 205 | 206 | &::-webkit-scrollbar-thumb { 207 | background: #99a9bf; 208 | border-radius: 20px; 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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:#fff; 13 | $menuActiveText:#fff; 14 | $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 15 | 16 | $menuBg:#0d5391; 17 | $menuHover:#999; 18 | 19 | $subMenuBg:#0d5391; 20 | $subMenuHover:#999; 21 | $subMenuText: #333; 22 | 23 | $sideBarWidth: 210px; 24 | 25 | // the :export directive is the magic sauce for webpack 26 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 27 | :export { 28 | menuText: $menuText; 29 | menuActiveText: $menuActiveText; 30 | subMenuActiveText: $subMenuActiveText; 31 | menuBg: $menuBg; 32 | menuHover: $menuHover; 33 | subMenuBg: $subMenuBg; 34 | subMenuHover: $subMenuHover; 35 | sideBarWidth: $sideBarWidth; 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'vue_admin_template_token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken(token) { 10 | console.log(token) 11 | return Cookies.set(TokenKey, token, { expires: 360000 }) 12 | 13 | } 14 | 15 | export function removeToken() { 16 | console.log('删除') 17 | return Cookies.remove(TokenKey) 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Vue Admin Template' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by PanJiaChen on 16/11/18. 3 | */ 4 | 5 | /** 6 | * Parse the time to string 7 | * @param {(Object|string|number)} time 8 | * @param {string} cFormat 9 | * @returns {string | null} 10 | */ 11 | export function parseTime(time, cFormat) { 12 | if (arguments.length === 0) { 13 | return null 14 | } 15 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' 16 | let date 17 | if (typeof time === 'object') { 18 | date = time 19 | } else { 20 | if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { 21 | time = parseInt(time) 22 | } 23 | if ((typeof time === 'number') && (time.toString().length === 10)) { 24 | time = time * 1000 25 | } 26 | date = new Date(time) 27 | } 28 | const formatObj = { 29 | y: date.getFullYear(), 30 | m: date.getMonth() + 1, 31 | d: date.getDate(), 32 | h: date.getHours(), 33 | i: date.getMinutes(), 34 | s: date.getSeconds(), 35 | a: date.getDay() 36 | } 37 | const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => { 38 | const value = formatObj[key] 39 | // Note: getDay() returns 0 on Sunday 40 | if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } 41 | return value.toString().padStart(2, '0') 42 | }) 43 | return time_str 44 | } 45 | 46 | /** 47 | * @param {number} time 48 | * @param {string} option 49 | * @returns {string} 50 | */ 51 | export function formatTime(time, option) { 52 | if (('' + time).length === 10) { 53 | time = parseInt(time) * 1000 54 | } else { 55 | time = +time 56 | } 57 | const d = new Date(time) 58 | const now = Date.now() 59 | 60 | const diff = (now - d) / 1000 61 | 62 | if (diff < 30) { 63 | return '刚刚' 64 | } else if (diff < 3600) { 65 | // less 1 hour 66 | return Math.ceil(diff / 60) + '分钟前' 67 | } else if (diff < 3600 * 24) { 68 | return Math.ceil(diff / 3600) + '小时前' 69 | } else if (diff < 3600 * 24 * 2) { 70 | return '1天前' 71 | } 72 | if (option) { 73 | return parseTime(time, option) 74 | } else { 75 | return ( 76 | d.getMonth() + 77 | 1 + 78 | '月' + 79 | d.getDate() + 80 | '日' + 81 | d.getHours() + 82 | '时' + 83 | d.getMinutes() + 84 | '分' 85 | ) 86 | } 87 | } 88 | 89 | /** 90 | * @param {string} url 91 | * @returns {Object} 92 | */ 93 | export function param2Obj(url) { 94 | const search = url.split('?')[1] 95 | if (!search) { 96 | return {} 97 | } 98 | return JSON.parse( 99 | '{"' + 100 | decodeURIComponent(search) 101 | .replace(/"/g, '\\"') 102 | .replace(/&/g, '","') 103 | .replace(/=/g, '":"') 104 | .replace(/\+/g, ' ') + 105 | '"}' 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /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: 'http://192.168.1.193:8000', // url = base url + request url 9 | baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url 10 | // withCredentials: true, // send cookies when cross-domain requests 11 | timeout: 5000, // request timeout 12 | headers: { 13 | 'Content-Type': 'application/json' 14 | } 15 | }) 16 | 17 | // request interceptor 18 | service.interceptors.request.use( 19 | config => { 20 | // do something before request is sent 21 | 22 | if (store.getters.token) { 23 | // let each request carry token 24 | // ['X-Token'] is a custom headers key 25 | // please modify it according to the actual situation 26 | config.headers['Authorization'] = 'JWT ' + getToken() 27 | } 28 | return config 29 | }, 30 | error => { 31 | // do something with request error 32 | console.log(error) // for debug 33 | return Promise.reject(error) 34 | } 35 | ) 36 | 37 | // response interceptor 38 | service.interceptors.response.use( 39 | /** 40 | * If you want to get http information such as headers or status 41 | * Please return response => response 42 | */ 43 | 44 | /** 45 | * Determine the request status by custom code 46 | * Here is just an example 47 | * You can also judge the status by HTTP Status Code 48 | */ 49 | response => { 50 | const res = response.data 51 | 52 | // if the custom code is not 20000, it is judged as an error. 53 | if (res.code !== 20000) { 54 | Message({ 55 | message: res.message || 'Error', 56 | type: 'error', 57 | duration: 5 * 1000 58 | }) 59 | if (res.code === 50001) { 60 | Message({ 61 | message: res.message, 62 | type: 'error', 63 | duration: 5 * 1000 64 | }) 65 | } 66 | // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; 67 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) { 68 | // to re-login 69 | MessageBox.confirm('你已注销,可以取消以停留在此页,或重新登录', '已注销', { 70 | confirmButtonText: '重新登录', 71 | cancelButtonText: '取消', 72 | type: 'warning' 73 | }).then(() => { 74 | store.dispatch('user/resetToken').then(() => { 75 | location.reload() 76 | }) 77 | }) 78 | } 79 | return Promise.reject(new Error(res.message || 'Error')) 80 | } else { 81 | return res 82 | } 83 | }, 84 | error => { 85 | console.log('err' + error) // for debug 86 | Message({ 87 | message: error.message, 88 | type: 'error', 89 | duration: 5 * 1000 90 | }) 91 | return Promise.reject(error) 92 | } 93 | ) 94 | 95 | export default service 96 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by PanJiaChen on 16/11/18. 3 | */ 4 | 5 | /** 6 | * @param {string} path 7 | * @returns {Boolean} 8 | */ 9 | export function isExternal(path) { 10 | return /^(https?:|mailto:|tel:)/.test(path) 11 | } 12 | 13 | /** 14 | * @param {string} str 15 | * @returns {Boolean} 16 | */ 17 | export function validUsername(str) { 18 | const valid_map = ['admin', 'editor'] 19 | return valid_map.indexOf(str.trim()) >= 0 20 | } 21 | -------------------------------------------------------------------------------- /src/vendor/Export2Zip.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { saveAs } from 'file-saver' 3 | import JSZip from 'jszip' 4 | 5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) { 6 | const zip = new JSZip() 7 | const txt_name = txtName || 'file' 8 | const zip_name = zipName || 'file' 9 | const data = jsonData 10 | let txtData = `${th}\r\n` 11 | data.forEach((row) => { 12 | let tempStr = '' 13 | tempStr = row.toString() 14 | txtData += `${tempStr}\r\n` 15 | }) 16 | zip.file(`${txt_name}.txt`, txtData) 17 | zip.generateAsync({ 18 | type: "blob" 19 | }).then((blob) => { 20 | saveAs(blob, `${zip_name}.zip`) 21 | }, (err) => { 22 | alert('导出失败') 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | 22 | 33 | -------------------------------------------------------------------------------- /src/views/documentation/details.vue: -------------------------------------------------------------------------------- 1 | 57 | 103 | 156 | -------------------------------------------------------------------------------- /src/views/documentation/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 60 | 78 | -------------------------------------------------------------------------------- /src/views/editor/index.scss: -------------------------------------------------------------------------------- 1 | @backgroundColor: #fbfbfb; 2 | @borderColor: #dadce0; 3 | 4 | @itempannelAndPageBorder: 1px solid #ccc; 5 | @pageHeight: calc(100vh - 41px - 37px); 6 | 7 | body { 8 | margin: 0; 9 | overflow: hidden; 10 | } 11 | 12 | #vue-g6-editor { 13 | header:nth-of-type(1) { 14 | background: @backgroundColor; 15 | line-height: 40px; 16 | padding-left: 20px; 17 | border-bottom: 1px solid @borderColor; 18 | box-sizing: border-box; 19 | } 20 | #toolbar { 21 | background: @backgroundColor; 22 | border-bottom: 1px solid @borderColor; 23 | padding: 4px 14px; 24 | i { 25 | font-size: 18px; 26 | padding: 4px; 27 | margin-right: 8px; 28 | color: #999999; 29 | &:hover { 30 | cursor: pointer; 31 | background-color: #eeeeee; 32 | color: #5cb6ff; 33 | } 34 | } 35 | } 36 | #itempannel { 37 | box-sizing: border-box; 38 | background-color: @backgroundColor; 39 | border-right: 1px solid @borderColor; 40 | height: @pageHeight; 41 | padding-top: 10px; 42 | overflow: hidden; 43 | display: flex; 44 | flex-direction: row; 45 | flex-wrap: wrap; 46 | justify-content: space-around; 47 | align-content: flex-start; 48 | .getItem { 49 | cursor: move; 50 | width: 80px; 51 | height: 80px; 52 | margin-bottom: 20px; 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | img { 57 | width: 100%; 58 | } 59 | } 60 | } 61 | #page { 62 | height: @pageHeight; 63 | canvas { 64 | display: block; 65 | width: 100%; 66 | } 67 | } 68 | .right-part { 69 | height: @pageHeight; 70 | display: flex; 71 | flex-direction: column; 72 | justify-content: flex-start; 73 | } 74 | #detailpannel { 75 | flex-grow: 1; 76 | 77 | background-color: @backgroundColor; 78 | border-left: 1px solid @borderColor; 79 | overflow-y: scroll; 80 | #nodeAttributeBar, 81 | #edgeAttributeBar, 82 | #groupAttributeBar, 83 | #canvasAttributeBar, 84 | #multiAttributeBar { 85 | .title { 86 | height: 34px; 87 | line-height: 34px; 88 | text-align: center; 89 | box-sizing: border-box; 90 | font-weight: bold; 91 | font-size: 13px; 92 | border-width: 0 0 1px 0; 93 | border-style: solid; 94 | border-color: @borderColor; 95 | } 96 | .main { 97 | padding: 10px; 98 | } 99 | } 100 | } 101 | #minimap { 102 | background-color: @backgroundColor; 103 | border-top: 1px solid #ccc; 104 | border-left: 1px solid #ccc; 105 | .title { 106 | height: 34px; 107 | line-height: 34px; 108 | text-align: center; 109 | box-sizing: border-box; 110 | font-weight: bold; 111 | font-size: 13px; 112 | border-width: 0 0 1px 0; 113 | border-style: solid; 114 | border-color: @borderColor; 115 | } 116 | } 117 | #contextmenu { 118 | display: none; 119 | .menu { 120 | /deep/ .el-button { 121 | width: 100%; 122 | display: block; 123 | margin-left: 0; 124 | border-radius: 0 !important; 125 | border-bottom: none; 126 | &:nth-last-of-type(1) { 127 | border-bottom: 1px solid #dcdfe6; 128 | } 129 | } 130 | } 131 | } 132 | .save-as-image-dialog { 133 | /deep/ .el-select { 134 | display: block; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/views/editor/mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | // 保存节点属性 4 | saveNodeAttribute() { 5 | this.editor.executeCommand(() => { 6 | // 获取画布 7 | const page = this.editor.getCurrentPage() 8 | // 获取所选对象 9 | const selectedItem = page.getSelected()[0] 10 | page.update(selectedItem.id, { 11 | label: this.nodeAttributeForm.label, 12 | size: this.nodeAttributeForm.width + '*' + this.nodeAttributeForm.height, 13 | color: this.nodeAttributeForm.color 14 | }) 15 | }) 16 | }, 17 | // 保存边属性 18 | saveEdgeAttribute() { 19 | this.editor.executeCommand(() => { 20 | // 获取画布 21 | const page = this.editor.getCurrentPage() 22 | // 获取所选对象 23 | const selectedItem = page.getSelected()[0] 24 | console.log(this.edgeAttributeForm) 25 | page.update(selectedItem.id, { 26 | label: this.edgeAttributeForm.label, 27 | shape: this.edgeAttributeForm.shape 28 | }) 29 | }) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /static/css/chunk-575555d9.96b835c7.css: -------------------------------------------------------------------------------- 1 | @supports (-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-55776db6]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-55776db6]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-55776db6]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-55776db6]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-55776db6]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-55776db6]{position:relative}.login-container .title-container .title[data-v-55776db6]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-55776db6]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} -------------------------------------------------------------------------------- /static/css/chunk-768ebd2f.a1c9ad1b.css: -------------------------------------------------------------------------------- 1 | #vue-g6-editor .database{overflow:hidden}#vue-g6-editor .el-menu{background-color:#fbfbfb}#vue-g6-editor .el-submenu__title{height:35px;line-height:35px}#vue-g6-editor header:first-of-type{background:#fbfbfb;line-height:40px;padding-left:20px;border-bottom:1px solid #dadce0;-webkit-box-sizing:border-box;box-sizing:border-box}#vue-g6-editor #toolbar{background:#fbfbfb;border-bottom:1px solid #dadce0;padding:4px 14px}#vue-g6-editor #toolbar i{font-size:15px;padding:4px;margin-right:8px;color:#333}#vue-g6-editor #toolbar i:hover{cursor:pointer;background-color:#eee;color:#5cb6ff}#vue-g6-editor #itempannel{-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#f7f9fb;border-right:1px solid #dadce0;height:calc(100vh - 78px);padding-top:10px;overflow:hidden;display:inherit;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:distribute;justify-content:space-around;-ms-flex-line-pack:start;align-content:flex-start}#vue-g6-editor #itempannel .getItem{color:rgba(0,0,0,.65);border-radius:4px;height:28px;line-height:26px;padding-left:8px;list-style-type:none;font-size:12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#vue-g6-editor #page{height:calc(71vh - 78px)}#vue-g6-editor #page canvas{display:block;width:100%}#vue-g6-editor .right-part{height:calc(100vh - 78px);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}#vue-g6-editor #detailpannel{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;background-color:#fbfbfb;border-left:1px solid #dadce0;overflow-y:scroll}#vue-g6-editor #detailpannel #canvasAttributeBar .title,#vue-g6-editor #detailpannel #edgeAttributeBar .title,#vue-g6-editor #detailpannel #groupAttributeBar .title,#vue-g6-editor #detailpannel #multiAttributeBar .title,#vue-g6-editor #detailpannel #nodeAttributeBar .title{height:34px;line-height:34px;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;font-weight:700;font-size:13px;border-width:0 0 1px 0;border-style:solid;border-color:#dadce0}#vue-g6-editor #detailpannel #canvasAttributeBar .main,#vue-g6-editor #detailpannel #edgeAttributeBar .main,#vue-g6-editor #detailpannel #groupAttributeBar .main,#vue-g6-editor #detailpannel #multiAttributeBar .main,#vue-g6-editor #detailpannel #nodeAttributeBar .main{padding:10px}#vue-g6-editor #minimap{background-color:#fbfbfb;border-top:1px solid #ccc;border-left:1px solid #ccc}#vue-g6-editor #minimap .title{height:34px;line-height:34px;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;font-weight:700;font-size:13px;border-width:0 0 1px 0;border-style:solid;border-color:#dadce0}#vue-g6-editor #contextmenu{display:none}#vue-g6-editor #contextmenu .menu .el-button{width:100%;display:block;margin-left:0;border-radius:0!important;border-bottom:none}#vue-g6-editor #contextmenu .menu .el-button:last-of-type{border-bottom:1px solid #dcdfe6}#vue-g6-editor .save-as-image-dialog .el-select{display:block}#vue-g6-editor .el-pagination{text-align:center}#vue-g6-editor .el-table--medium td,#vue-g6-editor .el-table--medium th{padding:5px 0}#vue-g6-editor .el-table{height:calc(40vh - 78px)!important}.itempannel ul{padding:0;padding-left:16px;border-bottom:1px solid #ccc;padding-bottom:20px}.itempannel li{color:rgba(0,0,0,.65);border-radius:4px;height:28px;line-height:26px;padding-left:8px;border:1px solid transparent;list-style-type:none;font-size:12px}.itempannel li,.itempannel li:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.itempannel li:hover{width:160px}.itempanel li:hover,.itempannel li:hover{background:#fff;border:1px solid #ced4d9;cursor:move}.itempannel .icon{width:16px;height:16px;display:inline-block;vertical-align:middle;margin-right:8px;font-size:14px;background:url(https://gw.alipayobjects.com/zos/rmsportal/czNEJAmyDpclFaSucYWB.svg)}.itempannel h6{font-size:15px;text-align:center;padding:0;margin:0;margin-bottom:14px}.itempannel i{margin-right:10px;margin-left:-10px} -------------------------------------------------------------------------------- /static/css/chunk-cbb61e6a.6e6cbd15.css: -------------------------------------------------------------------------------- 1 | .knowledge[data-v-74c84609]{padding-top:50px}.knowledge h3[data-v-74c84609]{text-align:center}.knowledge h3 span[data-v-74c84609]{font-size:20px}.knowledge .el-row[data-v-74c84609]{margin-bottom:20px}.knowledge .el-button--primary[data-v-74c84609]{background-color:#0d5391;border-color:#0d5391} -------------------------------------------------------------------------------- /static/css/chunk-d7a7d948.5719ffb1.css: -------------------------------------------------------------------------------- 1 | .dashboard-container[data-v-682337c0]{margin:30px}.dashboard-text[data-v-682337c0]{font-size:30px;line-height:46px} -------------------------------------------------------------------------------- /static/css/chunk-f1a6febe.2f3c92ef.css: -------------------------------------------------------------------------------- 1 | .knowledgeDetails[data-v-2636a9b4]{padding-top:10px}.knowledgeDetails .fa-fw[data-v-2636a9b4]{margin-right:5px}.knowledgeDetails .grid-content[data-v-2636a9b4]{height:calc(100vh - 80px)}.knowledgeDetails .grid-content .article[data-v-2636a9b4]{text-indent:30px;line-height:25px;font-size:14px}.knowledgeDetails .grid-content .el-card[data-v-2636a9b4]{height:100%}.knowledgeDetails .grid-content .el-tree[data-v-2636a9b4],.knowledgeDetails .grid-content .text[data-v-2636a9b4]{font-size:14px}.knowledgeDetails .grid-content .item[data-v-2636a9b4]{margin-bottom:18px}.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:after,.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:before{display:table;content:""}.knowledgeDetails .grid-content .clearfix[data-v-2636a9b4]:after{clear:both}.knowledgeDetails .lists li[data-v-2636a9b4]{list-style:none;margin-bottom:10px}.knowledgeDetails h3[data-v-2636a9b4]{text-align:center}.knowledgeDetails h3 span[data-v-2636a9b4]{font-size:20px}.knowledgeDetails .el-row[data-v-2636a9b4]{margin-bottom:20px} -------------------------------------------------------------------------------- /static/fonts/element-icons.2fad952a.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/element-icons.2fad952a.woff -------------------------------------------------------------------------------- /static/fonts/element-icons.6f0a7632.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/element-icons.6f0a7632.ttf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.674f50d2.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/fontawesome-webfont.674f50d2.eot -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.af7ae505.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/fontawesome-webfont.af7ae505.woff2 -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.b06871f2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/fontawesome-webfont.b06871f2.ttf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.fee66e71.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/fonts/fontawesome-webfont.fee66e71.woff -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/logo.7ec6731e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangwenwu/vue-element-admin-g6-editor/582636f1b259b573a7a0df9ccb3f82013d282fbb/static/img/logo.7ec6731e.png -------------------------------------------------------------------------------- /static/js/chunk-2d0da8f7.37c90bb0.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0da8f7"],{"6bb7":function(n,u,l){"use strict";l.r(u);var a,c,e=l("2877"),o={},p=Object(e["a"])(o,a,c,!1,null,null,null);u["default"]=p.exports}}]); -------------------------------------------------------------------------------- /static/js/chunk-2d21446b.65936c26.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d21446b"],{aff6:function(n,u,l){"use strict";l.r(u);var a,c,e=l("2877"),o={},p=Object(e["a"])(o,a,c,!1,null,null,null);u["default"]=p.exports}}]); -------------------------------------------------------------------------------- /static/js/chunk-575555d9.a6bafad6.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-575555d9"],{2017:function(e,t,n){"use strict";var s=n("b12d"),o=n.n(s);o.a},"3e0a":function(e,t,n){"use strict";var s=n("8dc6"),o=n.n(s);o.a},"8dc6":function(e,t,n){},"9ed6":function(e,t,n){"use strict";n.r(t);var s=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"login-container"},[n("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on","label-position":"left"}},[n("div",{staticClass:"title-container"},[n("h3",{staticClass:"title"},[e._v("系统登录")])]),e._v(" "),n("el-form-item",{attrs:{prop:"username"}},[n("span",{staticClass:"svg-container"},[n("svg-icon",{attrs:{"icon-class":"user"}})],1),e._v(" "),n("el-input",{ref:"username",attrs:{placeholder:"Username",name:"username",type:"text",tabindex:"1","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),e._v(" "),n("el-form-item",{attrs:{prop:"password"}},[n("span",{staticClass:"svg-container"},[n("svg-icon",{attrs:{"icon-class":"password"}})],1),e._v(" "),n("el-input",{key:e.passwordType,ref:"password",attrs:{type:e.passwordType,placeholder:"Password",name:"password",tabindex:"2","auto-complete":"on"},nativeOn:{keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.handleLogin(t)}},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}}),e._v(" "),n("span",{staticClass:"show-pwd",on:{click:e.showPwd}},[n("svg-icon",{attrs:{"icon-class":"password"===e.passwordType?"eye":"eye-open"}})],1)],1),e._v(" "),n("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:e.loading,type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),e.handleLogin(t)}}},[e._v("登录")])],1)],1)},o=[],r=n("61f7"),a={name:"Login",data:function(){var e=function(e,t,n){Object(r["b"])(t)?n():n(new Error("Please enter the correct user name"))},t=function(e,t,n){t.length<4?n(new Error("The password can not be less than 6 digits")):n()};return{loginForm:{username:"admin",password:"admin"},loginRules:{username:[{required:!0,trigger:"blur",validator:e}],password:[{required:!0,trigger:"blur",validator:t}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:{showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){e.$refs.password.focus()}))},handleLogin:function(){var e=this;this.$refs.loginForm.validate((function(t){if(!t)return console.log("error submit!!"),!1;e.loading=!0,e.$store.dispatch("user/login",e.loginForm).then((function(){e.$router.push({path:e.redirect||"/"}),e.loading=!1})).catch((function(){e.loading=!1}))}))}}},i=a,l=(n("2017"),n("3e0a"),n("2877")),c=Object(l["a"])(i,s,o,!1,null,"55776db6",null);t["default"]=c.exports},b12d:function(e,t,n){}}]); -------------------------------------------------------------------------------- /static/js/chunk-cbb61e6a.eb74048e.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-cbb61e6a"],{"25fa":function(t,e,a){"use strict";var i=a("d005"),s=a.n(i);s.a},"3c34":function(t,e,a){"use strict";a.r(e);var i=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"knowledge"},[a("el-row",[a("el-col",{attrs:{span:24}},[a("div",{staticClass:"grid-content bg-purple-dark"},[a("h3",{staticClass:"title"},[t._v("数据"),a("span",[t._v("·")]),t._v("知识"),a("span",[t._v("·")]),t._v("智能")])])])],1),t._v(" "),a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:12,offset:6}},[a("div",{staticClass:"grid-content bg-purple"},[a("div",[a("el-input",{staticClass:"input-with-select",attrs:{placeholder:"请输入内容"},model:{value:t.search,callback:function(e){t.search=e},expression:"search"}},[a("el-button",{attrs:{slot:"append",icon:"el-icon-search"},on:{click:t.enterSearch},slot:"append"})],1)],1)])])],1),t._v(" "),a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:12,offset:6}},[a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("法律")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("法规")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("审计")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("工商")]),t._v(" "),a("el-button",{attrs:{type:"primary",size:"mini"}},[t._v("公司")])],1)],1),t._v(" "),a("el-dialog",{attrs:{visible:t.dialogPvVisible,title:"title"},on:{"update:visible":function(e){t.dialogPvVisible=e}}},[a("el-table",{staticStyle:{width:"100%"},attrs:{data:t.pvData,border:"",fit:"","highlight-current-row":""}},[a("el-table-column",{attrs:{prop:"key",label:"Channel"}}),t._v(" "),a("el-table-column",{attrs:{prop:"pv",label:"Pv"}})],1),t._v(" "),a("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary"},on:{click:function(e){t.dialogPvVisible=!1}}},[t._v("取消")])],1)],1)],1)},s=[],l={data:function(){return{pvData:"",search:""}},created:function(){this.dialogPvVisible=!1},methods:{enterSearch:function(){this.$router.push({path:"/documentation/details/1"})}}},r=l,n=(a("25fa"),a("2877")),o=Object(n["a"])(r,i,s,!1,null,"74c84609",null);e["default"]=o.exports},d005:function(t,e,a){}}]); -------------------------------------------------------------------------------- /static/js/chunk-d7a7d948.06e0cdfa.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-d7a7d948"],{9406:function(t,a,s){"use strict";s.r(a);var n=function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"dashboard-container"},[s("div",{staticClass:"dashboard-text"},[t._v("用户名: "+t._s(t.name))]),t._v(" "),s("div",{staticClass:"dashboard-text"},[t._v("权限: "),t._l(t.roles,(function(a){return s("span",{key:a},[t._v(t._s(a))])}))],2)])},e=[],c=s("5530"),r=s("2f62"),d={name:"Dashboard",computed:Object(c["a"])({},Object(r["b"])(["name","roles"]))},o=d,i=(s("d117"),s("2877")),u=Object(i["a"])(o,n,e,!1,null,"682337c0",null);a["default"]=u.exports},d117:function(t,a,s){"use strict";var n=s("f250"),e=s.n(n);e.a},f250:function(t,a,s){}}]); -------------------------------------------------------------------------------- /static/js/chunk-f1a6febe.6af28b13.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-f1a6febe"],{bd9d:function(t,a,e){"use strict";var l=e("cbfa"),s=e.n(l);s.a},cbfa:function(t,a,e){},dc0f:function(t,a,e){"use strict";e.r(a);var l=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"knowledgeDetails"},[e("el-row",[e("el-col",{attrs:{span:4}},[e("div",{staticClass:"grid-content bg-purple-dark"},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("------")])]),t._v(" "),e("el-tree",{attrs:{data:t.data,props:t.defaultProps,"default-expand-all":"true"},on:{"node-click":t.handleNodeClick},scopedSlots:t._u([{key:"default",fn:function(a){var l=a.node;return e("span",{staticClass:"custom-tree-node"},[e("span",[e("i",{staticClass:"fa fa-file-o fa-fw"}),t._v(t._s(l.label)+"\n ")])])}}])})],1)],1)]),t._v(" "),e("el-col",{attrs:{span:4}},[e("div",{staticClass:"grid-content bg-purple-dark",staticStyle:{"border-left":"0","border-right":"0"}},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("关键字")])]),t._v(" "),t._l(4,(function(a){return e("div",{key:a,staticClass:"text item"},[t._v("\n "+t._s("列表内容 "+a)+"\n ")])}))],2)],1)]),t._v(" "),e("el-col",{attrs:{span:16}},[e("div",{staticClass:"grid-content bg-purple-dark"},[e("el-card",{staticClass:"box-card"},[e("div",{staticClass:"clearfix",attrs:{slot:"header"},slot:"header"},[e("span",[t._v("文章标题")])]),t._v(" "),e("div",{staticClass:"article"},[t._v("\n 中赢智数(广东)科技有限公司成立于2015年,以为政府及国有企业进行数据治理及大数据分析、数据咨询服务为核心业务及发展目标,为审计机关、检察院、纪委、财政、政府办公厅、工商等政府部门及国资委企业客户提供数据治理及数据分析产品及服务整体解决方案.公司成立产品研发中心、数据分析服务中心、实施交付中心等技术部门。用精湛的技术服务于广大客户并不断累积丰富的经验,为审计机关、教育从审计产品开发、实施及数据服务提供全方位的解决方案。\n "),e("br"),t._v("中赢智数(广东)科技有限公司成立于2015年,以为政府及国有企业进行数据治理及大数据分析、数据咨询服务为核心业务及发展目标,为审计机关、检察院、纪委、财政、政府办公厅、工商等政府部门及国资委企业客户提供数据治理及数据分析产品及服务整体解决方案。\n ")])])],1)])],1),t._v(" "),e("el-dialog",{attrs:{visible:t.dialogPvVisible,title:"title"},on:{"update:visible":function(a){t.dialogPvVisible=a}}},[e("el-table",{staticStyle:{width:"100%"},attrs:{data:t.pvData,border:"",fit:"","highlight-current-row":""}},[e("el-table-column",{attrs:{prop:"key",label:"Channel"}}),t._v(" "),e("el-table-column",{attrs:{prop:"pv",label:"Pv"}})],1),t._v(" "),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{attrs:{type:"primary"},on:{click:function(a){t.dialogPvVisible=!1}}},[t._v("取消")])],1)],1)],1)},s=[],i={data:function(){return{pvData:"",search:"",data:[{label:"法规库",children:[{label:"审计法规"}]},{label:"案例库",children:[{label:"审计案例"},{label:"审计案例3"}]},{label:"刑法库",children:[{label:"审计案例"},{label:"审计案例"}]}],defaultProps:{children:"children",label:"label"}}},created:function(){this.dialogPvVisible=!1},methods:{enterSearch:function(){this.$router.push({path:"/documentation/details/1"})},handleNodeClick:function(t){console.log(t)}}},r=i,n=(e("bd9d"),e("2877")),o=Object(n["a"])(r,l,s,!1,null,"2636a9b4",null);a["default"]=o.exports}}]); -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/components/Breadcrumb.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue } from '@vue/test-utils' 2 | import VueRouter from 'vue-router' 3 | import ElementUI from 'element-ui' 4 | import Breadcrumb from '@/components/Breadcrumb/index.vue' 5 | 6 | const localVue = createLocalVue() 7 | localVue.use(VueRouter) 8 | localVue.use(ElementUI) 9 | 10 | const routes = [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | children: [{ 15 | path: 'dashboard', 16 | name: 'dashboard' 17 | }] 18 | }, 19 | { 20 | path: '/menu', 21 | name: 'menu', 22 | children: [{ 23 | path: 'menu1', 24 | name: 'menu1', 25 | meta: { title: 'menu1' }, 26 | children: [{ 27 | path: 'menu1-1', 28 | name: 'menu1-1', 29 | meta: { title: 'menu1-1' } 30 | }, 31 | { 32 | path: 'menu1-2', 33 | name: 'menu1-2', 34 | redirect: 'noredirect', 35 | meta: { title: 'menu1-2' }, 36 | children: [{ 37 | path: 'menu1-2-1', 38 | name: 'menu1-2-1', 39 | meta: { title: 'menu1-2-1' } 40 | }, 41 | { 42 | path: 'menu1-2-2', 43 | name: 'menu1-2-2' 44 | }] 45 | }] 46 | }] 47 | }] 48 | 49 | const router = new VueRouter({ 50 | routes 51 | }) 52 | 53 | describe('Breadcrumb.vue', () => { 54 | const wrapper = mount(Breadcrumb, { 55 | localVue, 56 | router 57 | }) 58 | it('dashboard', () => { 59 | router.push('/dashboard') 60 | const len = wrapper.findAll('.el-breadcrumb__inner').length 61 | expect(len).toBe(1) 62 | }) 63 | it('normal route', () => { 64 | router.push('/menu/menu1') 65 | const len = wrapper.findAll('.el-breadcrumb__inner').length 66 | expect(len).toBe(2) 67 | }) 68 | it('nested route', () => { 69 | router.push('/menu/menu1/menu1-2/menu1-2-1') 70 | const len = wrapper.findAll('.el-breadcrumb__inner').length 71 | expect(len).toBe(4) 72 | }) 73 | it('no meta.title', () => { 74 | router.push('/menu/menu1/menu1-2/menu1-2-2') 75 | const len = wrapper.findAll('.el-breadcrumb__inner').length 76 | expect(len).toBe(3) 77 | }) 78 | // it('click link', () => { 79 | // router.push('/menu/menu1/menu1-2/menu1-2-2') 80 | // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 81 | // const second = breadcrumbArray.at(1) 82 | // console.log(breadcrumbArray) 83 | // const href = second.find('a').attributes().href 84 | // expect(href).toBe('#/menu/menu1') 85 | // }) 86 | // it('noRedirect', () => { 87 | // router.push('/menu/menu1/menu1-2/menu1-2-1') 88 | // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 89 | // const redirectBreadcrumb = breadcrumbArray.at(2) 90 | // expect(redirectBreadcrumb.contains('a')).toBe(false) 91 | // }) 92 | it('last breadcrumb', () => { 93 | router.push('/menu/menu1/menu1-2/menu1-2-1') 94 | const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') 95 | const redirectBreadcrumb = breadcrumbArray.at(3) 96 | expect(redirectBreadcrumb.contains('a')).toBe(false) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/unit/utils/formatTime.spec.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '@/utils/index.js' 2 | 3 | describe('Utils:formatTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | const retrofit = 5 * 1000 6 | 7 | it('ten digits timestamp', () => { 8 | expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') 9 | }) 10 | it('test now', () => { 11 | expect(formatTime(+new Date() - 1)).toBe('刚刚') 12 | }) 13 | it('less two minute', () => { 14 | expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') 15 | }) 16 | it('less two hour', () => { 17 | expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') 18 | }) 19 | it('less one day', () => { 20 | expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') 21 | }) 22 | it('more than one day', () => { 23 | expect(formatTime(d)).toBe('7月13日17时54分') 24 | }) 25 | it('format', () => { 26 | expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 27 | expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 28 | expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/unit/utils/parseTime.spec.js: -------------------------------------------------------------------------------- 1 | import { parseTime } from '@/utils/index.js' 2 | 3 | describe('Utils:parseTime', () => { 4 | const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" 5 | it('timestamp', () => { 6 | expect(parseTime(d)).toBe('2018-07-13 17:54:01') 7 | }) 8 | it('ten digits timestamp', () => { 9 | expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') 10 | }) 11 | it('new Date', () => { 12 | expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') 13 | }) 14 | it('format', () => { 15 | expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') 16 | expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') 17 | expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') 18 | }) 19 | it('get the day of the week', () => { 20 | expect(parseTime(d, '{a}')).toBe('五') // 星期五 21 | }) 22 | it('get the day of the week', () => { 23 | expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 24 | }) 25 | it('empty argument', () => { 26 | expect(parseTime()).toBeNull() 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /tests/unit/utils/validate.spec.js: -------------------------------------------------------------------------------- 1 | import { validUsername, isExternal } from '@/utils/validate.js' 2 | 3 | describe('Utils:validate', () => { 4 | it('validUsername', () => { 5 | expect(validUsername('admin')).toBe(true) 6 | expect(validUsername('editor')).toBe(true) 7 | expect(validUsername('xxxx')).toBe(false) 8 | }) 9 | it('isExternal', () => { 10 | expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true) 11 | expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true) 12 | expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false) 13 | expect(isExternal('/dashboard')).toBe(false) 14 | expect(isExternal('./dashboard')).toBe(false) 15 | expect(isExternal('dashboard')).toBe(false) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const defaultSettings = require('./src/settings.js') 4 | 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | 9 | const name = defaultSettings.title || 'vue Element Admin' // page title 10 | 11 | // If your port is set to 80, 12 | // use administrator privileges to execute the command line. 13 | // For example, Mac: sudo npm run 14 | // You can change the port by the following method: 15 | // port = 9527 npm run dev OR npm run dev --port = 9527 16 | const port = process.env.port || process.env.npm_config_port || 8888 // dev port 17 | 18 | // All configuration item explanations can be find in https://cli.vuejs.org/config/ 19 | module.exports = { 20 | /** 21 | * You will need to set publicPath if you plan to deploy your site under a sub path, 22 | * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, 23 | * then publicPath should be set to "/bar/". 24 | * In most cases please use '/' !!! 25 | * Detail: https://cli.vuejs.org/config/#publicpath 26 | */ 27 | publicPath: './', 28 | outputDir: 'dist', 29 | assetsDir: 'static', 30 | lintOnSave: process.env.NODE_ENV === 'development', 31 | productionSourceMap: false, 32 | devServer: { 33 | port: port, 34 | open: false, 35 | overlay: { 36 | warnings: false, 37 | errors: true 38 | }, 39 | before: require('./mock/mock-server.js') 40 | }, 41 | configureWebpack: { 42 | // provide the app's title in webpack's name field, so that 43 | // it can be accessed in index.html to inject the correct title. 44 | name: name, 45 | resolve: { 46 | alias: { 47 | '@': resolve('src') 48 | } 49 | } 50 | }, 51 | chainWebpack(config) { 52 | config.plugins.delete('preload') // TODO: need test 53 | config.plugins.delete('prefetch') // TODO: need test 54 | 55 | // set svg-sprite-loader 56 | config.module 57 | .rule('svg') 58 | .exclude.add(resolve('src/icons')) 59 | .end() 60 | config.module 61 | .rule('icons') 62 | .test(/\.svg$/) 63 | .include.add(resolve('src/icons')) 64 | .end() 65 | .use('svg-sprite-loader') 66 | .loader('svg-sprite-loader') 67 | .options({ 68 | symbolId: 'icon-[name]' 69 | }) 70 | .end() 71 | 72 | // set preserveWhitespace 73 | config.module 74 | .rule('vue') 75 | .use('vue-loader') 76 | .loader('vue-loader') 77 | .tap(options => { 78 | options.compilerOptions.preserveWhitespace = true 79 | return options 80 | }) 81 | .end() 82 | 83 | config 84 | // https://webpack.js.org/configuration/devtool/#development 85 | .when(process.env.NODE_ENV === 'development', 86 | config => config.devtool('cheap-source-map') 87 | ) 88 | 89 | config 90 | .when(process.env.NODE_ENV !== 'development', 91 | config => { 92 | config 93 | .plugin('ScriptExtHtmlWebpackPlugin') 94 | .after('html') 95 | .use('script-ext-html-webpack-plugin', [{ 96 | // `runtime` must same as runtimeChunk name. default is `runtime` 97 | inline: /runtime\..*\.js$/ 98 | }]) 99 | .end() 100 | config 101 | .optimization.splitChunks({ 102 | chunks: 'all', 103 | cacheGroups: { 104 | libs: { 105 | name: 'chunk-libs', 106 | test: /[\\/]node_modules[\\/]/, 107 | priority: 10, 108 | chunks: 'initial' // only package third parties that are initially dependent 109 | }, 110 | elementUI: { 111 | name: 'chunk-elementUI', // split elementUI into a single package 112 | priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app 113 | test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm 114 | }, 115 | commons: { 116 | name: 'chunk-commons', 117 | test: resolve('src/components'), // can customize your rules 118 | minChunks: 3, // minimum common number 119 | priority: 5, 120 | reuseExistingChunk: true 121 | } 122 | } 123 | }) 124 | config.optimization.runtimeChunk('single') 125 | } 126 | ) 127 | } 128 | } 129 | --------------------------------------------------------------------------------