├── .eslintignore ├── .babelrc ├── .env.production ├── src ├── view │ ├── error-page │ │ ├── blank.vue │ │ ├── 404.vue │ │ ├── 401.vue │ │ ├── 500.vue │ │ ├── error-content.vue │ │ ├── back-btn-group.vue │ │ └── error.less │ ├── dashboard │ │ ├── index.js │ │ ├── example.vue │ │ └── home.vue │ ├── login │ │ ├── login.less │ │ └── login.vue │ ├── join-page.vue │ ├── content │ │ ├── articletype │ │ │ ├── modal.vue │ │ │ └── index.vue │ │ ├── student │ │ │ ├── modal.vue │ │ │ └── index.vue │ │ └── article │ │ │ ├── index.vue │ │ │ └── modify.vue │ ├── sys │ │ └── log │ │ │ ├── modal.vue │ │ │ └── index.vue │ ├── message │ │ └── index.vue │ └── tool │ │ └── autocode.vue ├── components │ ├── main │ │ ├── index.js │ │ ├── components │ │ │ ├── user │ │ │ │ ├── index.js │ │ │ │ ├── user.less │ │ │ │ └── user.vue │ │ │ ├── a-back-top │ │ │ │ ├── index.js │ │ │ │ └── index.vue │ │ │ ├── language │ │ │ │ ├── index.js │ │ │ │ └── language.vue │ │ │ ├── tags-nav │ │ │ │ ├── index.js │ │ │ │ ├── tags-nav.less │ │ │ │ └── tags-nav.vue │ │ │ ├── header-bar │ │ │ │ ├── index.js │ │ │ │ ├── sider-trigger │ │ │ │ │ ├── index.js │ │ │ │ │ ├── sider-trigger.less │ │ │ │ │ └── sider-trigger.vue │ │ │ │ ├── custom-bread-crumb │ │ │ │ │ ├── index.js │ │ │ │ │ ├── custom-bread-crumb.less │ │ │ │ │ └── custom-bread-crumb.vue │ │ │ │ ├── header-bar.less │ │ │ │ └── header-bar.vue │ │ │ ├── side-menu │ │ │ │ ├── index.js │ │ │ │ ├── item-mixin.js │ │ │ │ ├── mixin.js │ │ │ │ ├── side-menu.less │ │ │ │ ├── side-menu-item.vue │ │ │ │ ├── collapsed-menu.vue │ │ │ │ └── side-menu.vue │ │ │ ├── error-store │ │ │ │ ├── index.js │ │ │ │ └── error-store.vue │ │ │ └── fullscreen │ │ │ │ ├── index.js │ │ │ │ └── fullscreen.vue │ │ ├── main.less │ │ └── main.vue │ ├── icons │ │ ├── index.js │ │ └── icons.vue │ ├── editor │ │ ├── index.js │ │ └── editor.vue │ ├── tables │ │ ├── index.js │ │ ├── index.less │ │ ├── handle-btns.js │ │ ├── edit.vue │ │ └── tables.vue │ ├── upload │ │ ├── index.js │ │ └── upload.vue │ ├── count-to │ │ ├── index.js │ │ ├── index.less │ │ └── count-to.vue │ ├── info-card │ │ ├── index.js │ │ └── infor-card.vue │ ├── login-form │ │ ├── index.js │ │ └── login-form.vue │ ├── common-icon │ │ ├── index.js │ │ └── common-icon.vue │ ├── drop-option │ │ ├── index.js │ │ └── DropOption.vue │ ├── parent-view │ │ ├── index.js │ │ └── parent-view.vue │ ├── charts │ │ ├── index.js │ │ ├── bar.vue │ │ ├── pie.vue │ │ └── theme.json │ └── common │ │ ├── util.js │ │ └── common.less ├── router │ ├── _import_production.js │ ├── _import_development.js │ ├── before-close.js │ ├── routers.js │ └── index.js ├── assets │ ├── icons │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.css │ │ └── iconfont.svg │ └── images │ │ └── qq-group.jpg ├── locale │ ├── lang │ │ ├── zh-TW.js │ │ ├── zh-CN.js │ │ └── en-US.js │ └── index.js ├── libs │ ├── render-dom.js │ ├── api.request.js │ ├── router-util.js │ ├── axios.js │ ├── tools.js │ └── util.js ├── directive │ ├── directives.js │ ├── index.js │ └── module │ │ ├── clipboard.js │ │ └── draggable.js ├── plugin │ ├── index.js │ └── error-store │ │ └── index.js ├── store │ ├── index.js │ └── module │ │ ├── app.js │ │ └── user.js ├── index.less ├── App.vue ├── config │ └── index.js ├── main.js └── api │ ├── user.js │ └── base.js ├── .postcssrc.js ├── .travis.yml ├── .env.development ├── .editorconfig ├── .eslintrc.js ├── vue.config.js ├── README.md └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@vue/app" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_UPLOAD_URL = 'http://120.55.160.92:9005/api/file' -------------------------------------------------------------------------------- /src/view/error-page/blank.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/main/index.js: -------------------------------------------------------------------------------- 1 | import Main from './main.vue' 2 | export default Main 3 | -------------------------------------------------------------------------------- /src/view/dashboard/index.js: -------------------------------------------------------------------------------- 1 | import home from './home.vue' 2 | export default home 3 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/icons/index.js: -------------------------------------------------------------------------------- 1 | import Icons from './icons.vue' 2 | export default Icons 3 | -------------------------------------------------------------------------------- /src/components/editor/index.js: -------------------------------------------------------------------------------- 1 | import Editor from './editor.vue' 2 | export default Editor 3 | -------------------------------------------------------------------------------- /src/components/tables/index.js: -------------------------------------------------------------------------------- 1 | import Tables from './tables.vue' 2 | export default Tables 3 | -------------------------------------------------------------------------------- /src/components/upload/index.js: -------------------------------------------------------------------------------- 1 | import Upload from './upload.vue' 2 | export default Upload 3 | -------------------------------------------------------------------------------- /src/components/count-to/index.js: -------------------------------------------------------------------------------- 1 | import countTo from './count-to.vue' 2 | export default countTo 3 | -------------------------------------------------------------------------------- /src/components/main/components/user/index.js: -------------------------------------------------------------------------------- 1 | import User from './user.vue' 2 | export default User 3 | -------------------------------------------------------------------------------- /src/router/_import_production.js: -------------------------------------------------------------------------------- 1 | module.exports = file => () => import('@/view/' + file + '.vue') 2 | -------------------------------------------------------------------------------- /src/components/info-card/index.js: -------------------------------------------------------------------------------- 1 | import InforCard from './infor-card.vue' 2 | export default InforCard 3 | -------------------------------------------------------------------------------- /src/components/login-form/index.js: -------------------------------------------------------------------------------- 1 | import LoginForm from './login-form.vue' 2 | export default LoginForm 3 | -------------------------------------------------------------------------------- /src/components/common-icon/index.js: -------------------------------------------------------------------------------- 1 | import CommonIcon from './common-icon.vue' 2 | export default CommonIcon 3 | -------------------------------------------------------------------------------- /src/components/drop-option/index.js: -------------------------------------------------------------------------------- 1 | import DropOption from './DropOption.vue' 2 | export default DropOption 3 | -------------------------------------------------------------------------------- /src/components/parent-view/index.js: -------------------------------------------------------------------------------- 1 | import ParentView from './parent-view.vue' 2 | export default ParentView 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | script: npm run lint 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /src/assets/icons/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beclass/iview-admin-cms/HEAD/src/assets/icons/iconfont.eot -------------------------------------------------------------------------------- /src/assets/icons/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beclass/iview-admin-cms/HEAD/src/assets/icons/iconfont.ttf -------------------------------------------------------------------------------- /src/components/main/components/a-back-top/index.js: -------------------------------------------------------------------------------- 1 | import ABackTop from './index.vue' 2 | export default ABackTop 3 | -------------------------------------------------------------------------------- /src/components/main/components/language/index.js: -------------------------------------------------------------------------------- 1 | import Language from './language.vue' 2 | export default Language 3 | -------------------------------------------------------------------------------- /src/components/main/components/tags-nav/index.js: -------------------------------------------------------------------------------- 1 | import TagsNav from './tags-nav.vue' 2 | export default TagsNav 3 | -------------------------------------------------------------------------------- /src/locale/lang/zh-TW.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: '首頁', 3 | login: '登錄', 4 | message: '消息中心' 5 | } 6 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_SRC = '/Users/test/fsadmin-cms/src' 2 | VUE_APP_UPLOAD_URL = 'http://127.0.0.1:9004/api/file' 3 | -------------------------------------------------------------------------------- /src/assets/icons/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beclass/iview-admin-cms/HEAD/src/assets/icons/iconfont.woff -------------------------------------------------------------------------------- /src/assets/images/qq-group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beclass/iview-admin-cms/HEAD/src/assets/images/qq-group.jpg -------------------------------------------------------------------------------- /src/components/main/components/header-bar/index.js: -------------------------------------------------------------------------------- 1 | import HeaderBar from './header-bar' 2 | export default HeaderBar 3 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/index.js: -------------------------------------------------------------------------------- 1 | import SideMenu from './side-menu.vue' 2 | export default SideMenu 3 | -------------------------------------------------------------------------------- /src/locale/lang/zh-CN.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: '控制台', 3 | login: '登录', 4 | message: '消息中心' 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/components/main/components/error-store/index.js: -------------------------------------------------------------------------------- 1 | import ErrorStore from './error-store.vue' 2 | export default ErrorStore 3 | -------------------------------------------------------------------------------- /src/components/main/components/fullscreen/index.js: -------------------------------------------------------------------------------- 1 | import Fullscreen from './fullscreen.vue' 2 | export default Fullscreen 3 | -------------------------------------------------------------------------------- /src/components/charts/index.js: -------------------------------------------------------------------------------- 1 | import ChartPie from './pie.vue' 2 | import ChartBar from './bar.vue' 3 | export { ChartPie, ChartBar } 4 | -------------------------------------------------------------------------------- /src/locale/lang/en-US.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: 'dashboard', 3 | login: 'Login', 4 | message: 'Message Center' 5 | } 6 | -------------------------------------------------------------------------------- /src/router/_import_development.js: -------------------------------------------------------------------------------- 1 | module.default = file => require('@/view/' + file + '.vue').default // vue-loader at least v13.0.0+ 2 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/sider-trigger/index.js: -------------------------------------------------------------------------------- 1 | import siderTrigger from './sider-trigger.vue' 2 | export default siderTrigger 3 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/custom-bread-crumb/index.js: -------------------------------------------------------------------------------- 1 | import customBreadCrumb from './custom-bread-crumb.vue' 2 | export default customBreadCrumb 3 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.less: -------------------------------------------------------------------------------- 1 | .custom-bread-crumb{ 2 | display: inline-block; 3 | vertical-align: top; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/common/util.js: -------------------------------------------------------------------------------- 1 | export const showTitle = (item, vm) => { 2 | return vm.$config.useI18n ? vm.$t(item.name) : ((item.meta && item.meta.title) || item.name) 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/libs/render-dom.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'RenderDom', 3 | functional: true, 4 | props: { 5 | render: Function 6 | }, 7 | render: (h, ctx) => { 8 | return ctx.props.render(h) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/count-to/index.less: -------------------------------------------------------------------------------- 1 | @prefix: ~"count-to"; 2 | 3 | .@{prefix}-wrapper{ 4 | .content-outer{ 5 | display: inline-block; 6 | .@{prefix}-unit-text{ 7 | font-style: normal; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/directive/directives.js: -------------------------------------------------------------------------------- 1 | import draggable from './module/draggable' 2 | import clipboard from './module/clipboard' 3 | 4 | const directives = { 5 | draggable, 6 | clipboard 7 | } 8 | 9 | export default directives 10 | -------------------------------------------------------------------------------- /src/components/common/common.less: -------------------------------------------------------------------------------- 1 | .no-select{ 2 | -webkit-touch-callout: none; 3 | -webkit-user-select: none; 4 | -khtml-user-select: none; 5 | -moz-user-select: none; 6 | -ms-user-select: none; 7 | user-select: none; 8 | } 9 | -------------------------------------------------------------------------------- /src/libs/api.request.js: -------------------------------------------------------------------------------- 1 | import HttpRequest from '@/libs/axios' 2 | import config from '@/config' 3 | const baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro 4 | 5 | const axios = new HttpRequest(baseUrl) 6 | export default axios 7 | -------------------------------------------------------------------------------- /src/plugin/index.js: -------------------------------------------------------------------------------- 1 | import config from '@/config' 2 | const { plugin } = config 3 | 4 | export default (Vue) => { 5 | for (let name in plugin) { 6 | const value = plugin[name] 7 | Vue.use(require(`./${name}`).default, typeof value === 'object' ? value : undefined) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/main/components/user/user.less: -------------------------------------------------------------------------------- 1 | .user{ 2 | &-avator-dropdown{ 3 | cursor: pointer; 4 | display: inline-block; 5 | // height: 64px; 6 | vertical-align: middle; 7 | // line-height: 64px; 8 | .ivu-badge-dot{ 9 | top: 16px; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/header-bar.less: -------------------------------------------------------------------------------- 1 | .header-bar{ 2 | width: 100%; 3 | height: 100%; 4 | position: relative; 5 | .custom-content-con{ 6 | float: right; 7 | height: auto; 8 | padding-right: 20px; 9 | line-height: 64px; 10 | & > *{ 11 | float: right; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/tables/index.less: -------------------------------------------------------------------------------- 1 | .search-con{ 2 | padding: 10px 0; 3 | .search{ 4 | &-col{ 5 | display: inline-block; 6 | width: 200px; 7 | } 8 | &-input{ 9 | display: inline-block; 10 | width: 200px; 11 | margin-left: 2px; 12 | } 13 | &-btn{ 14 | margin-left: 2px; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/router/before-close.js: -------------------------------------------------------------------------------- 1 | import { Modal } from 'iview' 2 | 3 | const beforeClose = { 4 | before_close_normal: (resolve) => { 5 | Modal.confirm({ 6 | title: '确定要关闭这一页吗', 7 | onOk: () => { 8 | resolve(true) 9 | }, 10 | onCancel: () => { 11 | resolve(false) 12 | } 13 | }) 14 | } 15 | } 16 | 17 | export default beforeClose 18 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import user from './module/user' 5 | import app from './module/app' 6 | 7 | Vue.use(Vuex) 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | // 12 | }, 13 | mutations: { 14 | // 15 | }, 16 | actions: { 17 | // 18 | }, 19 | modules: { 20 | user, 21 | app 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /src/view/error-page/404.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /src/view/error-page/401.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /src/view/error-page/500.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/sider-trigger/sider-trigger.less: -------------------------------------------------------------------------------- 1 | .trans{ 2 | transition: transform .2s ease; 3 | } 4 | @size: 40px; 5 | .sider-trigger-a{ 6 | padding: 6px; 7 | width: @size; 8 | height: @size; 9 | display: inline-block; 10 | text-align: center; 11 | color: #5c6b77; 12 | margin-top: 12px; 13 | i{ 14 | .trans; 15 | vertical-align: top; 16 | } 17 | &.collapsed i{ 18 | transform: rotateZ(90deg); 19 | .trans; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/index.less: -------------------------------------------------------------------------------- 1 | @import '~iview/src/styles/index.less'; 2 | 3 | @menu-dark-title: #001529; 4 | @menu-dark-active-bg: #000c17; 5 | @layout-sider-background: #001529; 6 | 7 | .container{background: #ffffff;} 8 | .submit-div{position: fixed;bottom:20px;margin: 0 auto;left:0px;right: 0;width: 150px;text-align:center;z-index:2;} 9 | .ivu-form-item{margin-bottom: 17px} 10 | .ivu-dropdown-menu{min-width: 64px} 11 | .ivu-dropdown-item{text-align: center} 12 | .ivu-table-wrapper{overflow: visible} -------------------------------------------------------------------------------- /src/components/main/components/side-menu/item-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | parentItem: { 4 | type: Object, 5 | default: () => {} 6 | }, 7 | theme: String, 8 | iconSize: Number 9 | }, 10 | computed: { 11 | parentName () { 12 | return this.parentItem.name 13 | }, 14 | children () { 15 | return this.parentItem.children 16 | }, 17 | textColor () { 18 | return this.theme === 'dark' ? '#fff' : '#495060' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 31 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 'extends': [ 4 | 'plugin:vue/essential', 5 | '@vue/standard' 6 | ], 7 | rules: { 8 | // allow async-await 9 | 'generator-star-spacing': 'off', 10 | // allow debugger during development 11 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'vue/no-parsing-error': [2, { 13 | 'x-invalid-end-tag': false 14 | }], 15 | 'no-undef': 'off', 16 | 'camelcase': 'off' 17 | }, 18 | parserOptions: { 19 | parser: 'babel-eslint' 20 | } 21 | } -------------------------------------------------------------------------------- /src/plugin/error-store/index.js: -------------------------------------------------------------------------------- 1 | // import store from '@/store' 2 | export default { 3 | install (Vue, options) { 4 | if (options.developmentOff && process.env.NODE_ENV === 'development') return 5 | Vue.config.errorHandler = (error, vm, mes) => { 6 | let info = { 7 | type: 'script', 8 | code: 0, 9 | mes: error.message, 10 | url: window.location.href 11 | } 12 | Vue.nextTick(() => { 13 | console.log('报错了') 14 | console.log(info) 15 | // store.dispatch('addErrorLog', info) 16 | }) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/mixin.js: -------------------------------------------------------------------------------- 1 | import CommonIcon from '_c/common-icon' 2 | import { showTitle } from '@/libs/util' 3 | export default { 4 | components: { 5 | CommonIcon 6 | }, 7 | methods: { 8 | showTitle (item) { 9 | return showTitle(item, this) 10 | }, 11 | showChildren (item) { 12 | return item.children && (item.children.length > 1 || (item.meta && item.meta.showAlways)) 13 | }, 14 | getNameOrHref (item, children0) { 15 | return item.href ? `isTurnByHref_${item.href}` : (children0 ? item.children[0].name : item.name) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/icons/icons.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/directive/index.js: -------------------------------------------------------------------------------- 1 | import directive from './directives' 2 | 3 | const importDirective = Vue => { 4 | /** 5 | * 拖拽指令 v-draggable="options" 6 | * options = { 7 | * trigger: /这里传入作为拖拽触发器的CSS选择器/, 8 | * body: /这里传入需要移动容器的CSS选择器/, 9 | * recover: /拖动结束之后是否恢复到原来的位置/ 10 | * } 11 | */ 12 | Vue.directive('draggable', directive.draggable) 13 | /** 14 | * clipboard指令 v-draggable="options" 15 | * options = { 16 | * value: /在输入框中使用v-model绑定的值/, 17 | * success: /复制成功后的回调/, 18 | * error: /复制失败后的回调/ 19 | * } 20 | */ 21 | Vue.directive('clipboard', directive.clipboard) 22 | } 23 | 24 | export default importDirective 25 | -------------------------------------------------------------------------------- /src/view/error-page/error-content.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ code }} 7 | {{ desc }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /src/components/parent-view/parent-view.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 22 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/sider-trigger/sider-trigger.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 28 | -------------------------------------------------------------------------------- /src/components/drop-option/DropOption.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{title}} 5 | 6 | 7 | 8 | 9 | {{item.name}} 10 | 11 | 12 | 13 | 27 | -------------------------------------------------------------------------------- /src/view/login/login.less: -------------------------------------------------------------------------------- 1 | .login{ 2 | width: 100%; 3 | height: 100%; 4 | background:#2d8cf0; 5 | background-size: cover; 6 | background-position: center; 7 | position: relative; 8 | &-con{ 9 | position: absolute; 10 | right: 160px; 11 | top: 50%; 12 | transform: translateY(-60%); 13 | width: 300px; 14 | &-header{ 15 | font-size: 16px; 16 | font-weight: 300; 17 | text-align: center; 18 | padding: 30px 0; 19 | } 20 | .form-con{ 21 | padding: 10px 0 0; 22 | } 23 | .login-tip{ 24 | font-size: 10px; 25 | text-align: center; 26 | color: #c3c3c3; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/tables/handle-btns.js: -------------------------------------------------------------------------------- 1 | const btns = { 2 | delete: (h, params, vm) => { 3 | return h('Poptip', { 4 | props: { 5 | confirm: true, 6 | title: '你确定要删除吗?' 7 | }, 8 | on: { 9 | 'on-ok': () => { 10 | vm.$emit('on-delete', params) 11 | vm.$emit('input', params.tableData.filter((item, index) => index !== params.row.initRowIndex)) 12 | } 13 | } 14 | }, [ 15 | h('Button', { 16 | props: { 17 | type: 'text', 18 | ghost: true 19 | } 20 | }, [ 21 | h('Icon', { 22 | props: { 23 | type: 'md-trash', 24 | size: 18, 25 | color: '#000000' 26 | } 27 | }) 28 | ]) 29 | ]) 30 | } 31 | } 32 | 33 | export default btns 34 | -------------------------------------------------------------------------------- /src/view/join-page.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 本群为使用fsadmin-cms,iview-admin,vue,react,小程序,nodejs,php,java,go等全栈开发者提供交流平台,在这里共同进步 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 27 | 38 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * @description 配置显示在浏览器标签的title 4 | */ 5 | title: 'fsadmin-cms', 6 | /** 7 | * @description token在Cookie中存储的天数,默认1天 8 | */ 9 | cookieExpires: 1, 10 | /** 11 | * @description 是否使用国际化,默认为false 12 | * 如果不使用,则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'} 13 | * 用来在菜单中显示文字 14 | */ 15 | useI18n: false, 16 | /** 17 | * @description api请求基础路径 18 | */ 19 | baseUrl: { 20 | dev: 'http://127.0.0.1:9004', 21 | pro: 'http://120.55.160.92:9005' 22 | }, 23 | /** 24 | * @description 默认打开的首页的路由name值,默认为home 25 | */ 26 | homeName: 'dashboard', 27 | /** 28 | * @description 需要加载的插件 29 | */ 30 | plugin: { 31 | 'error-store': { 32 | showInHeader: false, // 设为false后不会在顶部显示错误日志徽标 33 | developmentOff: true // 设为true后在开发环境不会收集错误信息,方便开发中排查错误 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/side-menu.less: -------------------------------------------------------------------------------- 1 | .side-menu-wrapper{ 2 | user-select: none; 3 | .menu-collapsed{ 4 | padding-top: 10px; 5 | 6 | .ivu-dropdown{ 7 | width: 100%; 8 | .ivu-dropdown-rel a{ 9 | width: 100%; 10 | } 11 | } 12 | .ivu-tooltip{ 13 | width: 100%; 14 | .ivu-tooltip-rel{ 15 | width: 100%; 16 | } 17 | .ivu-tooltip-popper .ivu-tooltip-content{ 18 | .ivu-tooltip-arrow{ 19 | border-right-color: #fff; 20 | } 21 | .ivu-tooltip-inner{ 22 | background: #fff; 23 | color: #495060; 24 | } 25 | } 26 | } 27 | 28 | 29 | } 30 | a.drop-menu-a{ 31 | display: inline-block; 32 | padding: 6px 15px; 33 | width: 100%; 34 | text-align: center; 35 | color: #495060; 36 | } 37 | } 38 | .menu-title{ 39 | padding-left: 6px; 40 | } 41 | -------------------------------------------------------------------------------- /src/view/error-page/back-btn-group.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 返回首页 4 | 返回上一页({{ second }}s) 5 | 6 | 7 | 8 | 39 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/header-bar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 35 | -------------------------------------------------------------------------------- /src/components/common-icon/common-icon.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/view/login/login.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | fsadmin-cms@2019 11 | 12 | 13 | 14 | 15 | 16 | 38 | 40 | -------------------------------------------------------------------------------- /src/directive/module/clipboard.js: -------------------------------------------------------------------------------- 1 | import Clipboard from 'clipboard' 2 | export default { 3 | bind: (el, binding) => { 4 | const clipboard = new Clipboard(el, { 5 | text: () => binding.value.value 6 | }) 7 | el.__success_callback__ = binding.value.success 8 | el.__error_callback__ = binding.value.error 9 | clipboard.on('success', e => { 10 | const callback = el.__success_callback__ 11 | callback && callback(e) 12 | }) 13 | clipboard.on('error', e => { 14 | const callback = el.__error_callback__ 15 | callback && callback(e) 16 | }) 17 | el.__clipboard__ = clipboard 18 | }, 19 | update: (el, binding) => { 20 | el.__clipboard__.text = () => binding.value.value 21 | el.__success_callback__ = binding.value.success 22 | el.__error_callback__ = binding.value.error 23 | }, 24 | unbind: (el, binding) => { 25 | delete el.__success_callback__ 26 | delete el.__error_callback__ 27 | el.__clipboard__.destroy() 28 | delete el.__clipboard__ 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/view/error-page/error.less: -------------------------------------------------------------------------------- 1 | .error-page{ 2 | width: 100%; 3 | height: 100%; 4 | position: relative; 5 | background: #f8f8f9; 6 | .content-con{ 7 | width: 700px; 8 | height: 600px; 9 | position: absolute; 10 | left: 50%; 11 | top: 50%; 12 | transform: translate(-50%, -60%); 13 | img{ 14 | display: block; 15 | width: 100%; 16 | height: 100%; 17 | } 18 | .text-con{ 19 | position: absolute; 20 | left: 0px; 21 | top: 0px; 22 | h4{ 23 | position: absolute; 24 | left: 0px; 25 | top: 0px; 26 | font-size: 80px; 27 | font-weight: 700; 28 | color: #348EED; 29 | } 30 | h5{ 31 | position: absolute; 32 | width: 700px; 33 | left: 0px; 34 | top: 100px; 35 | font-size: 20px; 36 | font-weight: 700; 37 | color: #67647D; 38 | } 39 | } 40 | .back-btn-group{ 41 | position: absolute; 42 | right: 0px; 43 | bottom: 20px; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/main/components/error-store/error-store.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 38 | 39 | 50 | -------------------------------------------------------------------------------- /src/view/content/articletype/modal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 确定 10 | 11 | 12 | 13 | 39 | -------------------------------------------------------------------------------- /src/components/main/components/language/language.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 | {{ value }} 10 | 11 | 12 | 13 | 14 | 15 | 52 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const resolve = dir => { 3 | return path.join(__dirname, dir) 4 | } 5 | const BASE_URL = process.env.NODE_ENV === 'production' 6 | ? '/' 7 | : '/' 8 | module.exports = { 9 | // 警告 webpack 的性能提示 10 | configureWebpack: { 11 | performance: { 12 | hints: 'warning', 13 | maxEntrypointSize: 50000000, 14 | maxAssetSize: 30000000, 15 | assetFilter: function (assetFilename) { 16 | return assetFilename.endsWith('.js') 17 | } 18 | } 19 | }, 20 | publicPath: BASE_URL, 21 | lintOnSave: false, 22 | chainWebpack: config => { 23 | config.resolve.alias 24 | .set('@', resolve('src')) 25 | .set('_c', resolve('src/components')) 26 | }, 27 | // 打包时不生成.map文件 28 | productionSourceMap: false 29 | // 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串 30 | // devServer: { 31 | // proxy:{ 32 | // '/': { 33 | // target: 'http://127.0.0.1:9003', 34 | // secure: false, 35 | // changeOrigin: true, 36 | // pathRewrite: { 37 | // '^/sys': '' 38 | // } 39 | // } 40 | // }, 41 | // host: "localhost", 42 | // port: 8080 43 | // } 44 | } 45 | -------------------------------------------------------------------------------- /src/locale/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import { localRead } from '@/libs/util' 4 | import customZhCn from './lang/zh-CN' 5 | import customZhTw from './lang/zh-TW' 6 | import customEnUs from './lang/en-US' 7 | import zhCnLocale from 'iview/src/locale/lang/zh-CN' 8 | import enUsLocale from 'iview/src/locale/lang/en-US' 9 | import zhTwLocale from 'iview/src/locale/lang/zh-TW' 10 | 11 | Vue.use(VueI18n) 12 | 13 | // 自动根据浏览器系统语言设置语言 14 | const navLang = navigator.language 15 | const localLang = (navLang === 'zh-CN' || navLang === 'en-US') ? navLang : false 16 | let lang = localLang || localRead('local') || 'zh-CN' 17 | 18 | Vue.config.lang = lang 19 | 20 | // vue-i18n 6.x+写法 21 | Vue.locale = () => {} 22 | const messages = { 23 | 'zh-CN': Object.assign(zhCnLocale, customZhCn), 24 | 'zh-TW': Object.assign(zhTwLocale, customZhTw), 25 | 'en-US': Object.assign(enUsLocale, customEnUs) 26 | } 27 | const i18n = new VueI18n({ 28 | locale: lang, 29 | messages 30 | }) 31 | 32 | export default i18n 33 | 34 | // vue-i18n 5.x写法 35 | // Vue.locale('zh-CN', Object.assign(zhCnLocale, customZhCn)) 36 | // Vue.locale('en-US', Object.assign(zhTwLocale, customZhTw)) 37 | // Vue.locale('zh-TW', Object.assign(enUsLocale, customEnUs)) 38 | -------------------------------------------------------------------------------- /src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ showTitle(item) }} 7 | 8 | 9 | 10 | 11 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fsadmin-cms 2 | > 简化繁琐重复的工作,全套restful风格, 实现前端orm,关联查询,自动生成基础页面的实用,易扩展的admin-cms 3 | 4 | #### 效果图 5 |  6 | 7 | #### 在线地址 8 | 9 | [http://120.55.160.92:8082](http://120.55.160.92:8082) 10 | 11 | ``` 12 | 用户1:admin 13 | 密码:111111 14 | 15 | 用户2:guest 16 | 密码:111111 17 | 18 | ``` 19 | 20 | #### 服务端框架 21 | 22 | 优化中... 23 | 24 | 25 | ## 环境配置 26 | - `vue^2.5.10` 27 | - `vue-cli3` 28 | - `iview^3.2.2` 29 | 30 | ## 命令 31 | ```bash 32 | # 下载代码 33 | $ git clone https://github.com/beclass/fsadmin-cms.git 34 | 35 | # 安装依赖 36 | $ npm i 建议 cnpm i 37 | 38 | # 开发调试 39 | $ npm run dev 40 | 41 | # 构建 42 | $ npm run build 43 | ``` 44 | 45 | ## 约定 46 | - `filter.search$$title$$all` - 模糊查询title 47 | ```bash 48 | all:模糊查询 49 | > :大于 50 | < :小于 51 | >= :大于等于 52 | <= :小于等于 53 | ``` 54 | - `belongsTo` - 属于,{ name: 'article_type', fkey: 'article_type_id', attrs: ['name->typeName'] } }, 55 | - name:主表名 56 | - fkey:外键字段,关联主表的主键 57 | - attrs:需要关联查询出来的字段, ->表示取别名 58 | 59 | 60 | ## 参考 61 | 62 | #### 网址 63 | 64 | - [Vue.js 中文网](https://cn.vuejs.org/) 65 | - [iView - 一套基于 Vue.js 的高质量 UI 组件库](https://www.iviewui.com/) 66 | - [iview-admin](https://github.com/iview/iview-admin) 67 | 68 | 69 | 70 | 未完待续... 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/view/sys/log/modal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 登录 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 确定 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/side-menu-item.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ showTitle(parentItem) }} 6 | 7 | 8 | 9 | 10 | {{ showTitle(item.children[0]) }} 11 | 12 | 13 | 14 | {{ showTitle(item) }} 15 | 16 | 17 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /src/components/charts/bar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 59 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import store from './store' 7 | import iView from 'iview' 8 | import i18n from '@/locale' 9 | import config from '@/config' 10 | import importDirective from '@/directive' 11 | import { directive as clickOutside } from 'v-click-outside-x' 12 | import installPlugin from '@/plugin' 13 | import './index.less' 14 | import '@/assets/icons/iconfont.css' 15 | import { initRouter } from '@/libs/router-util' 16 | import VueHighlightJS from 'vue-highlightjs' 17 | import VueClipboard from 'vue-clipboard2' 18 | 19 | /* eslint-disable */ 20 | Vue.use(iView, { 21 | i18n: (key, value) => i18n.t(key, value) 22 | }) 23 | Vue.use(VueHighlightJS) 24 | Vue.use(VueClipboard) 25 | /** 26 | * @description 注册admin内置插件 27 | */ 28 | installPlugin(Vue) 29 | /** 30 | * @description 生产环境关掉提示 31 | */ 32 | Vue.config.productionTip = false 33 | /** 34 | * @description 全局注册应用配置 35 | */ 36 | Vue.prototype.$config = config 37 | /** 38 | * 注册指令 39 | */ 40 | importDirective(Vue) 41 | Vue.directive('clickOutside', clickOutside) 42 | 43 | /* eslint-disable no-new */ 44 | new Vue({ 45 | el: '#app', 46 | router, 47 | i18n, 48 | store, 49 | render: h => h(App), 50 | mounted() { 51 | initRouter() 52 | } 53 | }) 54 | -------------------------------------------------------------------------------- /src/components/main/components/user/user.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 消息中心 11 | 12 | 退出登录 13 | 14 | 15 | 16 | 17 | 59 | -------------------------------------------------------------------------------- /src/directive/module/draggable.js: -------------------------------------------------------------------------------- 1 | import { on } from '@/libs/tools' 2 | export default { 3 | inserted: (el, binding, vnode) => { 4 | let triggerDom = document.querySelector(binding.value.trigger) 5 | triggerDom.style.cursor = 'move' 6 | let bodyDom = document.querySelector(binding.value.body) 7 | let pageX = 0 8 | let pageY = 0 9 | let transformX = 0 10 | let transformY = 0 11 | let canMove = false 12 | const handleMousedown = e => { 13 | let transform = /\(.*\)/.exec(bodyDom.style.transform) 14 | if (transform) { 15 | transform = transform[0].slice(1, transform[0].length - 1) 16 | let splitxy = transform.split('px, ') 17 | transformX = parseFloat(splitxy[0]) 18 | transformY = parseFloat(splitxy[1].split('px')[0]) 19 | } 20 | pageX = e.pageX 21 | pageY = e.pageY 22 | canMove = true 23 | } 24 | const handleMousemove = e => { 25 | let xOffset = e.pageX - pageX + transformX 26 | let yOffset = e.pageY - pageY + transformY 27 | if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)` 28 | } 29 | const handleMouseup = e => { 30 | canMove = false 31 | } 32 | on(triggerDom, 'mousedown', handleMousedown) 33 | on(document, 'mousemove', handleMousemove) 34 | on(document, 'mouseup', handleMouseup) 35 | }, 36 | update: (el, binding, vnode) => { 37 | if (!binding.value.recover) return 38 | let bodyDom = document.querySelector(binding.value.body) 39 | bodyDom.style.transform = '' 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/view/content/student/modal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 男 10 | 女 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 确定 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fsadmin-cms", 3 | "version": "1.0.0", 4 | "author": "lwp", 5 | "private": false, 6 | "scripts": { 7 | "dev": "vue-cli-service serve --open --port 8082", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint", 10 | "test:unit": "vue-cli-service test:unit", 11 | "test:e2e": "vue-cli-service test:e2e" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.18.0", 15 | "clipboard": "^2.0.0", 16 | "countup": "^1.8.2", 17 | "echarts": "^4.0.4", 18 | "iview": "^3.2.2", 19 | "js-cookie": "^2.2.0", 20 | "vue": "^2.5.10", 21 | "vue-clipboard2": "^0.3.1", 22 | "vue-highlightjs": "^1.3.3", 23 | "vue-i18n": "^7.8.0", 24 | "vue-router": "^3.0.1", 25 | "vuex": "^3.0.1", 26 | "wangeditor": "^3.1.1" 27 | }, 28 | "devDependencies": { 29 | "@vue/cli-plugin-babel": "^3.0.1", 30 | "@vue/cli-plugin-eslint": "^3.0.1", 31 | "@vue/cli-plugin-unit-mocha": "^3.0.1", 32 | "@vue/cli-service": "^3.0.1", 33 | "@vue/eslint-config-standard": "^3.0.0-beta.10", 34 | "chai": "^4.1.2", 35 | "eslint-plugin-cypress": "^2.0.1", 36 | "less": "^2.7.3", 37 | "less-loader": "^4.0.5", 38 | "lint-staged": "^6.0.0", 39 | "vue-template-compiler": "^2.5.13" 40 | }, 41 | "browserslist": [ 42 | "> 1%", 43 | "last 2 versions", 44 | "not ie <= 8" 45 | ], 46 | "gitHooks": { 47 | "pre-commit": "lint-staged" 48 | }, 49 | "lint-staged": { 50 | "*.js": [ 51 | "vue-cli-service lint", 52 | "git add" 53 | ], 54 | "*.vue": [ 55 | "vue-cli-service lint", 56 | "git add" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/components/main/main.less: -------------------------------------------------------------------------------- 1 | .main{ 2 | .logo-con{ 3 | height: 64px; 4 | padding: 10px; 5 | img{ 6 | height: 44px; 7 | width: auto; 8 | display: block; 9 | margin: 0 auto; 10 | } 11 | } 12 | .header-con{ 13 | background: #fff; 14 | padding: 0 20px; 15 | width: 100%; 16 | } 17 | .main-layout-con{ 18 | height: 100%; 19 | overflow: hidden; 20 | } 21 | .main-content-con{ 22 | height: ~"calc(100% - 60px)"; 23 | overflow: hidden; 24 | } 25 | .tag-nav-wrapper{ 26 | padding: 0; 27 | height:40px; 28 | background:#F0F0F0; 29 | } 30 | .content-wrapper{ 31 | padding: 18px; 32 | height: ~"calc(100% - 80px)"; 33 | overflow: auto; 34 | } 35 | .left-sider{ 36 | .ivu-layout-sider-children{ 37 | overflow-y: scroll; 38 | margin-right: -18px; 39 | } 40 | } 41 | } 42 | .ivu-menu-item > i{ 43 | margin-right: 12px !important; 44 | } 45 | .ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i { 46 | margin-right: 8px !important; 47 | } 48 | .collased-menu-dropdown{ 49 | width: 100%; 50 | margin: 0; 51 | line-height: normal; 52 | padding: 7px 0 6px 16px; 53 | clear: both; 54 | font-size: 12px !important; 55 | white-space: nowrap; 56 | list-style: none; 57 | cursor: pointer; 58 | transition: background 0.2s ease-in-out; 59 | &:hover{ 60 | background: rgba(100, 100, 100, 0.1); 61 | } 62 | & * { 63 | color: #515a6e; 64 | } 65 | .ivu-menu-item > i{ 66 | margin-right: 12px !important; 67 | } 68 | .ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i { 69 | margin-right: 8px !important; 70 | } 71 | } 72 | 73 | .ivu-select-dropdown.ivu-dropdown-transfer{ 74 | max-height: 400px; 75 | } 76 | -------------------------------------------------------------------------------- /src/libs/router-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 定义初始化菜单 3 | */ 4 | import { getToken, localSave, localRead } from '@/libs/util' 5 | import { lazyLoadingCop } from '@/libs/tools' 6 | import { getMenus } from '@/api/user' 7 | import Main from '@/components/main' 8 | import parentView from '@/components/parent-view' 9 | import store from '@/store' 10 | let gotRouter 11 | // 初始化路由 12 | export const initRouter = async() => { 13 | if (!getToken()) return 14 | const menus = await getMenus(); 15 | localSave('dynamicRouter', JSON.stringify(menus)) // 存储路由到localStorage 16 | gotRouter = filterAsyncRouter(menus) // 过滤路由,路由组件转换 17 | store.commit('updateMenuList', gotRouter) 18 | dynamicRouterAdd() 19 | return gotRouter 20 | } 21 | // 加载路由菜单,从localStorage拿到路由,在创建路由时使用 22 | export const dynamicRouterAdd = () => { 23 | let dynamicRouter = [] 24 | let data = localRead('dynamicRouter') 25 | if (!data) { 26 | return dynamicRouter 27 | } 28 | dynamicRouter = filterAsyncRouter(JSON.parse(data)) 29 | return dynamicRouter 30 | } 31 | // @函数: 遍历后台传来的路由字符串,转换为组件对象 32 | export const filterAsyncRouter = asyncRouterMap => { 33 | const accessedRouters = asyncRouterMap.filter(route => { 34 | if (route.component) { 35 | if (route.component === 'Main') { 36 | // Main组件特殊处理 37 | route.component = Main 38 | } else if (route.component === 'parentView') { 39 | // parentView组件特殊处理 40 | route.component = parentView 41 | } else { 42 | route.component = lazyLoadingCop(route.component) 43 | } 44 | } 45 | if (route.children && route.children.length) { 46 | route.children = filterAsyncRouter(route.children) 47 | } 48 | return true 49 | }) 50 | return accessedRouters 51 | } 52 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import axios from '@/libs/api.request' 2 | 3 | export const login = ({ username, password }) => { 4 | const data = { 5 | username, 6 | password 7 | } 8 | return axios.request({ 9 | url: 'user/login', 10 | data, 11 | method: 'post' 12 | }) 13 | } 14 | export const getMenus = () => { 15 | return axios.request({ 16 | url: 'user/menus', 17 | method: 'get' 18 | }) 19 | } 20 | export const getUserInfo = () => { 21 | return axios.request({ 22 | url: 'user/info', 23 | method: 'get' 24 | }) 25 | } 26 | 27 | export const logout = (token) => { 28 | return axios.request({ 29 | url: 'logout', 30 | method: 'post' 31 | }) 32 | } 33 | 34 | export const getUnreadCount = () => { 35 | return axios.request({ 36 | url: 'message/count', 37 | method: 'get' 38 | }) 39 | } 40 | 41 | export const getMessage = () => { 42 | return axios.request({ 43 | url: 'message/init', 44 | method: 'get' 45 | }) 46 | } 47 | 48 | export const getContentByMsgId = msg_id => { 49 | return axios.request({ 50 | url: 'message/content', 51 | method: 'get', 52 | params: { 53 | msg_id 54 | } 55 | }) 56 | } 57 | 58 | export const hasRead = msg_id => { 59 | return axios.request({ 60 | url: 'message/has_read', 61 | method: 'post', 62 | data: { 63 | msg_id 64 | } 65 | }) 66 | } 67 | 68 | export const removeReaded = msg_id => { 69 | return axios.request({ 70 | url: 'message/remove_readed', 71 | method: 'post', 72 | data: { 73 | msg_id 74 | } 75 | }) 76 | } 77 | 78 | export const restoreTrash = msg_id => { 79 | return axios.request({ 80 | url: 'message/restore', 81 | method: 'post', 82 | data: { 83 | msg_id 84 | } 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /src/components/charts/pie.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 71 | -------------------------------------------------------------------------------- /src/libs/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '@/store' 3 | import { Message } from 'iview' 4 | import { getToken } from '@/libs/util' 5 | class HttpRequest { 6 | constructor (baseUrl = baseURL) { 7 | this.baseUrl = baseUrl + '/sys' 8 | this.queue = {} 9 | } 10 | getInsideConfig () { 11 | const config = { 12 | baseURL: this.baseUrl, 13 | headers: { 14 | Author: 'lwp', 15 | Authorization: 'Bearer ' + getToken() 16 | } 17 | } 18 | return config 19 | } 20 | destroy (url) { 21 | delete this.queue[url] 22 | if (!Object.keys(this.queue).length) { 23 | // Spin.hide() 24 | } 25 | } 26 | interceptors (instance, url) { 27 | // 请求拦截 28 | instance.interceptors.request.use(config => { 29 | this.queue[url] = true 30 | return config 31 | }, error => { 32 | return Promise.reject(error) 33 | }) 34 | // 响应拦截 35 | instance.interceptors.response.use(res => { 36 | this.destroy(url) 37 | const { success, errcode } = res.data 38 | if (typeof (errcode) !== 'undefined' && !success) { 39 | Message.error(res.data.errmsg) 40 | if (errcode === 401) { 41 | return store.dispatch('handleLogOut') 42 | } 43 | return false 44 | } 45 | return res.data 46 | }, error => { 47 | Message.error('网络连接失败,请检查网络状态和系统代理设置') 48 | console.log(error) 49 | }) 50 | } 51 | request (options) { 52 | const instance = axios.create() 53 | let config = this.getInsideConfig() 54 | if (options.ext) { 55 | config.baseURL = config.baseURL.replace('sys', options.ext) 56 | delete options.ext 57 | } 58 | options = Object.assign(config, options) 59 | this.interceptors(instance, options.url) 60 | return instance(options) 61 | } 62 | } 63 | export default HttpRequest 64 | -------------------------------------------------------------------------------- /src/api/base.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Desc: 基础api 3 | * @Author: lwp 4 | * @Date: 2019-09-04 18:43:19 5 | * @LastEditors: lwp 6 | * @LastEditTime: 2019-09-05 11:08:04 7 | */ 8 | import axios from '@/libs/api.request' 9 | const list = (model, query) => { 10 | return axios.request({ 11 | url: `/${model}`, 12 | method: 'get', 13 | params: query 14 | }) 15 | } 16 | const info = (model, id) => { 17 | return axios.request({ 18 | url: `/${model}/${id}`, 19 | method: 'get' 20 | }) 21 | } 22 | const create = (model, data) => { 23 | return axios.request({ 24 | url: '/' + model, 25 | data: data, 26 | method: 'post' 27 | }) 28 | } 29 | const update = (model, id, data) => { 30 | return axios.request({ 31 | url: `/${model}/${id}`, 32 | data: data, 33 | method: 'put' 34 | }) 35 | } 36 | const remove = (model, ids) => { 37 | return axios.request({ 38 | url: `/${model}`, 39 | data: { ids: ids }, 40 | method: 'delete' 41 | }) 42 | } 43 | const cmdFun = (model, cmd, params, method) => { 44 | let obj = { 45 | url: `/${model}/cmd/${cmd}`, 46 | data: params, 47 | method: method || 'get' 48 | } 49 | if (obj.method === 'get') { 50 | obj.params = params 51 | } else { 52 | obj.data = params 53 | } 54 | return axios.request(obj) 55 | } 56 | const upload = (formData) => { 57 | return axios.request({ 58 | url: '/file/upload', 59 | data: formData, 60 | method: 'post' 61 | }) 62 | } 63 | const removeFile = (params) => { 64 | return axios.request({ 65 | url: '/file', 66 | data: params, 67 | method: 'delete', 68 | ext: 'api' 69 | }) 70 | } 71 | const createView = (params) => { 72 | return axios.request({ 73 | url: '/util/create/view', 74 | data: params, 75 | method: 'post' 76 | }) 77 | } 78 | export { 79 | list, info, create, update, remove, cmdFun, upload, removeFile, createView 80 | } 81 | -------------------------------------------------------------------------------- /src/components/login-form/login-form.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 登录 19 | 20 | 21 | 22 | 73 | -------------------------------------------------------------------------------- /src/components/info-card/infor-card.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 55 | 56 | 95 | -------------------------------------------------------------------------------- /src/components/main/components/tags-nav/tags-nav.less: -------------------------------------------------------------------------------- 1 | .no-select{ 2 | -webkit-touch-callout: none; 3 | -webkit-user-select: none; 4 | -khtml-user-select: none; 5 | -moz-user-select: none; 6 | -ms-user-select: none; 7 | user-select: none; 8 | } 9 | .size{ 10 | width: 100%; 11 | height: 100%; 12 | } 13 | .tags-nav{ 14 | position: relative; 15 | border-top: 1px solid #F0F0F0; 16 | border-bottom: 1px solid #F0F0F0; 17 | .no-select; 18 | .size; 19 | .close-con{ 20 | position: absolute; 21 | right: 0; 22 | top: 0; 23 | height: 100%; 24 | width: 32px; 25 | background: #fff; 26 | text-align: center; 27 | z-index: 10; 28 | } 29 | .btn-con{ 30 | position: absolute; 31 | top: 0px; 32 | height: 100%; 33 | background: #fff; 34 | padding-top: 3px; 35 | z-index: 10; 36 | button{ 37 | padding: 6px 4px; 38 | line-height: 14px; 39 | text-align: center; 40 | } 41 | &.left-btn{ 42 | left: 0px; 43 | } 44 | &.right-btn{ 45 | right: 32px; 46 | border-right: 1px solid #F0F0F0; 47 | } 48 | } 49 | .scroll-outer{ 50 | position: absolute; 51 | left: 28px; 52 | right: 61px; 53 | top: 0; 54 | bottom: 0; 55 | box-shadow: 0px 0 3px 2px rgba(100,100,100,.1) inset; 56 | .scroll-body{ 57 | height: ~"calc(100% - 1px)"; 58 | display: inline-block; 59 | padding: 1px 4px 0; 60 | position: absolute; 61 | overflow: visible; 62 | white-space: nowrap; 63 | transition: left .3s ease; 64 | .ivu-tag-dot-inner{ 65 | transition: background .2s ease; 66 | } 67 | } 68 | } 69 | .contextmenu { 70 | position: absolute; 71 | margin: 0; 72 | padding: 5px 0; 73 | background: #fff; 74 | z-index: 1000; 75 | list-style-type: none; 76 | border-radius: 4px; 77 | box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .1); 78 | li { 79 | margin: 0; 80 | padding: 5px 15px; 81 | cursor: pointer; 82 | &:hover { 83 | background: #eee; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/components/tables/edit.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ value }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 45 | 46 | 74 | -------------------------------------------------------------------------------- /src/router/routers.js: -------------------------------------------------------------------------------- 1 | import Main from '@/components/main' 2 | /** 3 | * meta除了原生参数外可配置的参数: 4 | * meta: { 5 | * title: { String|Number|Function } 6 | * 显示在侧边栏、面包屑和标签栏的文字 7 | * 使用'{{ 多语言字段 }}'形式结合多语言使用,例子看多语言的路由配置; 8 | * 可以传入一个回调函数,参数是当前路由对象,例子看动态路由和带参路由 9 | * hideInBread: (false) 设为true后此级路由将不会出现在面包屑中,示例看QQ群路由配置 10 | * hideInMenu: (false) 设为true后在左侧菜单不会显示该页面选项 11 | * notCache: (false) 设为true后页面在切换标签后不会缓存,如果需要缓存,无需设置这个字段,而且需要设置页面组件name属性和路由配置的name一致 12 | * access: (null) 可访问该页面的权限数组,当前路由设置的权限会影响子路由 13 | * icon: (-) 该页面在左侧菜单、面包屑和标签导航处显示的图标,如果是自定义图标,需要在图标名称前加下划线'_' 14 | * beforeCloseName: (-) 设置该字段,则在关闭当前tab页时会去'@/router/before-close.js'里寻找该字段名对应的方法,作为关闭前的钩子函数 15 | * } 16 | */ 17 | // 不作为Main组件的子页面展示的页面单独写 18 | export const otherRouter = [ 19 | { 20 | path: '/login', 21 | name: 'login', 22 | meta: { 23 | title: 'Login - 登录', 24 | hideInMenu: true 25 | }, 26 | component: () => import('@/view/login/login.vue') 27 | }, 28 | { 29 | path: '/401', 30 | name: 'error_401', 31 | meta: { 32 | hideInMenu: true 33 | }, 34 | component: () => import('@/view/error-page/401.vue') 35 | }, 36 | { 37 | path: '*', 38 | name: 'error_404', 39 | meta: { 40 | hideInMenu: true 41 | }, 42 | component: () => import('@/view/error-page/blank.vue') 43 | } 44 | 45 | ] 46 | // 作为Main组件的子页面展示但是不在左侧菜单显示的路由写在mainRouter里 47 | export const mainRouter = [ 48 | 49 | { 50 | path: '/', 51 | name: 'dashboard', 52 | redirect: '/dashboard', 53 | component: Main, 54 | meta: { 55 | hideInMenu: true, 56 | notCache: true 57 | }, 58 | children: [ 59 | { 60 | path: '/dashboard', 61 | name: 'dashboard', 62 | meta: { 63 | hideInMenu: true, 64 | title: '控制台', 65 | notCache: true, 66 | icon: 'md-home' 67 | }, 68 | component: () => import('@/view/dashboard') 69 | } 70 | ] 71 | } 72 | ] 73 | export const routes = [...otherRouter, ...mainRouter] 74 | // 所有上面定义的路由都要写在下面输出 75 | export default routes 76 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import routes from './routers' 4 | import store from '@/store' 5 | import iView from 'iview' 6 | import { dynamicRouterAdd } from '@/libs/router-util' 7 | import { setToken, getToken, canTurnTo, setTitle } from '@/libs/util' 8 | import config from '@/config' 9 | const { homeName } = config 10 | 11 | Vue.use(Router) 12 | const router = new Router({ 13 | routes, 14 | mode: 'history' 15 | }) 16 | const LOGIN_PAGE_NAME = 'login' 17 | 18 | const turnTo = (to, access, next) => { 19 | if (canTurnTo(to.name, access, [...routes, ...dynamicRouterAdd()])) next() 20 | else next({ replace: true, name: 'error_401' }) // 无权限,重定向到401页面 21 | } 22 | 23 | router.beforeEach((to, from, next) => { 24 | iView.LoadingBar.start() 25 | const token = getToken() 26 | if (!token && to.name !== LOGIN_PAGE_NAME) { 27 | // 未登录且要跳转的页面不是登录页 28 | next({ 29 | name: LOGIN_PAGE_NAME // 跳转到登录页 30 | }) 31 | } else if (!token && to.name === LOGIN_PAGE_NAME) { 32 | // 未登陆且要跳转的页面是登录页 33 | next() // 跳转 34 | } else if (token && to.name === LOGIN_PAGE_NAME) { 35 | // 已登录且要跳转的页面是登录页 36 | next({ 37 | name: homeName // 跳转到homeName页 38 | }) 39 | } else { 40 | // next() 41 | // return 42 | // const access= ["assets.add_ecs", "assets.delete_ecs","lwp"] 43 | // turnTo(to, access, next) 44 | // return 45 | if (store.state.user.hasGetInfo) { 46 | turnTo(to, store.state.user.access, next) 47 | } else { 48 | store.dispatch('getUserInfo').then(user => { 49 | // 拉取用户信息,通过用户权限和跳转的页面的name来判断是否有权限访问;access必须是一个数组,如:['super_admin'] ['super_admin', 'admin'] 50 | turnTo(to, user.access, next) 51 | store.commit('setAppData', user) 52 | }).catch(() => { 53 | setToken('') 54 | next({ 55 | name: 'login' 56 | }) 57 | }) 58 | } 59 | } 60 | }) 61 | 62 | router.afterEach(to => { 63 | setTitle(to, router.app, store.state.app.appName) 64 | iView.LoadingBar.finish() 65 | window.scrollTo(0, 0) 66 | }) 67 | 68 | export default router 69 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/collapsed-menu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ showTitle(parentItem) }} 4 | 5 | 6 | 7 | {{ showTitle(child) }} 8 | 9 | 10 | 11 | 12 | 52 | -------------------------------------------------------------------------------- /src/components/main/components/a-back-top/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 91 | -------------------------------------------------------------------------------- /src/components/editor/editor.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 88 | -------------------------------------------------------------------------------- /src/components/main/components/fullscreen/fullscreen.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 75 | 76 | 85 | -------------------------------------------------------------------------------- /src/view/dashboard/example.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 115 | -------------------------------------------------------------------------------- /src/view/content/article/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{item.name}} 7 | 8 | 搜索 9 | 添加 10 | 11 | 12 | 13 | 14 | 修改 15 | 删除 16 | 17 | 18 | 19 | 20 | 21 | 22 | 84 | -------------------------------------------------------------------------------- /src/store/module/app.js: -------------------------------------------------------------------------------- 1 | import { 2 | getBreadCrumbList, 3 | setTagNavListInLocalstorage, 4 | getMenuByRouter, 5 | getTagNavListFromLocalstorage, 6 | getHomeRoute, 7 | getNextRoute, 8 | routeHasExist, 9 | routeEqual, 10 | getRouteTitleHandled, 11 | localSave, 12 | localRead 13 | } from '@/libs/util' 14 | import router from '@/router' 15 | import config from '@/config' 16 | const { homeName } = config 17 | const closePage = (state, route) => { 18 | const nextRoute = getNextRoute(state.tagNavList, route) 19 | state.tagNavList = state.tagNavList.filter(item => { 20 | return !routeEqual(item, route) 21 | }) 22 | router.push(nextRoute) 23 | } 24 | export default { 25 | state: { 26 | appName: '', 27 | minLogo: '', 28 | maxLogo: '', 29 | breadCrumbList: [], 30 | tagNavList: [], 31 | homeRoute: {}, 32 | local: localRead('local'), 33 | errorList: [], 34 | hasReadErrorPage: false, 35 | menuList: [] 36 | }, 37 | getters: { 38 | menuList: (state, getters, rootState) => { 39 | return getMenuByRouter(state.menuList, rootState.user.access) 40 | }, 41 | errorCount: state => state.errorList.length 42 | }, 43 | mutations: { 44 | setAppData (state, data) { 45 | state.appName = data.appName 46 | state.maxLogo = data.maxLogo 47 | state.minLogo = data.minLogo 48 | }, 49 | updateMenuList (state, routes) { 50 | // 接受前台数组,刷新菜单 51 | router.addRoutes(routes) 52 | state.menuList = routes 53 | }, 54 | setBreadCrumb (state, route) { 55 | state.breadCrumbList = getBreadCrumbList(route, state.homeRoute) 56 | }, 57 | setHomeRoute (state, routes) { 58 | state.homeRoute = getHomeRoute(routes, homeName) 59 | }, 60 | setTagNavList (state, list) { 61 | let tagList = [] 62 | if (list) { 63 | tagList = [...list] 64 | } else tagList = getTagNavListFromLocalstorage() || [] 65 | if (tagList[0] && tagList[0].name !== homeName) tagList.shift() 66 | let homeTagIndex = tagList.findIndex(item => item.name === homeName) 67 | if (homeTagIndex > 0) { 68 | let homeTag = tagList.splice(homeTagIndex, 1)[0] 69 | tagList.unshift(homeTag) 70 | } 71 | state.tagNavList = tagList 72 | setTagNavListInLocalstorage([...tagList]) 73 | }, 74 | closeTag (state, route) { 75 | let tag = state.tagNavList.filter(item => routeEqual(item, route)) 76 | route = tag[0] ? tag[0] : null 77 | if (!route) return 78 | closePage(state, route) 79 | }, 80 | addTag (state, { route, type = 'unshift' }) { 81 | let router = getRouteTitleHandled(route) 82 | if (!routeHasExist(state.tagNavList, router)) { 83 | if (type === 'push') state.tagNavList.push(router) 84 | else { 85 | if (router.name === homeName) state.tagNavList.unshift(router) 86 | else state.tagNavList.splice(1, 0, router) 87 | } 88 | setTagNavListInLocalstorage([...state.tagNavList]) 89 | } 90 | }, 91 | setLocal (state, lang) { 92 | localSave('local', lang) 93 | state.local = lang 94 | }, 95 | addError (state, error) { 96 | state.errorList.push(error) 97 | }, 98 | setHasReadErrorLoggerStatus (state, status = true) { 99 | state.hasReadErrorPage = status 100 | } 101 | }, 102 | actions: { 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/assets/icons/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1541579316141'); /* IE9*/ 4 | src: url('iconfont.eot?t=1541579316141#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAiEAAsAAAAADmgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8eUnXY21hcAAAAYAAAACjAAACLi+YJuBnbHlmAAACJAAABAgAAAcg4dRWHmhlYWQAAAYsAAAAMQAAADYTL8piaGhlYQAABmAAAAAgAAAAJAfdA4xobXR4AAAGgAAAABQAAAAsLAD//2xvY2EAAAaUAAAAGAAAABgImgpGbWF4cAAABqwAAAAfAAAAIAEcAG5uYW1lAAAGzAAAAUUAAAJtPlT+fXBvc3QAAAgUAAAAbgAAAI54roygeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMTx/ytzwv4EhhrmBoRkozAiSAwDuUwzMeJzlkUEKwkAMRd/YabXFhQvxFF6qPYPrUujGY7jyIr1JoZNjtMnEhag3MOEN5MMk8D9QAoVyVSKEJwGrh6oh6wVN1iM3nc+cVImJVKdOehlklElmWdYVstp+ql8VdIv15a1NLW0zFXsO7Kjz3erH/3+rY37vr6kxnx1LKNWOJZlaxxJNnWOpSu+ot8jgqMvI6KjfyOSo88jsaAbI4tBsig89rQB4nLVUTWwbRRSeNzO767i2g7N/FP9s7MRrE5ON4/V6rSZyU0PiINSSNImES4IUoapWz6hEiqiMBDQqEojkAkiFStyKRC+9VSoFCeUEyqESVUAqEkcu3OAQb3hrJxAXwSGI3X0/szPz5vvm2x0i7O/vf8IJe5VkSJnUyUtklRBQJE1VIjRtUafkmk6pSu2ipleh4+xikkKxSksWTUeo8m8NoagpYtoslTmxrLl37z64e33esuJjU8P5Wd262LxoPVnPZ06Pxfe+C0YjkhSJygPhQCA8ABPOykwuN7NyuRvgUnAgLEnhATkaCQQiUe/7XKUyV6nQz+t2o7l66+rs7NVbq82GXTdrdjxjRGU5amTids2bUDMFtzCsqsMYMqr3IDY6OT05GjsI8Exv/6CSkOWEQigh+y3clxY5QVTcEZFIGtHLxDUJs6WsHR1y9SFKdr1HggCp3V1ICYL36OOpVmvKN9bC1u6R3vZ0qwWtVovgJfqOfUvfIYxIWL+fyETHNVJqSkIT1JTjW8ZWh3yDJDz0ctvsyt51etvrg9/QHhqGlzMM+vbmizPnDWPLMNbW19e7tffvsBzL99aWEfBRY46t+tbe3PypXv/IMDYN43WsQBe9HL2NC33RuxABrPsG+xH3o4bVRE2KgCRqulbWNf8W/UYVHM129aKra24VshZkq+CWD/Oy6Xt8cGYEthgHVlVliCfynAlqjo6oysTKlYUAD4docMI5/1ZioN+GwZNBcTwWUmTdBUqhTwX29QebXzF4An4JJMzwfMl+WQ01+IlQZVR4yhie53ycA16pOI/ODiYNGK4MChdCgXNnX5gIJXPCSYnf2OF850aQ+zJIyOs+u8+mMO8jQdwtg1TIWVRjKAnFcslMi8KfGUPoSUCergUyUk77dMyS69Ms6tijKZKYwUGKbpfdzu+iYeZYAHMFiOVi+MD7h9mb99qC0L7X8c+XatMfTj97KZ5IxJt/pd43tYYQKEjAnXMOB6kQEBrwg+LPjindAPOHNdC3q3ait0I3/ZIunZEARLNYNEUA6czSP3N/7j9wz6ZESdX0VNl1zGNS/szbQaQSIGk4DtVPcZf8AgXpf9A2OyTit5s2syZmand46bhEe2WtodLHkvaoqtTXuXN2/c42WADP9HGfbUcUW7JgqHss4xHtlMys679FqUomdP9VJBQBdnlPABBubpuNwqnmQj6/0HwNQzKxDUJFgKiXurBG6dqFjmeBzsvtRPJgGIZThYa5fdOvsReOticPh6JHHXxsv7ItJpOniYPYsmZ/x0QD/o5P105DeQwF6MH33ogoLi+KQp7zpY3HQV5bFMURzheXeds7gpP+jKNXljjHuYvXHke7cdCxLLZf6YX7B63UcCV4nGNgZGBgAOKAN2ZR8fw2Xxm4WRhA4AbHYRMY/f///1oWBuYGIJeDgQkkCgAvWgs2AAAAeJxjYGRgYG7438AQw8Lw/z8DAwsDA1AEBXADAHXiBHJ4nGNhYGBgYfj/nwVM48cATwECKwAAAAAAjAC6AOgBFAGAAf4CbgLqAzgDkHicY2BkYGDgZkhiYGcAASYg5gJCBob/YD4DABOmAYsAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicbYhdDoIwEAb3a6k/YIIX8VArWewmdJFWJOnpJTG+OQ+TzJCjLy39p4ODR4OAA4444YwWHS7U3IVzn6Voldtb8ksHnvohrlqjjmw1rmzXsvdT7fEbblnCmOfNfJIYStJJfGIL27yb6AOCGR89AAA=') format('woff'), 6 | url('iconfont.ttf?t=1541579316141') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1541579316141#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-bear:before { content: "\e600"; } 19 | 20 | .icon-resize-vertical:before { content: "\e7c3"; } 21 | 22 | .icon-chuizhifanzhuan:before { content: "\e661"; } 23 | 24 | .icon-shuipingfanzhuan:before { content: "\e662"; } 25 | 26 | .icon-qq:before { content: "\e609"; } 27 | 28 | .icon-frown:before { content: "\e77e"; } 29 | 30 | .icon-meh:before { content: "\e780"; } 31 | 32 | .icon-smile:before { content: "\e783"; } 33 | 34 | .icon-man:before { content: "\e7e2"; } 35 | 36 | .icon-woman:before { content: "\e7e5"; } 37 | 38 | -------------------------------------------------------------------------------- /src/view/content/student/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 搜索 9 | 添加 10 | 11 | 12 | 13 | 15 | 16 | 修改 17 | 删除 18 | 19 | 20 | 21 | 23 | this.save(e)"/> 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/view/content/article/modify.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 基本信息 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | temp.thumbnail=e" /> 16 | 17 | 18 | this.temp.content=e"/> 19 | 20 | 21 | 22 | 23 | 24 | 其他信息 25 | 26 | 27 | {{item.name}} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 提交 36 | 重置 37 | 38 | 39 | 40 | 102 | -------------------------------------------------------------------------------- /src/view/sys/log/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 修改 7 | 删除 8 | 9 | 10 | 11 | 13 | this.save(e)"/> 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/main/components/side-menu/side-menu.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{ showTitle(item.children[0]) }} 10 | 11 | 12 | 13 | 14 | {{ showTitle(item) }} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 114 | 117 | -------------------------------------------------------------------------------- /src/components/count-to/count-to.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ init }}{{ unitText }} 5 | 6 | 7 | 8 | 9 | 175 | -------------------------------------------------------------------------------- /src/components/upload/upload.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 103 | 141 | -------------------------------------------------------------------------------- /src/view/dashboard/home.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 最高¥2000 9 | 红包限量 10 | 11 | 云产品通用,可叠加官网常规优惠使用 | 限时领取 12 | 13 | 立即领取 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{ infor.title }} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 96 | 97 | 169 | -------------------------------------------------------------------------------- /src/view/content/articletype/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 搜索 9 | 添加 10 | 11 | 12 | onMenuClick(e)" :options="[{key:-1,name:'删除'}]"/> 13 | 14 | 15 | 16 | this.selectedRowKeys = e.map(item=>{return item.id})"> 17 | 18 | 19 | {{ row.name }} 20 | 21 | 22 | 修改 23 | 保存 24 | 删除 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 134 | -------------------------------------------------------------------------------- /src/store/module/user.js: -------------------------------------------------------------------------------- 1 | import { 2 | login, 3 | getUserInfo, 4 | getMessage, 5 | getContentByMsgId, 6 | hasRead, 7 | removeReaded, 8 | restoreTrash 9 | } from '@/api/user' 10 | import { setToken, localSave } from '@/libs/util' 11 | import { initRouter } from '@/libs/router-util' // 引入动态菜单渲染 12 | 13 | export default { 14 | state: { 15 | userName: '', 16 | avatorImgPath: '', 17 | access: '', 18 | hasGetInfo: false, 19 | unreadCount: 0, 20 | messageUnreadList: [], 21 | messageReadedList: [], 22 | messageTrashList: [], 23 | messageContentStore: {} 24 | }, 25 | mutations: { 26 | setAvator (state, avatorPath) { 27 | state.avatorImgPath = avatorPath 28 | }, 29 | setUserName (state, name) { 30 | state.userName = name 31 | }, 32 | setAccess (state, access) { 33 | state.access = access 34 | }, 35 | setHasGetInfo (state, status) { 36 | state.hasGetInfo = status 37 | }, 38 | setMessageCount (state, count) { 39 | state.unreadCount = count 40 | }, 41 | setMessageUnreadList (state, list) { 42 | state.messageUnreadList = list 43 | }, 44 | setMessageReadedList (state, list) { 45 | state.messageReadedList = list 46 | }, 47 | setMessageTrashList (state, list) { 48 | state.messageTrashList = list 49 | }, 50 | updateMessageContentStore (state, { msg_id, content }) { 51 | state.messageContentStore[msg_id] = content 52 | }, 53 | moveMsg (state, { from, to, msg_id }) { 54 | const index = state[from].findIndex(_ => _.msg_id === msg_id) 55 | const msgItem = state[from].splice(index, 1)[0] 56 | msgItem.loading = false 57 | state[to].unshift(msgItem) 58 | } 59 | }, 60 | getters: { 61 | messageUnreadCount: state => state.messageUnreadList.length, 62 | messageReadedCount: state => state.messageReadedList.length, 63 | messageTrashCount: state => state.messageTrashList.length 64 | }, 65 | actions: { 66 | // 登录 67 | async handleLogin (object, { username, password }) { 68 | username = username.trim() 69 | const data = await login({ username, password }) 70 | if (data) { 71 | setToken(data.token) 72 | initRouter() 73 | return true 74 | } 75 | }, 76 | // 退出登录 77 | async handleLogOut ({ state, commit }) { 78 | // await logout() 79 | setToken('') 80 | commit('setAccess', []) 81 | localSave('dynamicRouter', []) // 清空本地路由 82 | localSave('tagNaveList', []) // 清空localStorage中的tagNaveList记录 83 | }, 84 | // 获取用户相关信息 85 | async getUserInfo ({ state, commit }) { 86 | const data = await getUserInfo() 87 | commit('setAvator', data.avator) 88 | commit('setUserName', data.nickName) 89 | commit('setAccess', data.access) 90 | commit('setHasGetInfo', true) 91 | return data 92 | }, 93 | // 此方法用来获取未读消息条数,接口只返回数值,不返回消息列表 94 | getUnreadMessageCount ({ state, commit }) { 95 | commit('setMessageCount', 0) 96 | // getUnreadCount().then(res => { 97 | // const { data } = res 98 | // commit('setMessageCount', data) 99 | // }) 100 | }, 101 | // 获取消息列表,其中包含未读、已读、回收站三个列表 102 | getMessageList ({ state, commit }) { 103 | return new Promise((resolve, reject) => { 104 | getMessage().then(res => { 105 | const { unread, readed, trash } = res.data 106 | commit('setMessageUnreadList', unread.sort((a, b) => new Date(b.create_time) - new Date(a.create_time))) 107 | commit('setMessageReadedList', readed.map(_ => { 108 | _.loading = false 109 | return _ 110 | }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time))) 111 | commit('setMessageTrashList', trash.map(_ => { 112 | _.loading = false 113 | return _ 114 | }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time))) 115 | resolve() 116 | }).catch(error => { 117 | reject(error) 118 | }) 119 | }) 120 | }, 121 | // 根据当前点击的消息的id获取内容 122 | getContentByMsgId ({ state, commit }, { msg_id }) { 123 | return new Promise((resolve, reject) => { 124 | let contentItem = state.messageContentStore[msg_id] 125 | if (contentItem) { 126 | resolve(contentItem) 127 | } else { 128 | getContentByMsgId(msg_id).then(res => { 129 | const content = res.data 130 | commit('updateMessageContentStore', { msg_id, content }) 131 | resolve(content) 132 | }) 133 | } 134 | }) 135 | }, 136 | // 把一个未读消息标记为已读 137 | hasRead ({ state, commit }, { msg_id }) { 138 | return new Promise((resolve, reject) => { 139 | hasRead(msg_id).then(() => { 140 | commit('moveMsg', { 141 | from: 'messageUnreadList', 142 | to: 'messageReadedList', 143 | msg_id 144 | }) 145 | commit('setMessageCount', state.unreadCount - 1) 146 | resolve() 147 | }).catch(error => { 148 | reject(error) 149 | }) 150 | }) 151 | }, 152 | // 删除一个已读消息到回收站 153 | removeReaded ({ commit }, { msg_id }) { 154 | return new Promise((resolve, reject) => { 155 | removeReaded(msg_id).then(() => { 156 | commit('moveMsg', { 157 | from: 'messageReadedList', 158 | to: 'messageTrashList', 159 | msg_id 160 | }) 161 | resolve() 162 | }).catch(error => { 163 | reject(error) 164 | }) 165 | }) 166 | }, 167 | // 还原一个已删除消息到已读消息 168 | restoreTrash ({ commit }, { msg_id }) { 169 | return new Promise((resolve, reject) => { 170 | restoreTrash(msg_id).then(() => { 171 | commit('moveMsg', { 172 | from: 'messageTrashList', 173 | to: 'messageReadedList', 174 | msg_id 175 | }) 176 | resolve() 177 | }).catch(error => { 178 | reject(error) 179 | }) 180 | }) 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/components/main/main.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 185 | -------------------------------------------------------------------------------- /src/view/message/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 未读消息 8 | 9 | 10 | 已读消息 11 | 12 | 13 | 回收站 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | {{ item.title }} 28 | 29 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {{ showingMsgItem.title }} 47 | {{ showingMsgItem.create_time }} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 136 | 137 | 195 | -------------------------------------------------------------------------------- /src/libs/tools.js: -------------------------------------------------------------------------------- 1 | export const forEach = (arr, fn) => { 2 | if (!arr.length || !fn) return 3 | let i = -1 4 | let len = arr.length 5 | while (++i < len) { 6 | let item = arr[i] 7 | fn(item, i, arr) 8 | } 9 | } 10 | 11 | /** 12 | * @param {Array} arr1 13 | * @param {Array} arr2 14 | * @description 得到两个数组的交集, 两个数组的元素为数值或字符串 15 | */ 16 | export const getIntersection = (arr1, arr2) => { 17 | let len = Math.min(arr1.length, arr2.length) 18 | let i = -1 19 | let res = [] 20 | while (++i < len) { 21 | const item = arr2[i] 22 | if (arr1.indexOf(item) > -1) res.push(item) 23 | } 24 | return res 25 | } 26 | 27 | /** 28 | * @param {Array} arr1 29 | * @param {Array} arr2 30 | * @description 得到两个数组的并集, 两个数组的元素为数值或字符串 31 | */ 32 | export const getUnion = (arr1, arr2) => { 33 | return Array.from(new Set([...arr1, ...arr2])) 34 | } 35 | 36 | /** 37 | * @param {Array} target 目标数组 38 | * @param {Array} arr 需要查询的数组 39 | * @description 判断要查询的数组是否至少有一个元素包含在目标数组中 40 | */ 41 | export const hasOneOf = (targetarr, arr) => { 42 | return targetarr.some(_ => arr.indexOf(_) > -1) 43 | } 44 | 45 | /** 46 | * @param {String|Number} value 要验证的字符串或数值 47 | * @param {*} validList 用来验证的列表 48 | */ 49 | export function oneOf (value, validList) { 50 | for (let i = 0; i < validList.length; i++) { 51 | if (value === validList[i]) { 52 | return true 53 | } 54 | } 55 | return false 56 | } 57 | 58 | /** 59 | * @param {Number} timeStamp 判断时间戳格式是否是毫秒 60 | * @returns {Boolean} 61 | */ 62 | const isMillisecond = timeStamp => { 63 | const timeStr = String(timeStamp) 64 | return timeStr.length > 10 65 | } 66 | 67 | /** 68 | * @param {Number} timeStamp 传入的时间戳 69 | * @param {Number} currentTime 当前时间时间戳 70 | * @returns {Boolean} 传入的时间戳是否早于当前时间戳 71 | */ 72 | const isEarly = (timeStamp, currentTime) => { 73 | return timeStamp < currentTime 74 | } 75 | 76 | /** 77 | * @param {Number} num 数值 78 | * @returns {String} 处理后的字符串 79 | * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0 80 | */ 81 | const getHandledValue = num => { 82 | return num < 10 ? '0' + num : num 83 | } 84 | 85 | /** 86 | * @param {Number} timeStamp 传入的时间戳 87 | * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间 88 | */ 89 | const getDate = (timeStamp, startType) => { 90 | const d = new Date(timeStamp * 1000) 91 | const year = d.getFullYear() 92 | const month = getHandledValue(d.getMonth() + 1) 93 | const date = getHandledValue(d.getDate()) 94 | const hours = getHandledValue(d.getHours()) 95 | const minutes = getHandledValue(d.getMinutes()) 96 | const second = getHandledValue(d.getSeconds()) 97 | let resStr = '' 98 | if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second 99 | else resStr = month + '-' + date + ' ' + hours + ':' + minutes 100 | return resStr 101 | } 102 | 103 | /** 104 | * @param {String|Number} timeStamp 时间戳 105 | * @returns {String} 相对时间字符串 106 | */ 107 | export const getRelativeTime = timeStamp => { 108 | // 判断当前传入的时间戳是秒格式还是毫秒 109 | const IS_MILLISECOND = isMillisecond(timeStamp) 110 | // 如果是毫秒格式则转为秒格式 111 | if (IS_MILLISECOND) Math.floor(timeStamp /= 1000) 112 | // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型 113 | timeStamp = Number(timeStamp) 114 | // 获取当前时间时间戳 115 | const currentTime = Math.floor(Date.parse(new Date()) / 1000) 116 | // 判断传入时间戳是否早于当前时间戳 117 | const IS_EARLY = isEarly(timeStamp, currentTime) 118 | // 获取两个时间戳差值 119 | let diff = currentTime - timeStamp 120 | // 如果IS_EARLY为false则差值取反 121 | if (!IS_EARLY) diff = -diff 122 | let resStr = '' 123 | const dirStr = IS_EARLY ? '前' : '后' 124 | // 少于等于59秒 125 | if (diff <= 59) resStr = diff + '秒' + dirStr 126 | // 多于59秒,少于等于59分钟59秒 127 | else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr 128 | // 多于59分钟59秒,少于等于23小时59分钟59秒 129 | else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr 130 | // 多于23小时59分钟59秒,少于等于29天59分钟59秒 131 | else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr 132 | // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前 133 | else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp) 134 | else resStr = getDate(timeStamp, 'year') 135 | return resStr 136 | } 137 | 138 | /** 139 | * @returns {String} 当前浏览器名称 140 | */ 141 | export const getExplorer = () => { 142 | const ua = window.navigator.userAgent 143 | const isExplorer = (exp) => { 144 | return ua.indexOf(exp) > -1 145 | } 146 | if (isExplorer('MSIE')) return 'IE' 147 | else if (isExplorer('Firefox')) return 'Firefox' 148 | else if (isExplorer('Chrome')) return 'Chrome' 149 | else if (isExplorer('Opera')) return 'Opera' 150 | else if (isExplorer('Safari')) return 'Safari' 151 | } 152 | 153 | /** 154 | * @description 绑定事件 on(element, event, handler) 155 | */ 156 | export const on = (function () { 157 | if (document.addEventListener) { 158 | return function (element, event, handler) { 159 | if (element && event && handler) { 160 | element.addEventListener(event, handler, false) 161 | } 162 | } 163 | } else { 164 | return function (element, event, handler) { 165 | if (element && event && handler) { 166 | element.attachEvent('on' + event, handler) 167 | } 168 | } 169 | } 170 | })() 171 | 172 | /** 173 | * @description 解绑事件 off(element, event, handler) 174 | */ 175 | export const off = (function () { 176 | if (document.removeEventListener) { 177 | return function (element, event, handler) { 178 | if (element && event) { 179 | element.removeEventListener(event, handler, false) 180 | } 181 | } 182 | } else { 183 | return function (element, event, handler) { 184 | if (element && event) { 185 | element.detachEvent('on' + event, handler) 186 | } 187 | } 188 | } 189 | })() 190 | 191 | /** 192 | * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性 193 | * 如果没有传入key这个参数,则判断obj对象是否有键值对 194 | */ 195 | export const hasKey = (obj, key) => { 196 | if (key) return key in obj 197 | else { 198 | let keysArr = Object.keys(obj) 199 | return keysArr.length 200 | } 201 | } 202 | 203 | /** 204 | * @param {*} obj1 对象 205 | * @param {*} obj2 对象 206 | * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串 207 | */ 208 | export const objEqual = (obj1, obj2) => { 209 | const keysArr1 = Object.keys(obj1) 210 | const keysArr2 = Object.keys(obj2) 211 | if (keysArr1.length !== keysArr2.length) return false 212 | else if (keysArr1.length === 0 && keysArr2.length === 0) return true 213 | /* eslint-disable-next-line */ 214 | else return !keysArr1.some(key => obj1[key] != obj2[key]) 215 | } 216 | 217 | export const lazyLoadingCop = file => { 218 | try { 219 | return require('@/view/' + file + '.vue').default 220 | } catch (err) {} 221 | } 222 | -------------------------------------------------------------------------------- /src/components/main/components/tags-nav/tags-nav.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 关闭所有 10 | 关闭其他 11 | 12 | 13 | 14 | 15 | {{item}} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{ showTitleInside(item) }} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 206 | 207 | 210 | -------------------------------------------------------------------------------- /src/view/tool/autocode.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 基本信息 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 否 21 | 是 22 | 23 | 24 | 25 | 26 | 否 27 | 是 28 | 29 | 30 | 31 | 32 | 否 33 | 是 34 | 35 | 36 | 37 | 38 | 39 | 字段信息 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 文字 52 | 数字 53 | 单选 54 | 多选 55 | 下拉 56 | 日期 57 | 图片 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 否 67 | 是 68 | 69 | 70 | 71 | 72 | 主键 73 | 搜索 74 | 普通 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 生成到本地 83 | 生成首页 84 | 生成modal 85 | 重置 86 | 87 | 88 | 89 | 90 | 复制代码 91 | 92 | 93 | 94 | 95 | 168 | 171 | -------------------------------------------------------------------------------- /src/assets/icons/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/components/tables/tables.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ item.title }} 6 | 7 | 8 | 搜索 9 | 10 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {{ item.title }} 44 | 45 | 46 | 搜索 47 | 48 | 49 | 50 | 51 | 52 | 278 | -------------------------------------------------------------------------------- /src/libs/util.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | // cookie保存的天数 3 | import config from '@/config' 4 | import { forEach, hasOneOf, objEqual } from '@/libs/tools' 5 | const { title, cookieExpires, useI18n } = config 6 | 7 | export const TOKEN_KEY = 'token' 8 | 9 | export const setToken = (token) => { 10 | Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 }) 11 | } 12 | 13 | export const getToken = () => { 14 | const token = Cookies.get(TOKEN_KEY) 15 | if (token) return token 16 | else return false 17 | } 18 | 19 | export const hasChild = (item) => { 20 | return item.children && item.children.length !== 0 21 | } 22 | 23 | const showThisMenuEle = (item, access) => { 24 | if (item.meta && item.meta.access && item.meta.access.length) { 25 | if (hasOneOf(item.meta.access, access)) return true 26 | else return false 27 | } else return true 28 | } 29 | /** 30 | * @param {Array} list 通过路由列表得到菜单列表 31 | * @returns {Array} 32 | */ 33 | export const getMenuByRouter = (list, access) => { 34 | let res = [] 35 | forEach(list, item => { 36 | if (!item.meta || (item.meta && !item.meta.hideInMenu)) { 37 | let obj = { 38 | icon: (item.meta && item.meta.icon) || '', 39 | name: item.name, 40 | meta: item.meta 41 | } 42 | if ( 43 | (hasChild(item) || (item.meta && item.meta.showAlways)) && 44 | showThisMenuEle(item, access) 45 | ) { 46 | obj.children = getMenuByRouter(item.children, access) 47 | } 48 | if (item.meta && item.meta.href) obj.href = item.meta.href 49 | if (showThisMenuEle(item, access)) res.push(obj) 50 | } 51 | }) 52 | return res 53 | } 54 | 55 | /** 56 | * @param {Array} routeMetched 当前路由metched 57 | * @returns {Array} 58 | */ 59 | export const getBreadCrumbList = (route, homeRoute) => { 60 | let homeItem = { ...homeRoute, icon: homeRoute.meta.icon } 61 | let routeMetched = route.matched 62 | if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem] 63 | let res = routeMetched.filter(item => { 64 | return item.meta === undefined || !item.meta.hideInBread 65 | }).map(item => { 66 | let meta = { ...item.meta } 67 | if (meta.title && typeof meta.title === 'function') { 68 | meta.__titleIsFunction__ = true 69 | meta.title = meta.title(route) 70 | } 71 | let obj = { 72 | icon: (item.meta && item.meta.icon) || '', 73 | name: item.name, 74 | meta: meta 75 | } 76 | return obj 77 | }) 78 | res = res.filter(item => { 79 | return !item.meta.hideInMenu 80 | }) 81 | return [{ ...homeItem, to: homeRoute.path }, ...res] 82 | } 83 | 84 | export const getRouteTitleHandled = (route) => { 85 | let router = { ...route } 86 | let meta = { ...route.meta } 87 | let title = '' 88 | if (meta.title) { 89 | if (typeof meta.title === 'function') { 90 | meta.__titleIsFunction__ = true 91 | title = meta.title(router) 92 | } else title = meta.title 93 | } 94 | meta.title = title 95 | router.meta = meta 96 | return router 97 | } 98 | // 根据菜单显示菜单文字 99 | export const showTitle = (item, vm) => { 100 | let { title, __titleIsFunction__ } = item.meta 101 | if (!title) return 102 | if (useI18n) { 103 | if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim()))) 104 | else if (__titleIsFunction__) title = item.meta.title 105 | else title = vm.$t(item.name) 106 | } else title = (item.meta && item.meta.title) || item.name 107 | return title 108 | } 109 | 110 | /** 111 | * @description 本地存储和获取标签导航列表 112 | */ 113 | export const setTagNavListInLocalstorage = list => { 114 | localStorage.tagNaveList = JSON.stringify(list) 115 | } 116 | /** 117 | * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项 118 | */ 119 | export const getTagNavListFromLocalstorage = () => { 120 | const list = localStorage.tagNaveList 121 | return list ? JSON.parse(list) : [] 122 | } 123 | 124 | /** 125 | * @param {Array} routers 路由列表数组 126 | * @description 用于找到路由列表中name为home的对象 127 | */ 128 | export const getHomeRoute = (routers, homeName = 'home') => { 129 | let i = -1 130 | let len = routers.length 131 | let homeRoute = {} 132 | while (++i < len) { 133 | let item = routers[i] 134 | if (item.children && item.children.length) { 135 | let res = getHomeRoute(item.children, homeName) 136 | if (res.name) return res 137 | } else { 138 | if (item.name === homeName) homeRoute = item 139 | } 140 | } 141 | return homeRoute 142 | } 143 | 144 | /** 145 | * @param {*} list 现有标签导航列表 146 | * @param {*} newRoute 新添加的路由原信息对象 147 | * @description 如果该newRoute已经存在则不再添加 148 | */ 149 | export const getNewTagList = (list, newRoute) => { 150 | const { name, path, meta } = newRoute 151 | let newList = [...list] 152 | if (newList.findIndex(item => item.name === name) >= 0) return newList 153 | else newList.push({ name, path, meta }) 154 | return newList 155 | } 156 | 157 | /** 158 | * @param {*} access 用户权限数组,如 ['super_admin', 'admin'] 159 | * @param {*} route 路由列表 160 | */ 161 | const hasAccess = (access, route) => { 162 | if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access) 163 | else return true 164 | } 165 | 166 | /** 167 | * 权鉴 168 | * @param {*} name 即将跳转的路由name 169 | * @param {*} access 用户权限数组 170 | * @param {*} routes 路由列表 171 | * @description 用户是否可跳转到该页 172 | */ 173 | export const canTurnTo = (name, access, routes) => { 174 | const routePermissionJudge = (list) => { 175 | return list.some(item => { 176 | if (item.children && item.children.length) { 177 | return routePermissionJudge(item.children) 178 | } else if (item.name === name) { 179 | return hasAccess(access, item) 180 | } 181 | }) 182 | } 183 | 184 | return routePermissionJudge(routes) 185 | } 186 | 187 | /** 188 | * @param {String} url 189 | * @description 从URL中解析参数 190 | */ 191 | export const getParams = url => { 192 | const keyValueArr = url.split('?')[1].split('&') 193 | let paramObj = {} 194 | keyValueArr.forEach(item => { 195 | const keyValue = item.split('=') 196 | paramObj[keyValue[0]] = keyValue[1] 197 | }) 198 | return paramObj 199 | } 200 | 201 | /** 202 | * @param {Array} list 标签列表 203 | * @param {String} name 当前关闭的标签的name 204 | */ 205 | export const getNextRoute = (list, route) => { 206 | let res = {} 207 | if (list.length === 2) { 208 | res = getHomeRoute(list) 209 | } else { 210 | const index = list.findIndex(item => routeEqual(item, route)) 211 | if (index === list.length - 1) res = list[list.length - 2] 212 | else res = list[index + 1] 213 | } 214 | return res 215 | } 216 | 217 | /** 218 | * @param {Number} times 回调函数需要执行的次数 219 | * @param {Function} callback 回调函数 220 | */ 221 | export const doCustomTimes = (times, callback) => { 222 | let i = -1 223 | while (++i < times) { 224 | callback(i) 225 | } 226 | } 227 | 228 | /** 229 | * @param {Object} file 从上传组件得到的文件对象 230 | * @returns {Promise} resolve参数是解析后的二维数组 231 | * @description 从Csv文件中解析出表格,解析成二维数组 232 | */ 233 | export const getArrayFromFile = (file) => { 234 | let nameSplit = file.name.split('.') 235 | let format = nameSplit[nameSplit.length - 1] 236 | return new Promise((resolve, reject) => { 237 | let reader = new FileReader() 238 | reader.readAsText(file) // 以文本格式读取 239 | let arr = [] 240 | reader.onload = function (evt) { 241 | let data = evt.target.result // 读到的数据 242 | let pasteData = data.trim() 243 | arr = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => { 244 | return row.split('\t') 245 | }).map(item => { 246 | return item[0].split(',') 247 | }) 248 | if (format === 'csv') resolve(arr) 249 | else reject(new Error('[Format Error]:你上传的不是Csv文件')) 250 | } 251 | }) 252 | } 253 | 254 | /** 255 | * @param {Array} array 表格数据二维数组 256 | * @returns {Object} { columns, tableData } 257 | * @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在iView的表格中展示数据 258 | */ 259 | export const getTableDataFromArray = (array) => { 260 | let columns = [] 261 | let tableData = [] 262 | if (array.length > 1) { 263 | let titles = array.shift() 264 | columns = titles.map(item => { 265 | return { 266 | title: item, 267 | key: item 268 | } 269 | }) 270 | tableData = array.map(item => { 271 | let res = {} 272 | item.forEach((col, i) => { 273 | res[titles[i]] = col 274 | }) 275 | return res 276 | }) 277 | } 278 | return { 279 | columns, 280 | tableData 281 | } 282 | } 283 | 284 | export const findNodeUpper = (ele, tag) => { 285 | if (ele.parentNode) { 286 | if (ele.parentNode.tagName === tag.toUpperCase()) { 287 | return ele.parentNode 288 | } else { 289 | return findNodeUpper(ele.parentNode, tag) 290 | } 291 | } 292 | } 293 | 294 | export const findNodeUpperByClasses = (ele, classes) => { 295 | let parentNode = ele.parentNode 296 | if (parentNode) { 297 | let classList = parentNode.classList 298 | if (classList && classes.every(className => classList.contains(className))) { 299 | return parentNode 300 | } else { 301 | return findNodeUpperByClasses(parentNode, classes) 302 | } 303 | } 304 | } 305 | 306 | export const findNodeDownward = (ele, tag) => { 307 | const tagName = tag.toUpperCase() 308 | if (ele.childNodes.length) { 309 | let i = -1 310 | let len = ele.childNodes.length 311 | while (++i < len) { 312 | let child = ele.childNodes[i] 313 | if (child.tagName === tagName) return child 314 | else return findNodeDownward(child, tag) 315 | } 316 | } 317 | } 318 | 319 | export const showByAccess = (access, canViewAccess) => { 320 | return hasOneOf(canViewAccess, access) 321 | } 322 | 323 | /** 324 | * @description 根据name/params/query判断两个路由对象是否相等 325 | * @param {*} route1 路由对象 326 | * @param {*} route2 路由对象 327 | */ 328 | export const routeEqual = (route1, route2) => { 329 | const params1 = route1.params || {} 330 | const params2 = route2.params || {} 331 | const query1 = route1.query || {} 332 | const query2 = route2.query || {} 333 | return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2) 334 | } 335 | 336 | /** 337 | * 判断打开的标签列表里是否已存在这个新添加的路由对象 338 | */ 339 | export const routeHasExist = (tagNavList, routeItem) => { 340 | let len = tagNavList.length 341 | let res = false 342 | doCustomTimes(len, (index) => { 343 | if (routeEqual(tagNavList[index], routeItem)) res = true 344 | }) 345 | return res 346 | } 347 | 348 | export const localSave = (key, value) => { 349 | localStorage.setItem(key, value) 350 | } 351 | 352 | export const localRead = (key) => { 353 | return localStorage.getItem(key) || '' 354 | } 355 | 356 | // scrollTop animation 357 | export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => { 358 | if (!window.requestAnimationFrame) { 359 | window.requestAnimationFrame = ( 360 | window.webkitRequestAnimationFrame || 361 | window.mozRequestAnimationFrame || 362 | window.msRequestAnimationFrame || 363 | function (callback) { 364 | return window.setTimeout(callback, 1000 / 60) 365 | } 366 | ) 367 | } 368 | const difference = Math.abs(from - to) 369 | const step = Math.ceil(difference / duration * 50) 370 | 371 | const scroll = (start, end, step) => { 372 | if (start === end) { 373 | endCallback && endCallback() 374 | return 375 | } 376 | 377 | let d = (start + step > end) ? end : start + step 378 | if (start > end) { 379 | d = (start - step < end) ? end : start - step 380 | } 381 | 382 | if (el === window) { 383 | window.scrollTo(d, d) 384 | } else { 385 | el.scrollTop = d 386 | } 387 | window.requestAnimationFrame(() => scroll(d, end, step)) 388 | } 389 | scroll(from, to, step) 390 | } 391 | /** 392 | * @description 根据当前跳转的路由设置显示在浏览器标签的title 393 | * @param {Object} routeItem 路由对象 394 | * @param {Object} vm Vue实例 395 | * @param {*} appName app名称 396 | */ 397 | export const setTitle = (routeItem, vm, appName) => { 398 | if (!appName) appName = title 399 | const handledRoute = getRouteTitleHandled(routeItem) 400 | const pageTitle = showTitle(handledRoute, vm) 401 | const resTitle = pageTitle ? `${appName} - ${pageTitle}` : appName 402 | window.document.title = resTitle 403 | } 404 | export const formatDay = (date) => { 405 | date = new Date(date) 406 | const year = date.getFullYear() 407 | const month = date.getMonth() + 1 408 | const day = date.getDate() 409 | return [year, month, day].map(formatNumber).join('-') 410 | } 411 | export const formatDayTime = (date) => { 412 | date = new Date(date) 413 | const year = date.getFullYear() 414 | const month = date.getMonth() + 1 415 | const day = date.getDate() 416 | const hour = date.getHours() 417 | const minute = date.getMinutes() 418 | const second = date.getSeconds() 419 | return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':') 420 | } 421 | function formatNumber (n) { 422 | n = n.toString() 423 | return n[1] ? n : '0' + n 424 | } 425 | -------------------------------------------------------------------------------- /src/components/charts/theme.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "color": [ 4 | "#2d8cf0", 5 | "#19be6b", 6 | "#ff9900", 7 | "#E46CBB", 8 | "#9A66E4", 9 | "#ed3f14" 10 | ], 11 | "backgroundColor": "rgba(0,0,0,0)", 12 | "textStyle": {}, 13 | "title": { 14 | "textStyle": { 15 | "color": "#516b91" 16 | }, 17 | "subtextStyle": { 18 | "color": "#93b7e3" 19 | } 20 | }, 21 | "line": { 22 | "itemStyle": { 23 | "normal": { 24 | "borderWidth": "2" 25 | } 26 | }, 27 | "lineStyle": { 28 | "normal": { 29 | "width": "2" 30 | } 31 | }, 32 | "symbolSize": "6", 33 | "symbol": "emptyCircle", 34 | "smooth": true 35 | }, 36 | "radar": { 37 | "itemStyle": { 38 | "normal": { 39 | "borderWidth": "2" 40 | } 41 | }, 42 | "lineStyle": { 43 | "normal": { 44 | "width": "2" 45 | } 46 | }, 47 | "symbolSize": "6", 48 | "symbol": "emptyCircle", 49 | "smooth": true 50 | }, 51 | "bar": { 52 | "itemStyle": { 53 | "normal": { 54 | "barBorderWidth": 0, 55 | "barBorderColor": "#ccc" 56 | }, 57 | "emphasis": { 58 | "barBorderWidth": 0, 59 | "barBorderColor": "#ccc" 60 | } 61 | } 62 | }, 63 | "pie": { 64 | "itemStyle": { 65 | "normal": { 66 | "borderWidth": 0, 67 | "borderColor": "#ccc" 68 | }, 69 | "emphasis": { 70 | "borderWidth": 0, 71 | "borderColor": "#ccc" 72 | } 73 | } 74 | }, 75 | "scatter": { 76 | "itemStyle": { 77 | "normal": { 78 | "borderWidth": 0, 79 | "borderColor": "#ccc" 80 | }, 81 | "emphasis": { 82 | "borderWidth": 0, 83 | "borderColor": "#ccc" 84 | } 85 | } 86 | }, 87 | "boxplot": { 88 | "itemStyle": { 89 | "normal": { 90 | "borderWidth": 0, 91 | "borderColor": "#ccc" 92 | }, 93 | "emphasis": { 94 | "borderWidth": 0, 95 | "borderColor": "#ccc" 96 | } 97 | } 98 | }, 99 | "parallel": { 100 | "itemStyle": { 101 | "normal": { 102 | "borderWidth": 0, 103 | "borderColor": "#ccc" 104 | }, 105 | "emphasis": { 106 | "borderWidth": 0, 107 | "borderColor": "#ccc" 108 | } 109 | } 110 | }, 111 | "sankey": { 112 | "itemStyle": { 113 | "normal": { 114 | "borderWidth": 0, 115 | "borderColor": "#ccc" 116 | }, 117 | "emphasis": { 118 | "borderWidth": 0, 119 | "borderColor": "#ccc" 120 | } 121 | } 122 | }, 123 | "funnel": { 124 | "itemStyle": { 125 | "normal": { 126 | "borderWidth": 0, 127 | "borderColor": "#ccc" 128 | }, 129 | "emphasis": { 130 | "borderWidth": 0, 131 | "borderColor": "#ccc" 132 | } 133 | } 134 | }, 135 | "gauge": { 136 | "itemStyle": { 137 | "normal": { 138 | "borderWidth": 0, 139 | "borderColor": "#ccc" 140 | }, 141 | "emphasis": { 142 | "borderWidth": 0, 143 | "borderColor": "#ccc" 144 | } 145 | } 146 | }, 147 | "candlestick": { 148 | "itemStyle": { 149 | "normal": { 150 | "color": "#edafda", 151 | "color0": "transparent", 152 | "borderColor": "#d680bc", 153 | "borderColor0": "#8fd3e8", 154 | "borderWidth": "2" 155 | } 156 | } 157 | }, 158 | "graph": { 159 | "itemStyle": { 160 | "normal": { 161 | "borderWidth": 0, 162 | "borderColor": "#ccc" 163 | } 164 | }, 165 | "lineStyle": { 166 | "normal": { 167 | "width": 1, 168 | "color": "#aaa" 169 | } 170 | }, 171 | "symbolSize": "6", 172 | "symbol": "emptyCircle", 173 | "smooth": true, 174 | "color": [ 175 | "#2d8cf0", 176 | "#19be6b", 177 | "#f5ae4a", 178 | "#9189d5", 179 | "#56cae2", 180 | "#cbb0e3" 181 | ], 182 | "label": { 183 | "normal": { 184 | "textStyle": { 185 | "color": "#eee" 186 | } 187 | } 188 | } 189 | }, 190 | "map": { 191 | "itemStyle": { 192 | "normal": { 193 | "areaColor": "#f3f3f3", 194 | "borderColor": "#516b91", 195 | "borderWidth": 0.5 196 | }, 197 | "emphasis": { 198 | "areaColor": "rgba(165,231,240,1)", 199 | "borderColor": "#516b91", 200 | "borderWidth": 1 201 | } 202 | }, 203 | "label": { 204 | "normal": { 205 | "textStyle": { 206 | "color": "#000" 207 | } 208 | }, 209 | "emphasis": { 210 | "textStyle": { 211 | "color": "rgb(81,107,145)" 212 | } 213 | } 214 | } 215 | }, 216 | "geo": { 217 | "itemStyle": { 218 | "normal": { 219 | "areaColor": "#f3f3f3", 220 | "borderColor": "#516b91", 221 | "borderWidth": 0.5 222 | }, 223 | "emphasis": { 224 | "areaColor": "rgba(165,231,240,1)", 225 | "borderColor": "#516b91", 226 | "borderWidth": 1 227 | } 228 | }, 229 | "label": { 230 | "normal": { 231 | "textStyle": { 232 | "color": "#000" 233 | } 234 | }, 235 | "emphasis": { 236 | "textStyle": { 237 | "color": "rgb(81,107,145)" 238 | } 239 | } 240 | } 241 | }, 242 | "categoryAxis": { 243 | "axisLine": { 244 | "show": true, 245 | "lineStyle": { 246 | "color": "#cccccc" 247 | } 248 | }, 249 | "axisTick": { 250 | "show": false, 251 | "lineStyle": { 252 | "color": "#333" 253 | } 254 | }, 255 | "axisLabel": { 256 | "show": true, 257 | "textStyle": { 258 | "color": "#999999" 259 | } 260 | }, 261 | "splitLine": { 262 | "show": true, 263 | "lineStyle": { 264 | "color": [ 265 | "#eeeeee" 266 | ] 267 | } 268 | }, 269 | "splitArea": { 270 | "show": false, 271 | "areaStyle": { 272 | "color": [ 273 | "rgba(250,250,250,0.05)", 274 | "rgba(200,200,200,0.02)" 275 | ] 276 | } 277 | } 278 | }, 279 | "valueAxis": { 280 | "axisLine": { 281 | "show": true, 282 | "lineStyle": { 283 | "color": "#cccccc" 284 | } 285 | }, 286 | "axisTick": { 287 | "show": false, 288 | "lineStyle": { 289 | "color": "#333" 290 | } 291 | }, 292 | "axisLabel": { 293 | "show": true, 294 | "textStyle": { 295 | "color": "#999999" 296 | } 297 | }, 298 | "splitLine": { 299 | "show": true, 300 | "lineStyle": { 301 | "color": [ 302 | "#eeeeee" 303 | ] 304 | } 305 | }, 306 | "splitArea": { 307 | "show": false, 308 | "areaStyle": { 309 | "color": [ 310 | "rgba(250,250,250,0.05)", 311 | "rgba(200,200,200,0.02)" 312 | ] 313 | } 314 | } 315 | }, 316 | "logAxis": { 317 | "axisLine": { 318 | "show": true, 319 | "lineStyle": { 320 | "color": "#cccccc" 321 | } 322 | }, 323 | "axisTick": { 324 | "show": false, 325 | "lineStyle": { 326 | "color": "#333" 327 | } 328 | }, 329 | "axisLabel": { 330 | "show": true, 331 | "textStyle": { 332 | "color": "#999999" 333 | } 334 | }, 335 | "splitLine": { 336 | "show": true, 337 | "lineStyle": { 338 | "color": [ 339 | "#eeeeee" 340 | ] 341 | } 342 | }, 343 | "splitArea": { 344 | "show": false, 345 | "areaStyle": { 346 | "color": [ 347 | "rgba(250,250,250,0.05)", 348 | "rgba(200,200,200,0.02)" 349 | ] 350 | } 351 | } 352 | }, 353 | "timeAxis": { 354 | "axisLine": { 355 | "show": true, 356 | "lineStyle": { 357 | "color": "#cccccc" 358 | } 359 | }, 360 | "axisTick": { 361 | "show": false, 362 | "lineStyle": { 363 | "color": "#333" 364 | } 365 | }, 366 | "axisLabel": { 367 | "show": true, 368 | "textStyle": { 369 | "color": "#999999" 370 | } 371 | }, 372 | "splitLine": { 373 | "show": true, 374 | "lineStyle": { 375 | "color": [ 376 | "#eeeeee" 377 | ] 378 | } 379 | }, 380 | "splitArea": { 381 | "show": false, 382 | "areaStyle": { 383 | "color": [ 384 | "rgba(250,250,250,0.05)", 385 | "rgba(200,200,200,0.02)" 386 | ] 387 | } 388 | } 389 | }, 390 | "toolbox": { 391 | "iconStyle": { 392 | "normal": { 393 | "borderColor": "#999" 394 | }, 395 | "emphasis": { 396 | "borderColor": "#666" 397 | } 398 | } 399 | }, 400 | "legend": { 401 | "textStyle": { 402 | "color": "#999999" 403 | } 404 | }, 405 | "tooltip": { 406 | "axisPointer": { 407 | "lineStyle": { 408 | "color": "#ccc", 409 | "width": 1 410 | }, 411 | "crossStyle": { 412 | "color": "#ccc", 413 | "width": 1 414 | } 415 | } 416 | }, 417 | "timeline": { 418 | "lineStyle": { 419 | "color": "#8fd3e8", 420 | "width": 1 421 | }, 422 | "itemStyle": { 423 | "normal": { 424 | "color": "#8fd3e8", 425 | "borderWidth": 1 426 | }, 427 | "emphasis": { 428 | "color": "#8fd3e8" 429 | } 430 | }, 431 | "controlStyle": { 432 | "normal": { 433 | "color": "#8fd3e8", 434 | "borderColor": "#8fd3e8", 435 | "borderWidth": 0.5 436 | }, 437 | "emphasis": { 438 | "color": "#8fd3e8", 439 | "borderColor": "#8fd3e8", 440 | "borderWidth": 0.5 441 | } 442 | }, 443 | "checkpointStyle": { 444 | "color": "#8fd3e8", 445 | "borderColor": "rgba(138,124,168,0.37)" 446 | }, 447 | "label": { 448 | "normal": { 449 | "textStyle": { 450 | "color": "#8fd3e8" 451 | } 452 | }, 453 | "emphasis": { 454 | "textStyle": { 455 | "color": "#8fd3e8" 456 | } 457 | } 458 | } 459 | }, 460 | "visualMap": { 461 | "color": [ 462 | "#516b91", 463 | "#59c4e6", 464 | "#a5e7f0" 465 | ] 466 | }, 467 | "dataZoom": { 468 | "backgroundColor": "rgba(0,0,0,0)", 469 | "dataBackgroundColor": "rgba(255,255,255,0.3)", 470 | "fillerColor": "rgba(167,183,204,0.4)", 471 | "handleColor": "#a7b7cc", 472 | "handleSize": "100%", 473 | "textStyle": { 474 | "color": "#333" 475 | } 476 | }, 477 | "markPoint": { 478 | "label": { 479 | "normal": { 480 | "textStyle": { 481 | "color": "#eee" 482 | } 483 | }, 484 | "emphasis": { 485 | "textStyle": { 486 | "color": "#eee" 487 | } 488 | } 489 | } 490 | } 491 | } 492 | --------------------------------------------------------------------------------
8 | 本群为使用fsadmin-cms,iview-admin,vue,react,小程序,nodejs,php,java,go等全栈开发者提供交流平台,在这里共同进步 9 |
fsadmin-cms@2019
基本信息
其他信息
{{ init }}{{ unitText }}
{{ infor.title }}
{{ item.title }}
字段信息