├── public ├── robots.txt ├── favicon.ico ├── img │ └── icons │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-150x150.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ └── msapplication-icon-144x144.png ├── manifest.json └── index.html ├── .browserslistrc ├── cypress.json ├── babel.config.js ├── postcss.config.js ├── src ├── assets │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ └── img │ │ ├── 1543313198.png │ │ └── 1540538985180.jpg ├── shims-vue.d.ts ├── api │ ├── table.ts │ ├── login.ts │ ├── authentication.ts │ ├── passport.ts │ ├── pay.ts │ └── account.ts ├── views │ ├── nested │ │ ├── menu2 │ │ │ └── index.vue │ │ └── menu1 │ │ │ ├── menu1-3 │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ ├── menu1-2 │ │ │ ├── menu1-2-1 │ │ │ │ └── index.vue │ │ │ ├── menu1-2-2 │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ │ └── menu1-1 │ │ │ └── index.vue │ ├── movie-orders │ │ ├── order │ │ │ └── index.vue │ │ └── product │ │ │ └── index.vue │ ├── layout │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── Sidebar │ │ │ │ ├── Item.vue │ │ │ │ ├── index.vue │ │ │ │ └── SidebarItem.vue │ │ │ ├── AppMain.vue │ │ │ └── Navbar.vue │ │ ├── mixin │ │ │ └── ResizeHandler.ts │ │ └── Layout.vue │ ├── passport │ │ ├── tvlogin │ │ │ ├── new-native-layout.vue │ │ │ ├── view-native-layout.vue │ │ │ └── edit-native-layout.vue │ │ └── user │ │ │ └── index.vue │ ├── dashboard │ │ └── index.vue │ ├── tree │ │ └── index.vue │ ├── table │ │ └── index.vue │ ├── form │ │ └── index.vue │ ├── changepwd.vue │ ├── account │ │ ├── editUserRole.vue │ │ └── edit.vue │ ├── app │ │ └── edit.vue │ └── role │ │ └── edit.vue ├── store │ ├── getters.ts │ ├── index.ts │ └── modules │ │ ├── role.ts │ │ ├── user.ts │ │ ├── tagsView.ts │ │ └── app.ts ├── icons │ ├── components │ │ ├── table.ts │ │ ├── link.ts │ │ ├── user.ts │ │ ├── layout.ts │ │ ├── example.ts │ │ ├── nested.ts │ │ ├── form.ts │ │ ├── password.ts │ │ ├── layout1.ts │ │ ├── task.ts │ │ ├── dataline.ts │ │ ├── checkaudit.ts │ │ ├── audit.ts │ │ ├── customerservice.ts │ │ ├── layout2.ts │ │ ├── card.ts │ │ ├── eye.ts │ │ ├── unknowntype.ts │ │ ├── tree.ts │ │ ├── role.ts │ │ ├── gif.ts │ │ ├── bmp.ts │ │ ├── jpg.ts │ │ ├── png.ts │ │ └── coocaa.ts │ ├── index.ts │ └── svg │ │ ├── table.svg │ │ ├── link.svg │ │ ├── 布局.svg │ │ ├── user.svg │ │ ├── example.svg │ │ ├── 布局 (1).svg │ │ ├── icon_任务进程.svg │ │ ├── 数据看板.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── 提案审批.svg │ │ ├── 客服.svg │ │ ├── 权限审批.svg │ │ ├── audit.svg │ │ ├── form.svg │ │ ├── 1005布局_标题.svg │ │ ├── 卡.svg │ │ ├── 未知格式.svg │ │ ├── icon_账号.svg │ │ ├── eye.svg │ │ ├── 角色管理.svg │ │ ├── tree.svg │ │ ├── GIF.svg │ │ ├── BMP.svg │ │ ├── JPG.svg │ │ ├── PNG.svg │ │ └── 机器人.svg ├── App.vue ├── components │ ├── BaseTableDelete │ │ └── index.ts │ ├── SearchPane │ │ └── index.vue │ ├── ListTablePane │ │ └── index.vue │ ├── EditFormPane │ │ └── index.vue │ ├── BaseEdit │ │ └── index.ts │ ├── SearchPagePane │ │ └── index.vue │ ├── Breadcrumb │ │ └── index.vue │ ├── ScrollPane │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ └── BaseList │ │ └── index.ts ├── styles │ ├── mixin.scss │ ├── eyehealth.scss │ ├── element-ui.scss │ ├── svgicon.scss │ ├── transition.scss │ ├── edit-form.scss │ ├── variables.scss │ ├── index.scss │ └── sidebar.scss ├── main.ts ├── utils │ ├── request-pay.ts │ ├── request-authenticaton.ts │ ├── request-passport.ts │ ├── request-account.ts │ ├── auth.ts │ ├── request.ts │ ├── auth-interceptor.ts │ └── validate.ts ├── registerServiceWorker.ts └── permission.ts ├── .env ├── .env.development ├── .env.test ├── .eslintrc ├── .env.production ├── tests └── e2e │ ├── specs │ └── test.js │ ├── plugins │ └── index.js │ └── support │ ├── index.js │ └── commands.js ├── README.md ├── .gitignore ├── vue.config.js ├── tslint.json ├── jest.config.js ├── .editorconfig ├── tsconfig.json ├── LICENSE ├── yarn.md └── package.json /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/img/1543313198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/src/assets/img/1543313198.png -------------------------------------------------------------------------------- /src/assets/img/1540538985180.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/src/assets/img/1540538985180.jpg -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieguangcai/vue-order-admin/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | #说明https://cli.vuejs.org/guide/mode-and-env.html#modes 2 | #变量必须以VUE_APP_开头 3 | VUE_APP_MOCK_API=https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin 4 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_ACCOUNT_API=http://127.0.0.1:8085/ 2 | VUE_APP_PASSPORT_API=http://127.0.0.1:8085/ 3 | VUE_APP_PAY_API=http://127.0.0.1:8085/ 4 | VUE_APP_MOVIE_API=http://127.0.0.1:8085/ 5 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | VUE_APP_ACCOUNT_API=http://172.20.133.35:8085/ 2 | VUE_APP_PASSPORT_API=http://172.20.133.35:8085/ 3 | VUE_APP_PAY_API=http://172.20.133.35:8085/ 4 | VUE_APP_MOVIE_API=http://172.20.133.35:8085/ 5 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | 6 | declare module '*.svg' { 7 | import Vue from 'vue'; 8 | export default Vue; 9 | } 10 | -------------------------------------------------------------------------------- /src/api/table.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | export function getList(params: any) { 4 | return request({ 5 | url: '/table/list', 6 | method: 'get', 7 | params, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6 5 | }, 6 | "env": { 7 | "node": true 8 | }, 9 | "rules": { 10 | "comma-dangle": 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_MOVIE_API=http://production.VUE_APP_movies.order.com/api 2 | VUE_APP_ACCOUNT_API=http://production.union.com/api 3 | VUE_APP_PASSPORT_API=http://passport.com/api 4 | VUE_APP_PAY_API=http://pay.coocaa.com/api 5 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/views/movie-orders/order/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/views/movie-orders/product/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/views/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AppMain } from './AppMain.vue'; 2 | export { default as Navbar } from './Navbar.vue'; 3 | export { default as Sidebar } from './Sidebar/index.vue'; 4 | export { default as TagsView } from './TagsView.vue'; 5 | -------------------------------------------------------------------------------- /src/store/getters.ts: -------------------------------------------------------------------------------- 1 | import {IAppState} from './modules/app'; 2 | import {Store, StoreOptions} from 'vuex'; 3 | import {IRootState} from '@/store/index'; 4 | 5 | const getters = { 6 | sidebar: (state: IRootState) => state.app.sidebar, 7 | }; 8 | export default getters; 9 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | // describe('My First Test', () => { 4 | // it('Visits the app root url', () => { 5 | // cy.visit('/') 6 | // cy.contains('h1', 'Welcome to Your Vue.js + TypeScript App') 7 | // }) 8 | // }) 9 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-order-admin 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | -------------------------------------------------------------------------------- /src/icons/components/table.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | table: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw* 25 | -------------------------------------------------------------------------------- /src/icons/index.ts: -------------------------------------------------------------------------------- 1 | // All svg components were generated by vue-svgicon using svg files 2 | // https://github.com/MMF-FE/vue-svgicon 3 | 4 | // Require all svg components in svg folder 5 | // https://webpack.js.org/guides/dependency-management/#require-context 6 | const requireAll = (requireContext: any) => requireContext.keys().forEach(requireContext); 7 | requireAll(require.context('./components', false, /\.ts$/)); 8 | -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/guides/guides/plugins-guide.html 2 | 3 | module.exports = (on, config) => { 4 | return Object.assign({}, config, { 5 | fixturesFolder: 'tests/e2e/fixtures', 6 | integrationFolder: 'tests/e2e/specs', 7 | screenshotsFolder: 'tests/e2e/screenshots', 8 | videosFolder: 'tests/e2e/videos', 9 | supportFile: 'tests/e2e/support/index.js' 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /src/icons/components/link.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | link: { 5 | width: 200, 6 | height: 200, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/views/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import { IAppState} from './modules/app'; 4 | import { IUserState} from './modules/user'; 5 | import { ITagsViewState} from './modules/tagsView'; 6 | 7 | Vue.use(Vuex); 8 | 9 | export interface IRootState { 10 | app: IAppState; 11 | user: IUserState; 12 | tagsView: ITagsViewState; 13 | role: IRootState; 14 | } 15 | 16 | const store = new Vuex.Store({ 17 | }); 18 | 19 | export default store; 20 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const uglify = require('uglifyjs-webpack-plugin'); 2 | 3 | module.exports = { 4 | baseUrl: process.env.NODE_ENV === 'production' ? '/' : '/', 5 | pwa: { 6 | name: 'vue-order-admin' 7 | }, 8 | configureWebpack:{ 9 | // 压缩并重命名js文件 10 | // plugins:[ 11 | // new uglify() 12 | // ], 13 | // output:{ 14 | // filename: '[name].[hash:5].js' 15 | // } 16 | } 17 | } 18 | // https://blog.csdn.net/sunny_desmond/article/details/80916706 一篇说明文章 19 | -------------------------------------------------------------------------------- /src/components/BaseTableDelete/index.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-property-decorator'; 2 | 3 | @Component 4 | export default class BaseTableDelete extends Vue { 5 | 6 | mutipleSelection: any[] = []; 7 | 8 | handleSelectionChange(val: any) { 9 | console.log(val); 10 | this.mutipleSelection = val; 11 | } 12 | 13 | handleDel() { 14 | console.log('正在删除列表信息'); 15 | this.handleDelRows(this.mutipleSelection); 16 | } 17 | 18 | handleDelRows(row: any) { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "short_name": "app", 4 | "icons": [ 5 | { 6 | "src": "/img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } -------------------------------------------------------------------------------- /src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | &::-webkit-scrollbar { 14 | width: 6px; 15 | } 16 | &::-webkit-scrollbar-thumb { 17 | background: #99a9bf; 18 | border-radius: 20px; 19 | } 20 | } 21 | 22 | @mixin relative { 23 | position: relative; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/eyehealth.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: $mainBg; 3 | 4 | } 5 | 6 | .el-menu { 7 | background: $mainBg; 8 | } 9 | 10 | .el-table, .el-table tr, .el-table th, .el-dialog { 11 | background: $tableBg; 12 | } 13 | 14 | .el-input__inner, .el-range-input { 15 | background: $inputBg; 16 | } 17 | 18 | .el-loading-mask { 19 | background: $loadingMaskBg; 20 | } 21 | .el-input.is-disabled .el-input__inner, .el-range-editor.is-disabled input, .el-checkbox__input.is-disabled + span.el-checkbox__labe{ 22 | color: #606266; 23 | } 24 | -------------------------------------------------------------------------------- /src/icons/components/user.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | user: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | // to reset element-ui default css 2 | .el-upload { 3 | input[type="file"] { 4 | display: none !important; 5 | } 6 | } 7 | 8 | .el-upload__input { 9 | display: none; 10 | } 11 | 12 | // 暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 13 | .el-dialog { 14 | transform: none; 15 | left: 0; 16 | position: relative; 17 | margin: 0 auto; 18 | } 19 | 20 | // element ui upload 21 | .upload-container { 22 | .el-upload { 23 | width: 100%; 24 | .el-upload-dragger { 25 | width: 100%; 26 | height: 200px; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "quotemark": [true, "single"], 13 | "indent": [true, "spaces", 2], 14 | "interface-name": false, 15 | "ordered-imports": false, 16 | "object-literal-sort-keys": false, 17 | "max-line-length": false, 18 | "member-access": false, 19 | "no-console": [true, "warning"], 20 | "no-consecutive-blank-lines": false, 21 | "no-empty": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/icons/components/layout.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | layout: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/views/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /src/views/passport/tvlogin/new-native-layout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /src/styles/svgicon.scss: -------------------------------------------------------------------------------- 1 | /* recommended css code for vue-svgicon */ 2 | .svg-icon { 3 | display: inline-block; 4 | width: 16px; 5 | height: 16px; 6 | color: inherit; 7 | vertical-align: -0.15em; 8 | fill: none; 9 | stroke: currentColor; 10 | } 11 | 12 | .svg-fill { 13 | fill: currentColor; 14 | stroke: none; 15 | } 16 | 17 | .svg-up { 18 | /* default */ 19 | transform: rotate(0deg); 20 | } 21 | 22 | .svg-right { 23 | transform: rotate(90deg); 24 | } 25 | 26 | .svg-down { 27 | transform: rotate(180deg); 28 | } 29 | 30 | .svg-left { 31 | transform: rotate(-90deg); 32 | } 33 | -------------------------------------------------------------------------------- /src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue', 7 | 'ts', 8 | 'tsx' 9 | ], 10 | transform: { 11 | '^.+\\.vue$': 'vue-jest', 12 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 13 | '^.+\\.tsx?$': 'ts-jest' 14 | }, 15 | moduleNameMapper: { 16 | '^@/(.*)$': '/src/$1' 17 | }, 18 | snapshotSerializers: [ 19 | 'jest-serializer-vue' 20 | ], 21 | testMatch: [ 22 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 23 | ], 24 | testURL: 'http://localhost/' 25 | } 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-order-admin 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/icons/components/example.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | example: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import 'normalize.css'; 4 | import ElementUI from 'element-ui'; 5 | import SvgIcon from 'vue-svgicon'; 6 | import '@/styles/index.scss'; 7 | import '@/icons'; 8 | import '@/permission'; 9 | 10 | import App from '@/App.vue'; 11 | import router from '@/router'; 12 | import store from '@/store'; 13 | import '@/registerServiceWorker'; 14 | 15 | Vue.use(ElementUI); 16 | Vue.use(SvgIcon, { 17 | tagName: 'svg-icon', 18 | defaultWidth: '1em', 19 | defaultHeight: '1em', 20 | }); 21 | 22 | Vue.config.productionTip = false; 23 | 24 | new Vue({ 25 | router, 26 | store, 27 | render: (h) => h(App), 28 | }).$mount('#app'); 29 | -------------------------------------------------------------------------------- /src/utils/request-pay.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Message, MessageBox } from 'element-ui'; 3 | import {authFilter, authHeader, authRejectFilter} from '@/utils/auth-interceptor'; 4 | 5 | // 创建axios实例 6 | const service = axios.create({ 7 | baseURL: process.env.VUE_APP_PAY_API + 'api', 8 | timeout: 5000, 9 | }); 10 | 11 | // request 拦截器 12 | service.interceptors.request.use( 13 | authHeader, 14 | (error) => { 15 | // Handle request error here 16 | Promise.reject(error); 17 | }, 18 | ); 19 | 20 | // respone 拦截器 21 | service.interceptors.response.use( 22 | authFilter, 23 | authRejectFilter, 24 | ); 25 | 26 | export default service; 27 | -------------------------------------------------------------------------------- /src/utils/request-authenticaton.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Message, MessageBox } from 'element-ui'; 3 | import {authFilter, authHeader, authRejectFilter} from '@/utils/auth-interceptor'; 4 | 5 | // 创建axios实例 6 | const service = axios.create({ 7 | baseURL: process.env.VUE_APP_MOVIE_API + 'api', 8 | timeout: 5000, 9 | }); 10 | 11 | // request 拦截器 12 | service.interceptors.request.use( 13 | authHeader, 14 | (error) => { 15 | // Handle request error here 16 | Promise.reject(error); 17 | }, 18 | ); 19 | 20 | // respone 拦截器 21 | service.interceptors.response.use( 22 | authFilter, 23 | authRejectFilter, 24 | ); 25 | 26 | export default service; 27 | -------------------------------------------------------------------------------- /src/utils/request-passport.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Message, MessageBox } from 'element-ui'; 3 | import {authFilter, authHeader, authRejectFilter} from '@/utils/auth-interceptor'; 4 | 5 | // 创建axios实例 6 | const service = axios.create({ 7 | baseURL: process.env.VUE_APP_PASSPORT_API + 'api', 8 | timeout: 5000, 9 | }); 10 | 11 | // request 拦截器 12 | service.interceptors.request.use( 13 | authHeader, 14 | (error) => { 15 | // Handle request error here 16 | Promise.reject(error); 17 | }, 18 | ); 19 | 20 | // respone 拦截器 21 | service.interceptors.response.use( 22 | authFilter, 23 | authRejectFilter, 24 | ); 25 | 26 | export default service; 27 | -------------------------------------------------------------------------------- /src/icons/components/nested.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | nested: { 5 | width: 227.93, 6 | height: 200, 7 | viewBox: '0 0 1167 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/components/form.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | form: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/views/passport/tvlogin/view-native-layout.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /src/utils/request-account.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Message, MessageBox } from 'element-ui'; 3 | import {authFilter, authHeader, authRejectFilter} from '@/utils/auth-interceptor'; 4 | 5 | // 创建axios实例 6 | const service = axios.create({ 7 | baseURL: process.env.VUE_APP_ACCOUNT_API + 'ums/', 8 | // baseURL: process.env.VUE_APP_MOCK_API, 9 | timeout: 5000, 10 | }); 11 | 12 | // request 拦截器 13 | service.interceptors.request.use( 14 | authHeader, 15 | (error) => { 16 | // Handle request error here 17 | Promise.reject(error); 18 | }, 19 | ); 20 | 21 | // respone 拦截器 22 | service.interceptors.response.use( 23 | authFilter, 24 | authRejectFilter, 25 | ); 26 | 27 | export default service; 28 | -------------------------------------------------------------------------------- /src/icons/svg/布局.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/password.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | password: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/layout1.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | layout1: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1058 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | import { register } from 'register-service-worker'; 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | register(`${process.env.BASE_URL}service-worker.js`, { 5 | ready() { 6 | console.log( 7 | 'App is being served from cache by a service worker.\n' + 8 | 'For more details, visit https://goo.gl/AFskqB', 9 | ); 10 | }, 11 | cached() { 12 | console.log('Content has been cached for offline use.'); 13 | }, 14 | updated() { 15 | console.log('New content is available; please refresh.'); 16 | }, 17 | offline() { 18 | console.log('No internet connection found. App is running in offline mode.'); 19 | }, 20 | error(error) { 21 | console.error('Error during service worker registration:', error); 22 | }, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie'; 2 | 3 | const TokenKey = 'Admin-Token'; 4 | const TokenType = 'Admin-Token-Type'; 5 | 6 | export function getToken() { 7 | const tokenKey = Cookies.get(TokenKey); 8 | return tokenKey; 9 | } 10 | export function getFullToken(): string { 11 | const tokenKey = Cookies.get(TokenKey); 12 | if (tokenKey === undefined) { 13 | return ''; 14 | } 15 | const tokenType = Cookies.get(TokenType) || ''; 16 | return tokenType + tokenKey; 17 | } 18 | export function setToken(token: string, tokenType: string) { 19 | Cookies.set(TokenType, tokenType); 20 | return Cookies.set(TokenKey, token); 21 | } 22 | 23 | export function removeToken() { 24 | Cookies.remove(TokenType); 25 | return Cookies.remove(TokenKey); 26 | } 27 | 28 | export function getTokenType() { 29 | return Cookies.get(TokenType); 30 | } 31 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /src/icons/components/task.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | task: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | # Indentation override for js(x), ts(x) and vue files 14 | [*.{js,jsx,ts,tsx,vue}] 15 | indent_size = 2 16 | indent_style = space 17 | 18 | # Indentation override for css related files 19 | [*.{css,styl,scss,less,sass}] 20 | indent_size = 2 21 | indent_style = space 22 | 23 | # Indentation override for html files 24 | [*.html] 25 | indent_size = 2 26 | indent_style = space 27 | 28 | # Trailing space override for markdown file 29 | [*.md] 30 | trim_trailing_whitespace = false 31 | 32 | # Indentation override for config files 33 | [*.{json,yml}] 34 | indent_size = 2 35 | indent_style = space 36 | -------------------------------------------------------------------------------- /src/views/passport/tvlogin/edit-native-layout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/components/SearchPane/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /src/icons/components/dataline.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | dataline: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // globl transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | .fade-transform-enter { 20 | opacity: 0; 21 | transform: translateX(-30px); 22 | } 23 | .fade-transform-leave-to { 24 | opacity: 0; 25 | transform: translateX(30px); 26 | } 27 | 28 | /* fade */ 29 | .breadcrumb-enter-active, 30 | .breadcrumb-leave-active { 31 | transition: all .5s; 32 | } 33 | 34 | .breadcrumb-enter, 35 | .breadcrumb-leave-active { 36 | opacity: 0; 37 | transform: translateX(20px); 38 | } 39 | 40 | .breadcrumb-move { 41 | transition: all .5s; 42 | } 43 | 44 | .breadcrumb-leave-active { 45 | position: absolute; 46 | } 47 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": "./", 14 | "types": [ 15 | "node", 16 | "jest", 17 | "webpack-env" 18 | ], 19 | "paths": { 20 | "@/*": [ 21 | "src/*" 22 | ] 23 | }, 24 | "lib": [ 25 | "esnext", 26 | "dom", 27 | "dom.iterable", 28 | "scripthost" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx", 34 | "src/**/*.vue", 35 | "tests/**/*.ts", 36 | "tests/**/*.tsx", 37 | "config/**/*.ts" 38 | ], 39 | "exclude": [ 40 | "node_modules" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/components/ListTablePane/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 26 | 27 | 37 | -------------------------------------------------------------------------------- /src/api/login.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request-account'; 2 | import {getToken} from '@/utils/auth'; 3 | 4 | export function login(username: string, password: string) { 5 | return request({ 6 | url: '/oauth/token', 7 | method: 'get', 8 | params: { 9 | username, 10 | password, 11 | }, 12 | }); 13 | } 14 | 15 | export function getInfo(accessToken: string) { 16 | return request({ 17 | url: '/user', 18 | method: 'get', 19 | params: { access_token: accessToken }, 20 | }); 21 | } 22 | 23 | export function logout() { 24 | const token = getToken(); 25 | return request({ 26 | url: '/oauth/delete/token', 27 | method: 'get', 28 | params: {access_token: token}, 29 | }); 30 | } 31 | 32 | export function changePwd(oldPwd: string, newPwd: string) { 33 | return request({ 34 | url: '/api/me/changepwd', 35 | method: 'post', 36 | params: { oldPwd, newPwd}, 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /src/icons/svg/布局 (1).svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /src/styles/edit-form.scss: -------------------------------------------------------------------------------- 1 | //编辑弹窗的样式 2 | .main-container { 3 | .cc-edit-button { 4 | margin-left: 120px; 5 | } 6 | 7 | .cc-edit-form { 8 | margin: 0px 30px 0px 0px; 9 | } 10 | //搜索页面的样式、 11 | .search-page-panel { 12 | margin-top: 10px; 13 | margin-bottom: 10px; 14 | } 15 | 16 | .search-panel { 17 | margin-bottom: 10px; 18 | display: inline; 19 | font-size: 14px; 20 | } 21 | 22 | .search-panel .el-input { 23 | width: 150px; 24 | margin: 2px 2px; 25 | } 26 | 27 | .search-panel .el-date-editor { 28 | width: 180px; 29 | margin: 2px 2px; 30 | } 31 | 32 | .search-panel .el-range-editor { 33 | width: 220px; 34 | margin: 2px 2px; 35 | } 36 | .search-panel .el-date-editor--datetimerange{ 37 | width:310px; margin: 2px 2px; 38 | } 39 | .search-panel .el-button { 40 | margin-left: 10px; 41 | } 42 | 43 | .search-panel .el-select { 44 | margin: 2px 2px; 45 | } 46 | .search-panel .search-con-container{ 47 | display: inline; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/icons/svg/icon_任务进程.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/数据看板.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/checkaudit.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | checkaudit: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/components/audit.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | audit: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // sidebar 2 | //$menuBg:#304156; 3 | //$subMenuBg:#1f2d3d; 4 | //$menuHover:#001528; 5 | 6 | 7 | //不伤眼睛配色 8 | //$menuBg:#ccffcc; 9 | //$subMenuBg:#c7edcc; 10 | //$menuHover:#cfcbae; 11 | //$menuLineColor:#c7edcc; 12 | 13 | //$menuBg:#FAF9DE; 14 | //$subMenuBg:#FAF9DE; 15 | //$menuHover: #f8faab; 16 | //$menuLineColor:#FAF9DE; 17 | // 18 | //$mainBg:#FAF9DE; 19 | // 20 | //$tagsWrapperBg:#FAF9DE; 21 | //$tagsWrapperBbBg:#FAF9DE; 22 | //$tableBg:#FAF9DE; 23 | //$inputBg:#ebebe4; 24 | //$loadingMaskBg: rgba(235, 235, 228, 0.7); 25 | //$collapseBg: #FAF9DE; 26 | //$collapseHeaderBg:#DCE2F1; 27 | //$ul-border-right-color: rgba(216, 219, 225, 0.58); 28 | 29 | $menuBg:#f5f5f5; 30 | $subMenuBg:#f5f5f5; 31 | $menuHover: #f8faab; 32 | $menuLineColor:#FAF9DE; 33 | 34 | $mainBg:#f5f5f5; 35 | 36 | $tagsWrapperBg:#f5f5f5; 37 | $tagsWrapperBbBg:#f5f5f5; 38 | $tableBg:#ffffff; 39 | $inputBg: #ffffff; 40 | $loadingMaskBg: rgba(235, 235, 228, 0.7); 41 | $collapseBg: #ffffff; 42 | $collapseHeaderBg:#DCE2F1; 43 | $ul-border-right-color: rgba(216, 219, 225, 0.58); 44 | $navbar-bg-color:#fa7b11; 45 | 46 | -------------------------------------------------------------------------------- /src/icons/components/customerservice.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | customerservice: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chong Guo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/icons/components/layout2.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | layout2: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/store/modules/role.ts: -------------------------------------------------------------------------------- 1 | import {getModule, Module, MutationAction, VuexModule} from 'vuex-module-decorators'; 2 | import store from '../index'; 3 | import {RoleInfo} from '../../types/index'; 4 | import {getAllValidRoleList} from '../../api/account'; 5 | 6 | 7 | export interface IRoleState { 8 | roles: RoleInfo[]; 9 | } 10 | 11 | @Module({ dynamic: true, store, name: 'role' }) 12 | class Role extends VuexModule { 13 | roles: IRoleState['roles'] = []; 14 | 15 | @MutationAction({ mutate: [ 'roles'] }) 16 | async GetAllValidRoles(refresh: boolean) { 17 | console.log('xxx'); 18 | if (refresh || this.roles == null || this.roles.length === 0) { 19 | const {data} = await getAllValidRoleList(); 20 | if (data.data) { 21 | return { 22 | roles: data.data, 23 | }; 24 | } else { 25 | throw Error('GetAllValidRoles: data must be a non-null array!'); 26 | } 27 | } else { 28 | return { 29 | roles: this.roles, 30 | }; 31 | } 32 | } 33 | } 34 | 35 | export const RoleModule = getModule(Role.prototype); 36 | -------------------------------------------------------------------------------- /src/components/EditFormPane/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 44 | -------------------------------------------------------------------------------- /src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/card.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | card: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/components/eye.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | eye: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/提案审批.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/客服.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/权限审批.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/audit.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/icons/components/unknowntype.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | unknowntype: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/svg/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/1005布局_标题.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /yarn.md: -------------------------------------------------------------------------------- 1 | 在Nodejs环境下,通过npm install -g yarn 命令进行全局安装 2 | 3 | 例如: 4 | 5 | yarn version 6 | yarn init 7 | yarn install 8 | yarn add vue 9 | yarn add vue-router 10 | yarn remove vue-router 11 | CLI 命令比较 12 | 13 | npm Yarn 14 | npm install yarn install 15 | (N/A) yarn install --flat 16 | (N/A) yarn install --har 17 | (N/A) yarn install --no-lockfile 18 | (N/A) yarn install --pure-lockfile 19 | npm install [package] (N/A) 20 | npm install --save [package] yarn add [package] 21 | npm install --save-dev [package] yarn add [package] [--dev/-D] 22 | (N/A) yarn add [package] [--peer/-P] 23 | npm install --save-optional [package] yarn add [package] [--optional/-O] 24 | npm install --save-exact [package] yarn add [package] [--exact/-E] 25 | (N/A) yarn add [package] [--tilde/-T] 26 | npm install --global [package] yarn global add [package] 27 | npm rebuild yarn install --force 28 | npm uninstall [package] (N/A) 29 | npm uninstall --save [package] yarn remove [package] 30 | npm uninstall --save-dev [package] yarn remove [package] 31 | npm uninstall --save-optional [package] yarn remove [package] 32 | npm cache clean yarn cache clean 33 | rm -rf node_modules && npm install yarn upgrade 34 | 35 | 36 | 37 | 38 | typescript 声明文件管理工具 39 | https://www.cnblogs.com/dhcn/p/7722248.html 40 | typings 41 | 安装 npm install typings -g 42 | 安装依赖库如: 43 | ypings install dt~async --global 44 | -------------------------------------------------------------------------------- /src/components/BaseEdit/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Component, Vue} from 'vue-property-decorator'; 3 | import {ElForm} from 'element-ui/types/form'; 4 | import {AxiosPromise} from 'axios'; 5 | import {AppModule} from '@/store/modules/app'; 6 | 7 | @Component 8 | export default class BaseEdit extends Vue { 9 | 10 | get formLabelWidth() { 11 | return AppModule.formLabelWidth; 12 | } 13 | 14 | get recordeStatus() { 15 | return AppModule.recordeStatus; 16 | } 17 | get formSize() { 18 | return AppModule.formSize; 19 | } 20 | 21 | saveThenNew() { 22 | this.saveForm(() => this.$emit('save-success-then-new')); 23 | } 24 | getEditForm(): ElForm { 25 | return this.$refs.editForm as ElForm; 26 | } 27 | 28 | cancel() { 29 | const form = this.getEditForm(); 30 | form.resetFields(); 31 | this.$emit('cancel'); 32 | } 33 | 34 | save() { 35 | this.saveForm(() => this.$emit('save-success')); 36 | } 37 | 38 | saveForm(cb: () => void): void { 39 | const form = this.getEditForm(); 40 | form.validate(async (valid) => { 41 | if (!valid) { 42 | return false; 43 | } else { 44 | await this.saveFormData(); 45 | this.$message.success('保存成功'); 46 | form.resetFields(); 47 | cb(); 48 | } 49 | }); 50 | } 51 | 52 | saveFormData(): Promise | any { } 53 | } 54 | -------------------------------------------------------------------------------- /src/icons/svg/卡.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/layout/mixin/ResizeHandler.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue, Watch } from 'vue-property-decorator'; 2 | import { DeviceType, AppModule } from '@/store/modules/app'; 3 | 4 | const { body } = document; 5 | const WIDTH = 1024; 6 | const RATIO = 3; 7 | 8 | @Component 9 | export default class ResizeHandlerMixin extends Vue { 10 | get device() { 11 | return AppModule.device; 12 | } 13 | 14 | get sidebar() { 15 | return AppModule.sidebar; 16 | } 17 | 18 | @Watch('$route') 19 | OnRouteChange() { 20 | if (this.device === DeviceType.Mobile && this.sidebar.opened) { 21 | AppModule.CloseSideBar(false); 22 | } 23 | } 24 | 25 | beforeMount() { 26 | window.addEventListener('resize', this.resizeHandler); 27 | } 28 | 29 | mounted() { 30 | const isMobile = this.isMobile(); 31 | if (isMobile) { 32 | AppModule.ToggleDevice(DeviceType.Mobile); 33 | AppModule.CloseSideBar(true); 34 | } 35 | } 36 | 37 | isMobile() { 38 | const rect = body.getBoundingClientRect(); 39 | return rect.width - RATIO < WIDTH; 40 | } 41 | 42 | resizeHandler() { 43 | if (!document.hidden) { 44 | const isMobile = this.isMobile(); 45 | AppModule.ToggleDevice(isMobile ? DeviceType.Mobile : DeviceType.Desktop); 46 | if (isMobile) { 47 | AppModule.CloseSideBar(true); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/icons/svg/未知格式.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/SearchPagePane/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 59 | -------------------------------------------------------------------------------- /src/icons/svg/icon_账号.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 39 | 40 | 55 | -------------------------------------------------------------------------------- /src/views/tree/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 73 | 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-order-admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve --mode development", 7 | "build": "vue-cli-service build", 8 | "build-test": "vue-cli-service build --mode test", 9 | "lint": "vue-cli-service lint", 10 | "test:unit": "vue-cli-service test:unit", 11 | "test:e2e": "vue-cli-service test:e2e", 12 | "test": "yarn test:unit && yarn test:e2e" 13 | }, 14 | "dependencies": { 15 | "@types/js-cookie": "^2.1.0", 16 | "@types/nprogress": "^0.0.29", 17 | "@types/webpack-env": "^1.13.6", 18 | "axios": "^0.18.0", 19 | "element-ui": "^2.3.6", 20 | "js-cookie": "^2.2.0", 21 | "normalize.css": "^8.0.0", 22 | "nprogress": "^0.2.0", 23 | "register-service-worker": "^1.0.0", 24 | "vue": "^2.5.17", 25 | "vue-class-component": "^6.0.0", 26 | "vue-property-decorator": "^7.0.0", 27 | "vue-router": "^3.0.1", 28 | "vue-svgicon": "^3.1.0", 29 | "vuex": "^3.0.1", 30 | "vuex-class": "^0.3.1", 31 | "vuex-module-decorators": "^0.4.3", 32 | "webpack": "^4.17.2" 33 | }, 34 | "devDependencies": { 35 | "@types/jest": "^23.1.4", 36 | "@vue/cli-plugin-babel": "^3.0.1", 37 | "@vue/cli-plugin-e2e-cypress": "^3.0.1", 38 | "@vue/cli-plugin-pwa": "^3.0.1", 39 | "@vue/cli-plugin-typescript": "^3.0.1", 40 | "@vue/cli-plugin-unit-jest": "^3.0.1", 41 | "@vue/cli-service": "^3.0.1", 42 | "@vue/test-utils": "^1.0.0-beta.20", 43 | "babel-core": "7.0.0-bridge.0", 44 | "eslint": "^5.5.0", 45 | "jest": "^23.5.0", 46 | "node-sass": "^4.9.0", 47 | "sass-loader": "^7.0.1", 48 | "ts-jest": "^23.0.0", 49 | "typescript": "^3.0.0", 50 | "vue-cli-plugin-element-ui": "^1.1.2", 51 | "vue-template-compiler": "^2.5.17" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 63 | -------------------------------------------------------------------------------- /src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from './router'; 2 | import NProgress from 'nprogress'; 3 | import 'nprogress/nprogress.css'; 4 | import {Message, MessageBox} from 'element-ui'; 5 | import { getToken } from '@/utils/auth'; 6 | import { Route } from 'vue-router'; 7 | import { UserModule } from '@/store/modules/user'; 8 | 9 | const whiteList = ['/login']; 10 | 11 | router.beforeEach((to: Route, from: Route, next: any) => { 12 | NProgress.start(); 13 | if (getToken()) { 14 | if (to.path === '/login') { 15 | next({ path: '/' }); 16 | NProgress.done(); // If current page is dashboard will not trigger afterEach hook, so manually handle it 17 | } else { 18 | if (UserModule.roles.length === 0) { 19 | UserModule.GetInfo().then(() => { 20 | next(); 21 | }).catch((err) => { 22 | UserModule.FedLogOut().then(() => { 23 | Message.error(err || '授权失败,请重新登录'); 24 | next({ path: '/login' }); 25 | }); 26 | }); 27 | } else { 28 | next(); 29 | } 30 | } 31 | } else { 32 | if (whiteList.indexOf(to.path) !== -1) { 33 | next(); 34 | } else { 35 | if (to.path === '/login' || from.path === '/login') { 36 | NProgress.done(); 37 | } else { 38 | if (to.path === '/') { 39 | next('/login'); 40 | NProgress.done(); 41 | } else { 42 | MessageBox.alert( 43 | '由于太久没有操作授权已经超时,请重新登录', 44 | { 45 | confirmButtonText: '重新登录', 46 | type: 'error', 47 | }, 48 | ).then( 49 | () => { 50 | next('/login'); 51 | NProgress.done(); 52 | }, 53 | ); 54 | } 55 | } 56 | } 57 | } 58 | }); 59 | 60 | router.afterEach(() => { 61 | NProgress.done(); 62 | }); 63 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Message, MessageBox } from 'element-ui'; 3 | import { getToken } from '@/utils/auth'; 4 | import { UserModule } from '@/store/modules/user'; 5 | 6 | // 创建axios实例 7 | const service = axios.create({ 8 | baseURL: process.env.VUE_APP_MOCK_API, 9 | timeout: 5000, 10 | }); 11 | 12 | // request 拦截器 13 | service.interceptors.request.use( 14 | (config) => { 15 | if (UserModule.token) { 16 | config.headers['X-Token'] = getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 17 | } 18 | return config; 19 | }, 20 | (error) => { 21 | // Handle request error here 22 | Promise.reject(error); 23 | }, 24 | ); 25 | 26 | // respone 拦截器 27 | service.interceptors.response.use( 28 | (response) => { 29 | /** 30 | * code为非20000是抛错 可结合自己业务进行修改 31 | */ 32 | const res = response.data; 33 | if (res.code !== 20000) { 34 | Message({ 35 | message: res.message, 36 | type: 'error', 37 | duration: 5 * 1000, 38 | }); 39 | 40 | // 50008: 非法的token 41 | // 50012: 其他客户端登录了 42 | // 50014: Token 过期了 43 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) { 44 | MessageBox.confirm( 45 | '你已被登出,可以取消继续留在该页面,或者重新登录', 46 | '确定登出', 47 | { 48 | confirmButtonText: '重新登录', 49 | cancelButtonText: '取消', 50 | type: 'warning', 51 | }, 52 | ).then(() => { 53 | UserModule.FedLogOut().then(() => { 54 | location.reload(); // 为了重新实例化vue-router对象 避免bug 55 | }); 56 | }); 57 | } 58 | return Promise.reject('error'); 59 | } else { 60 | return response.data; 61 | } 62 | }, 63 | (error) => { 64 | Message({ 65 | message: error.message, 66 | type: 'error', 67 | duration: 5 * 1000, 68 | }); 69 | return Promise.reject(error); 70 | }, 71 | ); 72 | 73 | export default service; 74 | -------------------------------------------------------------------------------- /src/icons/components/tree.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | tree: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/ScrollPane/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 52 | 53 | 69 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 38 | 39 | 55 | -------------------------------------------------------------------------------- /src/icons/components/role.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | role: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1068 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/api/authentication.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request-authenticaton'; 2 | import { 3 | BaseMoviesIqiyiOrderBase, 4 | BaseMoviesIqiyiOrderBaseListQuery, OrderFlag, 5 | Pageable, 6 | ResponseResult, SearchHistoryModel, 7 | UserInfoFull, 8 | } from '@/types'; 9 | // @ts-ignore 10 | import qs from 'qs'; 11 | import {AxiosPromise, AxiosResponse} from 'axios'; 12 | import {AppModule} from '@/store/modules/app'; 13 | 14 | 15 | /** 16 | * --------------------authentication相关接口------------------------- 17 | */ 18 | export function getBaseMoviesIqiyiOrderBaseList(params: BaseMoviesIqiyiOrderBaseListQuery): AxiosPromise>> { 19 | return request({ 20 | url: '/authentication/order/list', 21 | method: 'get', 22 | params, 23 | paramsSerializer(p: any) { 24 | return qs.stringify(p, {arrayFormat: 'repeat'}); 25 | }, 26 | }); 27 | } 28 | 29 | export function getBaseMoviesIqiyiOrderBaseDetail(orderId: number, searchHistoryModel: SearchHistoryModel): AxiosPromise> { 30 | return request({ 31 | url: '/authentication/order/detail', 32 | method: 'get', 33 | params: {orderId, searchHistoryModel }, 34 | paramsSerializer(p: any) { 35 | return qs.stringify(p, {arrayFormat: 'repeat'}); 36 | }, 37 | }); 38 | } 39 | 40 | export function clientTypeToName(code: number) { 41 | let name = code + ''; 42 | AppModule.clientType.forEach((item) => { 43 | if (item.value === code) { 44 | name = item.label; 45 | return false; 46 | } 47 | }); 48 | return name; 49 | } 50 | 51 | export function orderTypeToName(code: number) { 52 | let name = code + ''; 53 | AppModule.orderType.forEach((item) => { 54 | if (item.value === code) { 55 | name = item.label; 56 | return false; 57 | } 58 | }); 59 | return name; 60 | } 61 | export function payFlagClassName(payFlag: OrderFlag): string { 62 | if (payFlag === 0) { 63 | return 'warning-row'; 64 | } else if (payFlag === 1) { 65 | return 'success-row'; 66 | } 67 | return ''; 68 | } 69 | 70 | /** 71 | * --------------------authentication相关接口------------------------- 72 | */ 73 | -------------------------------------------------------------------------------- /src/icons/components/gif.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | gif: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/svg/角色管理.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 53 | 54 | 88 | -------------------------------------------------------------------------------- /src/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/GIF.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/bmp.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | bmp: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/store/modules/user.ts: -------------------------------------------------------------------------------- 1 | import { VuexModule, Module, MutationAction, Mutation, Action, getModule } from 'vuex-module-decorators'; 2 | import { login, logout, getInfo } from '@/api/login'; 3 | import { getToken, setToken, removeToken } from '@/utils/auth'; 4 | import store from '@/store'; 5 | import {StatusInfo} from '@/types/index'; 6 | 7 | export interface IUserState { 8 | token: string; 9 | name: string; 10 | avatar: string; 11 | roles: string[]; 12 | accountStatus: StatusInfo[]; 13 | } 14 | 15 | @Module({ dynamic: true, store, name: 'user' }) 16 | class User extends VuexModule { 17 | token: IUserState['token'] = ''; 18 | name: IUserState['name'] = ''; 19 | avatar: IUserState['avatar'] = ''; 20 | roles: IUserState['roles'] = []; 21 | accountStatus: IUserState['accountStatus'] = [{value: 1, label: '正常'}, {value: 2, label: '禁止登录'}]; 22 | 23 | @Mutation 24 | SET_TOKEN(token: string) { 25 | this.token = token; 26 | } 27 | 28 | @Action({ commit: 'SET_TOKEN' }) 29 | async Login(userInfo: { username: string, password: string}) { 30 | const username = userInfo.username.trim(); 31 | const { data } = await login(username, userInfo.password); 32 | const token = data.access_token; 33 | setToken(token, data.token_type); 34 | return token; 35 | } 36 | 37 | @Action({ commit: 'SET_TOKEN' }) 38 | async FedLogOut() { 39 | removeToken(); 40 | return ''; 41 | } 42 | 43 | @MutationAction({ mutate: [ 'roles', 'name', 'avatar' ] }) 44 | async GetInfo() { 45 | const token = getToken(); 46 | if (token === undefined) { 47 | throw Error('GetInfo: token is undefined!'); 48 | } 49 | const { data } = await getInfo(token); 50 | if (data.authorities && data.authorities.length > 0) { 51 | const roles: string[] = []; 52 | data.authorities.forEach((i: any) => { 53 | roles.push(i.authority); 54 | }); 55 | return { 56 | roles, 57 | name: data.name, 58 | avatar: data.avatar, 59 | }; 60 | } else { 61 | throw Error('GetInfo: roles must be a non-null array!'); 62 | } 63 | } 64 | 65 | @MutationAction({ mutate: [ 'token', 'roles' ] }) 66 | async LogOut() { 67 | if (getToken() === undefined) { 68 | throw Error('LogOut: token is undefined!'); 69 | } 70 | await logout(); 71 | removeToken(); 72 | return { 73 | token: '', 74 | roles: [], 75 | }; 76 | } 77 | } 78 | 79 | export const UserModule = getModule(User.prototype); 80 | -------------------------------------------------------------------------------- /src/views/table/index.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 79 | -------------------------------------------------------------------------------- /src/icons/components/jpg.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | jpg: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/components/png.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | png: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1024 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/icons/svg/BMP.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/passport/user/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 78 | 79 | 80 | 83 | 84 | -------------------------------------------------------------------------------- /src/icons/svg/JPG.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/PNG.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/form/index.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 78 | 79 | 84 | 85 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | /* icon font path, required */ 2 | /* 改变主题色变量 */ 3 | 4 | @import './variables.scss'; 5 | $--color-primary: #fa7b11; 6 | $--collapse-content-fill:$collapseBg; 7 | $--collapse-header-fill:$collapseHeaderBg; 8 | 9 | /* 改变 icon 字体路径变量,必需 */ 10 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 11 | @import "~element-ui/packages/theme-chalk/src/index"; 12 | 13 | @import './mixin.scss'; 14 | @import './transition.scss'; 15 | @import './element-ui.scss'; 16 | @import './sidebar.scss'; 17 | @import './svgicon.scss'; 18 | @import './edit-form.scss'; 19 | @import './eyehealth.scss'; 20 | 21 | body { 22 | height: 100%; 23 | -moz-osx-font-smoothing: grayscale; 24 | -webkit-font-smoothing: antialiased; 25 | text-rendering: optimizeLegibility; 26 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 27 | } 28 | 29 | label { 30 | font-weight: 700; 31 | } 32 | 33 | html { 34 | height: 100%; 35 | box-sizing: border-box; 36 | } 37 | 38 | #app{ 39 | height: 100%; 40 | } 41 | 42 | *, 43 | *:before, 44 | *:after { 45 | box-sizing: inherit; 46 | } 47 | 48 | a, 49 | a:focus, 50 | a:hover { 51 | cursor: pointer; 52 | color: inherit; 53 | outline: none; 54 | text-decoration: none; 55 | } 56 | 57 | div:focus{ 58 | outline: none; 59 | } 60 | 61 | a:focus, 62 | a:active { 63 | outline: none; 64 | } 65 | 66 | a, 67 | a:focus, 68 | a:hover { 69 | cursor: pointer; 70 | color: inherit; 71 | text-decoration: none; 72 | } 73 | 74 | .clearfix { 75 | &:after { 76 | visibility: hidden; 77 | display: block; 78 | font-size: 0; 79 | content: " "; 80 | clear: both; 81 | height: 0; 82 | } 83 | } 84 | 85 | // main-container全局样式 86 | .app-main{ 87 | min-height: 100% 88 | } 89 | 90 | .app-container { 91 | //padding: 20px; 92 | //padding-top:32px; 93 | } 94 | .cc-router-container{ 95 | padding-top:32px; 96 | } 97 | // 全局table状态 98 | .el-table .warning-row ,.cc-order-table .warning-row{ 99 | color: #ff374e; 100 | } 101 | .el-table .success-row ,.cc-order-table .success-row{ 102 | color: #f98a0c; 103 | } 104 | .cc-order-table{ 105 | width:100%; 106 | border-left:1px solid #ccc; 107 | border-top:1px solid #ccc; 108 | } 109 | .cc-order-table .order-amount{ 110 | color: #ff741b; font-size:16px; font-weight: bold; 111 | } 112 | .cc-order-table tr td, .cc-order-table tr th{ 113 | padding:5px; border-right:1px solid #ccc; border-bottom:1px solid #ccc; 114 | } 115 | .cc-inline-title-table tr td:nth-child(2n+1){ 116 | font-size:15px; 117 | } 118 | .cc-form{ 119 | width:80%; 120 | padding:30px; 121 | } 122 | //不伤眼的色彩 苹果绿 RGB 204,255,204 #CCFFCC 杏仁黄 rgb 250 249 222 #FAF9DE 海天蓝 rgb 220 226 241 #DCE2F1 青草绿 rgb 227 237 205 #E3EDCD 123 | // 豆沙绿 #c7edcc 灰色 #ebebe4 124 | -------------------------------------------------------------------------------- /src/utils/auth-interceptor.ts: -------------------------------------------------------------------------------- 1 | import {Message, MessageBox} from 'element-ui'; 2 | import {UserModule} from '@/store/modules/user'; 3 | import {AxiosRequestConfig, AxiosResponse} from 'axios'; 4 | import {getFullToken} from '@/utils/auth'; 5 | import {ResponseResult} from '@/types'; 6 | 7 | export function authHeader(config: AxiosRequestConfig): AxiosRequestConfig { 8 | // if (UserModule.token) { 9 | const fullToken = getFullToken(); 10 | if (fullToken !== undefined) { 11 | config.headers.Authorization = getFullToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 12 | } 13 | return config; 14 | } 15 | 16 | export function authRejectFilter(error: any) { 17 | 18 | const response = error.response; 19 | let message = ''; 20 | if (typeof response === 'undefined') { 21 | message = error.message; 22 | Message({ 23 | message: '错误:' + message, 24 | type: 'error', 25 | duration: 5 * 1000, 26 | }); 27 | return Promise.reject(error); 28 | } else { 29 | message = response.data.error_description || response.data.message || response.data || '服务器资源访问出错'; 30 | if (response !== undefined) { 31 | const status = response.status; 32 | if (status === 401) { 33 | if (response.data.error === 'unauthorized') { 34 | Message({ 35 | message: '账号密码错误!', 36 | type: 'error', 37 | duration: 5 * 1000, 38 | }); 39 | return Promise.reject('error'); 40 | } 41 | MessageBox.confirm( 42 | '你已被登出,可以取消继续留在该页面,或者重新登录', 43 | '确定登出', 44 | { 45 | confirmButtonText: '重新登录', 46 | cancelButtonText: '取消', 47 | type: 'warning', 48 | }, 49 | ).then(() => { 50 | UserModule.FedLogOut().then(() => { 51 | location.reload(); // 为了重新实例化vue-router对象 避免bug 52 | }); 53 | }); 54 | const errorInfo: ResponseResult = {code: response.status, message, success: false, data: response}; 55 | 56 | return Promise.reject(errorInfo); 57 | } else { 58 | // Message({ 59 | // message: '服务错误:' + message, 60 | // type: 'error', 61 | // duration: 5 * 1000, 62 | // }); 63 | const errorInfo: ResponseResult = {code: response.status, message, success: false, data: response}; 64 | return Promise.reject(errorInfo); 65 | } 66 | } 67 | } 68 | } 69 | 70 | export function handlerCommonError(error: ResponseResult): void { 71 | Message({ 72 | message: '错误:' + error.message, 73 | type: 'error', 74 | duration: 5 * 1000, 75 | }); 76 | } 77 | 78 | export function authFilter(response: AxiosResponse): AxiosResponse | Promise> { 79 | const res = response.data; 80 | if (typeof res.success !== 'undefined' && !res.success) { 81 | return Promise.reject(res); 82 | } else { 83 | return response; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/components/BaseList/index.ts: -------------------------------------------------------------------------------- 1 | import {Component, Vue} from 'vue-property-decorator'; 2 | import {IPageinfo} from '@/types'; 3 | // @ts-ignore 4 | import qs from 'qs'; 5 | import {AxiosPromise} from 'axios'; 6 | import {getAppInfo} from '@/api/account'; 7 | import {pickerOptions} from '@/utils/validate'; 8 | import {RawLocation} from 'vue-router'; 9 | 10 | @Component 11 | export default class BaseList extends Vue { 12 | listLoading: boolean = true; 13 | 14 | listQuery: IPageinfo = {page: 0, size: 50, total: 0}; 15 | 16 | get total(): number { 17 | return this.listQuery.total; 18 | } 19 | get size(): number { 20 | return this.listQuery.size; 21 | } 22 | get page(): number { 23 | return this.listQuery.page; 24 | } 25 | 26 | get pickerOptions(): any { 27 | return pickerOptions(); 28 | } 29 | created() { 30 | this.onCreated(); 31 | const pageInfo: IPageinfo = {page: 0, size: 50, total: 0}; 32 | if (this.$route.query.page) { 33 | pageInfo.page = parseInt(this.$route.query.page, 10); 34 | } 35 | if (this.$route.query.size) { 36 | pageInfo.size = parseInt(this.$route.query.size, 10); 37 | } 38 | if (this.$route.query.total) { 39 | pageInfo.total = parseInt(this.$route.query.total, 10); 40 | } 41 | 42 | this.listQuery = Object.assign(this.listQuery, this.$route.query, pageInfo); 43 | this.fetchData(); 44 | } 45 | 46 | // 钩子函数的包装 47 | onCreated() { 48 | 49 | } 50 | 51 | handleSizeChange(size: number) { 52 | this.listQuery.size = size; 53 | this.fetchData(); 54 | } 55 | 56 | handleCurrentChange(page: number) { 57 | console.log('当前页码为:' + page); 58 | this.listQuery.page = page; 59 | this.fetchData(); 60 | } 61 | 62 | refetchData() { 63 | this.listQuery.page = 0; 64 | this.fetchData(); 65 | } 66 | 67 | generateRoute() { 68 | if (this.$route.name) { 69 | return this.$route; 70 | } 71 | return false; 72 | } 73 | 74 | fetchData() { 75 | this.listLoading = true; 76 | this.realFetchData().then(() => { 77 | const path = this.$route.path; 78 | // history.pushState(null, '条件查询', path + '?' + qs.stringify(this.listQuery, {arrayFormat: 'repeat'})); 79 | const newLocation: RawLocation = {} ; 80 | const oldRoute = this.$route; 81 | newLocation.path = oldRoute.path; 82 | newLocation.query = this.listQuery; 83 | newLocation.name = oldRoute.name; 84 | newLocation.params = oldRoute.params; 85 | newLocation.hash = oldRoute.hash; 86 | history.pushState(null, '条件查询', path + '?' + qs.stringify(this.listQuery, {arrayFormat: 'repeat'})); 87 | // this.$router.push(newLocation, () => { 88 | this.$store.dispatch('addVisitedViews', newLocation); 89 | // }); 90 | }).finally(() => { 91 | this.listLoading = false; 92 | }); 93 | } 94 | 95 | realFetchData(): AxiosPromise { 96 | return getAppInfo(0); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/store/modules/tagsView.ts: -------------------------------------------------------------------------------- 1 | import {VuexModule, Module, MutationAction, Mutation, Action, getModule} from 'vuex-module-decorators'; 2 | import {Route} from 'vue-router'; 3 | import store from '@/store'; 4 | // @ts-ignore 5 | import qs from 'qs'; 6 | 7 | export interface ITagsViewState { 8 | visitedViews: Route[]; 9 | cachedViews: string[]; 10 | } 11 | 12 | @Module({dynamic: true, store, name: 'tagsView'}) 13 | class TagsView extends VuexModule { 14 | visitedViews: ITagsViewState['visitedViews'] = []; 15 | cachedViews: ITagsViewState['cachedViews'] = []; 16 | 17 | @Mutation 18 | ADD_VISITED_VIEWS(view: Route) { 19 | let find = false; 20 | this.visitedViews.forEach((v) => { 21 | if (v.path === view.path) { 22 | v.query = view.query; 23 | // var query = qs.stringify(v.query, {arrayFormat: 'repeat'}); 24 | // console.log('存储的值为:' + v.path + '?' + query); 25 | find = true; 26 | } 27 | }); 28 | // @ts-ignore 29 | if (find === true) { 30 | return; 31 | } 32 | this.visitedViews.push( 33 | Object.assign({}, view, { 34 | title: view.meta.title || 'no-name', 35 | }), 36 | ); 37 | if (!view.meta.noCache) { 38 | this.cachedViews.push(view.name || ''); 39 | } 40 | } 41 | 42 | @Mutation 43 | DEL_VISITED_VIEWS(view: Route) { 44 | for (const [i, v] of this.visitedViews.entries()) { 45 | if (v.path === view.path) { 46 | this.visitedViews.splice(i, 1); 47 | break; 48 | } 49 | } 50 | for (const i of this.cachedViews) { 51 | if (i === view.name) { 52 | const index = this.cachedViews.indexOf(i); 53 | this.cachedViews.splice(index, 1); 54 | break; 55 | } 56 | } 57 | } 58 | 59 | @Mutation 60 | DEL_OTHERS_VIEWS(view: Route) { 61 | for (const [i, v] of this.visitedViews.entries()) { 62 | if (v.path === view.path) { 63 | this.visitedViews = this.visitedViews.slice(i, i + 1); 64 | break; 65 | } 66 | } 67 | for (const i of this.cachedViews) { 68 | if (i === view.name) { 69 | const index = this.cachedViews.indexOf(i); 70 | this.cachedViews = this.cachedViews.slice(index, index + 1); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | @Mutation 77 | DEL_ALL_VIEWS() { 78 | this.visitedViews = []; 79 | this.cachedViews = []; 80 | } 81 | 82 | @Action({commit: 'ADD_VISITED_VIEWS'}) 83 | addVisitedViews(view: Route) { 84 | return view; 85 | } 86 | 87 | @Action 88 | delVisitedViews(view: Route) { 89 | return new Promise((resolve) => { 90 | store.commit('DEL_VISITED_VIEWS', view); 91 | resolve(); 92 | }); 93 | } 94 | 95 | @Action 96 | delOthersViews(view: Route) { 97 | return new Promise((resolve) => { 98 | store.commit('DEL_OTHERS_VIEWS', view); 99 | resolve(); 100 | }); 101 | } 102 | 103 | @Action 104 | delAllViews() { 105 | return new Promise((resolve) => { 106 | store.commit('DEL_ALL_VIEWS'); 107 | resolve(); 108 | }); 109 | } 110 | } 111 | 112 | export const TagsViewModule = getModule(TagsView.prototype); 113 | -------------------------------------------------------------------------------- /src/views/layout/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 63 | 64 | 110 | 111 | -------------------------------------------------------------------------------- /src/views/layout/components/Sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 81 | -------------------------------------------------------------------------------- /src/icons/components/coocaa.ts: -------------------------------------------------------------------------------- 1 | import icon from 'vue-svgicon'; 2 | 3 | icon.register({ 4 | coocaa: { 5 | width: 64, 6 | height: 64, 7 | viewBox: '0 0 1179 1024', 8 | data: '', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/views/changepwd.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 90 | 91 | 100 | -------------------------------------------------------------------------------- /src/api/passport.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request-passport'; 2 | import { 3 | Pageable, 4 | RedisValueInfo, 5 | ResponseResult, 6 | SysAccount, SysLoginLayoutListQuery, 7 | SysLoginLayoutModel, 8 | } from '@/types'; 9 | // @ts-ignore 10 | import qs from 'qs'; 11 | import {AxiosPromise, AxiosResponse} from 'axios'; 12 | 13 | /** 14 | * --------------------pay相关接口------------------------- 15 | */ 16 | export function getSysAccountInfoDetail(openId: string): AxiosPromise> { 17 | return request({ 18 | url: '/passport/sys-account/detail', 19 | method: 'get', 20 | params: {openId}, 21 | paramsSerializer(p: any) { 22 | return qs.stringify(p, {arrayFormat: 'repeat'}); 23 | }, 24 | }); 25 | } 26 | 27 | export function getMoblieCode(mobile: string): AxiosPromise> { 28 | return request({ 29 | url: '/passport/redis/mobile-code', 30 | method: 'get', 31 | params: {mobile}, 32 | paramsSerializer(p: any) { 33 | return qs.stringify(p, {arrayFormat: 'repeat'}); 34 | }, 35 | }); 36 | } 37 | 38 | export function delMobileCheck(mobile: string): AxiosPromise> { 39 | return request({ 40 | url: '/passport/redis/del-mobile-check', 41 | method: 'get', 42 | params: {mobile}, 43 | paramsSerializer(p: any) { 44 | return qs.stringify(p, {arrayFormat: 'repeat'}); 45 | }, 46 | }); 47 | } 48 | 49 | 50 | export function getLoginLayoutList(params: SysLoginLayoutListQuery): AxiosPromise>> { 51 | return request({ 52 | url: '/passport/sys-login-layout/list', 53 | method: 'get', 54 | params, 55 | paramsSerializer(p: any) { 56 | return qs.stringify(p, {arrayFormat: 'repeat'}); 57 | }, 58 | }); 59 | } 60 | 61 | 62 | export function getLoginLayoutDetail(id: number): AxiosPromise> { 63 | return request({ 64 | url: '/passport/sys-login-layout/detail', 65 | method: 'get', 66 | params: {id}, 67 | paramsSerializer(p: any) { 68 | return qs.stringify(p, {arrayFormat: 'repeat'}); 69 | }, 70 | }); 71 | } 72 | 73 | export function addNewLoginLayout(data: SysLoginLayoutModel): AxiosPromise> { 74 | return request({ 75 | url: '/passport/edit/sys-login-layout/add', 76 | method: 'post', 77 | data, 78 | }); 79 | } 80 | 81 | export function updateLoginLayout(data: SysLoginLayoutModel): AxiosPromise> { 82 | return request({ 83 | url: '/passport/edit/sys-login-layout/update', 84 | method: 'post', 85 | data, 86 | }); 87 | } 88 | 89 | export function publisLoginLayoutTest(data: SysLoginLayoutModel): AxiosPromise> { 90 | return request({ 91 | url: '/passport/edit/sys-login-layout/publish-test', 92 | method: 'post', 93 | data, 94 | }); 95 | } 96 | 97 | export function publisLoginLayout(id: number): AxiosPromise> { 98 | return request({ 99 | url: '/passport/audit/sys-login-layout/publish', 100 | method: 'get', 101 | params: {id}, 102 | }); 103 | } 104 | 105 | export function offlineLoginLayout(id: number): AxiosPromise> { 106 | return request({ 107 | url: '/passport/audit/sys-login-layout/offline', 108 | method: 'get', 109 | params: {id}, 110 | }); 111 | } 112 | -------------------------------------------------------------------------------- /src/icons/svg/机器人.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | .el-submenu__title{ 2 | border-bottom: 1px solid $ul-border-right-color; 3 | } 4 | #app { 5 | // 主体区域 6 | .main-container { 7 | min-height: 100%; 8 | transition: margin-left .28s; 9 | margin-left: 180px; 10 | position: relative; 11 | } 12 | // 侧边栏 13 | .sidebar-container { 14 | transition: width 0.28s; 15 | width: 180px !important; 16 | height: 100%; 17 | position: fixed; 18 | font-size: 0px; 19 | top: 50px; 20 | bottom: 0; 21 | left: 0; 22 | z-index: 1001; 23 | overflow: hidden; 24 | // reset element-ui css 25 | .horizontal-collapse-transition { 26 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; 27 | } 28 | .scrollbar-wrapper { 29 | height: calc(100% + 15px); 30 | .el-scrollbar__view { 31 | height: 100%; 32 | } 33 | } 34 | .is-horizontal { 35 | display: none; 36 | } 37 | a { 38 | display: inline-block; 39 | width: 100%; 40 | overflow: hidden; 41 | //border-bottom: 1px solid $ul-border-right-color; 42 | } 43 | .svg-icon { 44 | margin-right: 16px; 45 | } 46 | .el-menu { 47 | border: none; 48 | height: 100%; 49 | width: 100% !important; 50 | } 51 | } 52 | .hideSidebar { 53 | .sidebar-container { 54 | width: 36px !important; 55 | } 56 | .main-container { 57 | margin-left: 36px; 58 | } 59 | .submenu-title-noDropdown { 60 | padding-left: 10px !important; 61 | position: relative; 62 | .el-tooltip { 63 | padding: 0 10px !important; 64 | } 65 | } 66 | .el-submenu { 67 | overflow: hidden; 68 | &>.el-submenu__title { 69 | padding-left: 10px !important; 70 | .el-submenu__icon-arrow { 71 | display: none; 72 | } 73 | } 74 | } 75 | .el-menu--collapse { 76 | .el-submenu { 77 | &>.el-submenu__title { 78 | &>span { 79 | height: 0; 80 | width: 0; 81 | overflow: hidden; 82 | visibility: hidden; 83 | display: inline-block; 84 | } 85 | } 86 | } 87 | } 88 | } 89 | .sidebar-container .nest-menu .el-submenu>.el-submenu__title, 90 | .sidebar-container .el-submenu .el-menu-item { 91 | min-width: 180px !important; 92 | background-color: $subMenuBg !important; 93 | &:hover { 94 | background-color: $menuHover !important; 95 | } 96 | } 97 | .el-menu--collapse .el-menu .el-submenu { 98 | min-width: 180px !important; 99 | } 100 | 101 | // 适配移动端 102 | .mobile { 103 | .main-container { 104 | margin-left: 0px; 105 | } 106 | .sidebar-container { 107 | transition: transform .28s; 108 | width: 180px !important; 109 | } 110 | &.hideSidebar { 111 | .sidebar-container { 112 | transition-duration: 0.3s; 113 | transform: translate3d(-180px, 0, 0); 114 | } 115 | } 116 | } 117 | .withoutAnimation { 118 | .main-container, 119 | .sidebar-container { 120 | transition: none; 121 | } 122 | } 123 | //调整保护色更改 124 | .sidebar-container .el-menu{ 125 | background: $menuBg; 126 | border-right:1px solid $ul-border-right-color; 127 | } 128 | .sidebar-container .menu-wrapper{ 129 | //border-bottom: 1px solid $ul-border-right-color; 130 | } 131 | .sidebar-container .el-menu-item{ 132 | border-bottom: 1px solid $ul-border-right-color; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/views/account/editUserRole.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 98 | 99 | 113 | -------------------------------------------------------------------------------- /src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | import {ElDatePicker} from 'element-ui/types/date-picker'; 2 | 3 | export function isValidUsername(str: string) { 4 | // const validMap = ['admin', 'editor']; 5 | // return validMap.indexOf(str.trim()) >= 0; 6 | return str.length > 3; 7 | } 8 | 9 | export function validateURL(textval: string) { 10 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; 11 | return urlregex.test(textval); 12 | } 13 | 14 | export function validateLowerCase(str: string) { 15 | const reg = /^[a-z]+$/; 16 | return reg.test(str); 17 | } 18 | 19 | export function validateUpperCase(str: string) { 20 | const reg = /^[A-Z]+$/; 21 | return reg.test(str); 22 | } 23 | 24 | export function validatAlphabets(str: string) { 25 | const reg = /^[A-Za-z]+$/; 26 | return reg.test(str); 27 | } 28 | export function validPhoneNo(str: string) { 29 | const reg = /^1\d{10}$/; 30 | return reg.test(str); 31 | } 32 | 33 | export function validPhoneNoRule(rule: any, value: string, callback: ((cc?: Error) => void)) { 34 | if (!validPhoneNo(value)) { 35 | callback(new Error('请输入正确的手机号')); 36 | } else { 37 | callback(); 38 | } 39 | } 40 | 41 | export function pickerOptions(): any { 42 | const CALMONTH = 25; 43 | const getNowDate = () => { 44 | const nowDate = new Date(); 45 | nowDate.setHours(0); 46 | nowDate.setMinutes(0); 47 | nowDate.setSeconds(0); 48 | nowDate.setMilliseconds(0); 49 | nowDate.setTime(nowDate.getTime() + 3600 * 24 * 1000); 50 | return nowDate; 51 | }; 52 | return { 53 | shortcuts: [{ 54 | text: '最近一周', 55 | onClick(picker: ElDatePicker) { 56 | const end = getNowDate(); 57 | const start = new Date(end.getTime() - 3600 * 1000 * 24 * 7); 58 | picker.$emit('pick', [start, end]); 59 | }, 60 | }, { 61 | text: '上一个周期', 62 | onClick(picker: ElDatePicker) { 63 | const end = getNowDate(); 64 | if (end.getDate() <= CALMONTH) { 65 | end.setMonth(end.getMonth() - 1); 66 | } 67 | end.setDate(CALMONTH); 68 | 69 | const start = new Date(end.getTime()); 70 | start.setMonth(end.getMonth() - 1); 71 | end.setDate(end.getDate() + 1); 72 | picker.$emit('pick', [start, end]); 73 | }, 74 | }, { 75 | text: '上三个周期', 76 | onClick(picker: ElDatePicker) { 77 | const end = getNowDate(); 78 | if (end.getDate() <= CALMONTH) { 79 | end.setMonth(end.getMonth() - 1); 80 | } 81 | end.setDate(CALMONTH); 82 | const start = new Date(end.getTime()); 83 | start.setMonth(end.getMonth() - 3); 84 | end.setDate(end.getDate() + 1); 85 | picker.$emit('pick', [start, end]); 86 | }, 87 | }, { 88 | text: '近一个周期', 89 | onClick(picker: ElDatePicker) { 90 | const end = getNowDate(); 91 | end.setDate(CALMONTH); 92 | 93 | const start = new Date(end.getTime()); 94 | start.setMonth(end.getMonth() - 1); 95 | end.setDate(end.getDate() + 1); 96 | picker.$emit('pick', [start, end]); 97 | }, 98 | }, { 99 | text: '近三个周期', 100 | onClick(picker: ElDatePicker) { 101 | const end = getNowDate(); 102 | end.setDate(CALMONTH); 103 | const start = new Date(end.getTime()); 104 | start.setMonth(end.getMonth() - 3); 105 | end.setDate(end.getDate() + 1); 106 | picker.$emit('pick', [start, end]); 107 | }, 108 | }], 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /src/api/pay.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request-pay'; 2 | import { 3 | CardInfo, 4 | CardInfoListQuery, 5 | OrderInfo, 6 | OrderInfoListQuery, OrderRefundInfo, OrderRefundInfoListQuery, 7 | Pageable, 8 | ResponseResult, StatusInfo, 9 | UserInfoFull, 10 | } from '@/types'; 11 | // @ts-ignore 12 | import qs from 'qs'; 13 | import {AxiosPromise, AxiosResponse} from 'axios'; 14 | import {AppModule} from '@/store/modules/app'; 15 | 16 | /** 17 | * --------------------pay相关接口------------------------- 18 | */ 19 | export function getOrderInfoList(params: OrderInfoListQuery): AxiosPromise>> { 20 | return request({ 21 | url: '/pay/order/list', 22 | method: 'get', 23 | params, 24 | paramsSerializer(p: any) { 25 | return qs.stringify(p, {arrayFormat: 'repeat'}); 26 | }, 27 | }); 28 | } 29 | 30 | export function getOrderInfo(orderId: number): AxiosPromise> { 31 | return request({ 32 | url: '/pay/order/detail', 33 | method: 'get', 34 | params: {orderId}, 35 | paramsSerializer(p: any) { 36 | return qs.stringify(p, {arrayFormat: 'repeat'}); 37 | }, 38 | }); 39 | } 40 | 41 | export function getOrderInfoByorigiOrderNo(origiOrderNo: string): AxiosPromise> { 42 | return request({ 43 | url: '/pay/order/detail', 44 | method: 'get', 45 | params: {origiOrderNo}, 46 | paramsSerializer(p: any) { 47 | return qs.stringify(p, {arrayFormat: 'repeat'}); 48 | }, 49 | }); 50 | } 51 | 52 | export function getUserInfoFullByOpenId(openId: string): AxiosPromise> { 53 | return request({ 54 | url: '/pay/user-info/detail', 55 | method: 'get', 56 | params: {openId}, 57 | paramsSerializer(p: any) { 58 | return qs.stringify(p, {arrayFormat: 'repeat'}); 59 | }, 60 | }); 61 | } 62 | 63 | export function orderStatusClass(orderStatus: string): string { 64 | if (orderStatus === 'CC00502') { 65 | return 'warning-row'; 66 | } else if (orderStatus === 'CC00503') { 67 | return 'success-row'; 68 | } 69 | return ''; 70 | } 71 | 72 | export function orderStatusName(code: string) { 73 | // let name = code; 74 | // AppModule.orderStatus.forEach((item) => { 75 | // if (item.value === code) { 76 | // name = item.label; 77 | // return false; 78 | // } 79 | // }); 80 | // return name; 81 | return statusToName(code, AppModule.orderStatus); 82 | 83 | } 84 | 85 | export function ossOrderSourceName(code: number) { 86 | // let name = code + ''; 87 | // AppModule.ossOrderSource.forEach((item) => { 88 | // if (item.value === code) { 89 | // name = item.label; 90 | // return false; 91 | // } 92 | // }); 93 | // return name; 94 | return statusToName(code, AppModule.ossOrderSource); 95 | } 96 | 97 | export function cardInfoStatusName(code: number) { 98 | return statusToName(code, AppModule.cardInfoStatus); 99 | } 100 | 101 | export function cardBatchStatusName(code: number) { 102 | return statusToName(code, AppModule.cardBatchStatus); 103 | } 104 | 105 | export function layoutStatusName(code: number) { 106 | return statusToName(code, AppModule.layoutStatus); 107 | } 108 | 109 | export function statusToName(code: number | string, valus: StatusInfo[]) { 110 | let name = code + ''; 111 | const strCode = code + ''; 112 | valus.forEach((item) => { 113 | if (item.value === strCode) { 114 | name = item.label; 115 | return false; 116 | } 117 | }); 118 | return name; 119 | } 120 | 121 | export function getCardInfoList(params: CardInfoListQuery): AxiosPromise>> { 122 | return request({ 123 | url: '/pay/card-info/list', 124 | method: 'get', 125 | params, 126 | paramsSerializer(p: any) { 127 | return qs.stringify(p, {arrayFormat: 'repeat'}); 128 | }, 129 | }); 130 | } 131 | 132 | export function getOrderRefundInfoList(params: OrderRefundInfoListQuery): AxiosPromise>> { 133 | return request({ 134 | url: '/pay/order-refund/list', 135 | method: 'get', 136 | params, 137 | paramsSerializer(p: any) { 138 | return qs.stringify(p, {arrayFormat: 'repeat'}); 139 | }, 140 | }); 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/views/app/edit.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 | 113 | -------------------------------------------------------------------------------- /src/views/role/edit.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 112 | -------------------------------------------------------------------------------- /src/views/account/edit.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 109 | 110 | -------------------------------------------------------------------------------- /src/api/account.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request-account'; 2 | import {AccountInfo, ApplicationInfo, Pageable, ResponseResult, RoleInfo} from '@/types/index'; 3 | // @ts-ignore 4 | import qs from 'qs'; 5 | import {AxiosPromise, AxiosResponse} from 'axios'; 6 | 7 | /** 8 | * --------------------account相关接口------------------------- 9 | */ 10 | export function getList(params: any): AxiosPromise>> { 11 | return request({ 12 | url: '/api/user/list', 13 | method: 'get', 14 | params, 15 | paramsSerializer(p: any) { 16 | return qs.stringify(p, {arrayFormat: 'repeat'}); 17 | }, 18 | }); 19 | } 20 | 21 | export function deleteUser(ids: Array) { 22 | return request({ 23 | url : '/api/user/delete', 24 | method: 'get', 25 | params: {ids}, 26 | paramsSerializer(p: any) { 27 | return qs.stringify(p, {arrayFormat: 'repeat'}); 28 | }, 29 | }); 30 | } 31 | 32 | export function saveUser(data: AccountInfo) { 33 | return request({ 34 | url : '/api/user/update', 35 | method: 'post', 36 | data, 37 | }); 38 | } 39 | export function getAccountInfo(id: number): AxiosPromise> { 40 | return request({ 41 | url : '/api/user/detail', 42 | method: 'post', 43 | params: {id}, 44 | }); 45 | } 46 | 47 | export function getAccountRoles(id: number): AxiosPromise> { 48 | return request({ 49 | url : '/api/user/roles', 50 | method: 'get', 51 | params: {id}, 52 | }); 53 | } 54 | 55 | export function saveAccountRoles(accountId: number, roleIds: Array): AxiosPromise> { 56 | return request({ 57 | url: '/api/user/save/roles', 58 | method: 'get', 59 | params : {accountId, roleIds}, 60 | paramsSerializer(p: any) { 61 | return qs.stringify(p, {arrayFormat: 'repeat'}); 62 | }, 63 | }); 64 | } 65 | 66 | export function newUser(data: AccountInfo) { 67 | return request({ 68 | url : '/api/user/new', 69 | method: 'post', 70 | data, 71 | }); 72 | } 73 | 74 | /** 75 | * --------------------account相关接口------------------------- 76 | */ 77 | 78 | /** 79 | * --------------------role相关接口------------------------- 80 | */ 81 | /** 82 | * 获取所有的角色信息 83 | * @param params 84 | */ 85 | export function getRoleList(params: any) { 86 | return request({ 87 | url: '/api/role/list', 88 | method: 'get', 89 | params, 90 | }); 91 | } 92 | 93 | export function getAllValidRoleList() { 94 | return request({ 95 | url: '/api/role/valid/list', 96 | method: 'get', 97 | }); 98 | } 99 | 100 | export function deleteRole(ids: Array) { 101 | return request({ 102 | url : '/api/role/delete', 103 | method: 'get', 104 | params: {ids}, 105 | paramsSerializer(p: any) { 106 | return qs.stringify(p, {arrayFormat: 'repeat'}); 107 | }, 108 | }); 109 | } 110 | 111 | export function saveRole(data: RoleInfo) { 112 | return request({ 113 | url : '/api/role/update', 114 | method: 'post', 115 | data, 116 | }); 117 | } 118 | 119 | export function newRole(data: RoleInfo) { 120 | return request({ 121 | url : '/api/role/new', 122 | method: 'post', 123 | data, 124 | }); 125 | } 126 | 127 | 128 | export function getRoleInfo(id: number): AxiosPromise> { 129 | return request({ 130 | url : '/api/role/detail', 131 | method: 'post', 132 | params: {id}, 133 | }); 134 | } 135 | /** 136 | * --------------------role相关接口------------------------- 137 | */ 138 | 139 | /** 140 | * --------------------application相关接口------------------------- 141 | */ 142 | export function getAppList(params: any) { 143 | return request({ 144 | url: '/api/application/list', 145 | method: 'get', 146 | params, 147 | }); 148 | } 149 | 150 | export function deleteApp(ids: Array) { 151 | return request({ 152 | url : '/api/application/delete', 153 | method: 'get', 154 | params: {ids}, 155 | paramsSerializer(p: any) { 156 | return qs.stringify(p, {arrayFormat: 'repeat'}); 157 | }, 158 | }); 159 | } 160 | export function getAppInfo(id: number): AxiosPromise> { 161 | return request({ 162 | url : '/api/application/detail', 163 | method: 'post', 164 | params: {id}, 165 | }); 166 | } 167 | export function saveApp(data: ApplicationInfo) { 168 | return request({ 169 | url : '/api/application/update', 170 | method: 'post', 171 | data, 172 | }); 173 | } 174 | 175 | export function newApp(data: ApplicationInfo) { 176 | return request({ 177 | url : '/api/application/new', 178 | method: 'post', 179 | data, 180 | }); 181 | } 182 | /** 183 | * --------------------application相关接口------------------------- 184 | */ 185 | -------------------------------------------------------------------------------- /src/store/modules/app.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie'; 2 | import {VuexModule, Module, Mutation, Action, getModule, MutationAction} from 'vuex-module-decorators'; 3 | import store from '@/store'; 4 | import {ElementUIComponentSize} from 'element-ui/types/component'; 5 | import {getToken, removeToken} from '@/utils/auth'; 6 | import {logout} from '@/api/login'; 7 | import {StatusInfo} from '@/types'; 8 | import {ElDatePicker} from 'element-ui/types/date-picker'; 9 | 10 | export enum DeviceType { 11 | Mobile, 12 | Desktop, 13 | } 14 | 15 | export interface IAppState { 16 | device: DeviceType; 17 | sidebar: { 18 | opened: boolean; 19 | withoutAnimation: boolean; 20 | }; 21 | formSize: ElementUIComponentSize; 22 | formLabelWidth: string; 23 | recordeStatus: StatusInfo[]; 24 | orderStatus: StatusInfo[]; 25 | appTypeCodeStatus: StatusInfo[]; 26 | ossOrderSource: StatusInfo[]; 27 | clientType: StatusInfo[]; 28 | orderType: StatusInfo[]; 29 | cardInfoStatus: StatusInfo[]; 30 | cardBatchStatus: StatusInfo[]; 31 | layoutStatus: StatusInfo[]; 32 | uploadAction: string; 33 | } 34 | 35 | @Module({dynamic: true, store, name: 'app'}) 36 | class App extends VuexModule { 37 | sidebar: IAppState['sidebar'] = { 38 | opened: Cookies.get('sidebarStatus') !== 'closed', 39 | withoutAnimation: false, 40 | }; 41 | device: IAppState['device'] = DeviceType.Desktop; 42 | formSize: IAppState['formSize'] = 'small'; 43 | formLabelWidth: IAppState['formLabelWidth'] = '120px'; 44 | recordeStatus: IAppState['recordeStatus'] = [{value: 1, label: '正常'}, {value: 2, label: '禁用'}]; 45 | orderStatus: IAppState['orderStatus'] = [ 46 | {value: 'CC00501', label: '待支付'}, 47 | {value: 'CC00502', label: '待授信'}, 48 | {value: 'CC00503', label: '完成'}, 49 | {value: 'CC00504', label: '取消 '}, 50 | {value: 'CC00505', label: '已发货'}, 51 | {value: 'CC00506', label: '支付中'}]; 52 | appTypeCodeStatus: IAppState['orderStatus'] = [ 53 | {value: 'CC01201', label: '影视'}, 54 | {value: 'CC01202', label: '游戏'}, 55 | {value: 'CC01203', label: 'PC商城'}, 56 | {value: 'CC01204', label: '购物'}, 57 | {value: 'CC01205', label: '教育'}, 58 | {value: 'CC01206', label: '其他'}, 59 | {value: 'CC01207', label: '旅游'}, 60 | {value: 'CC01208', label: '音乐'}, 61 | {value: 'CC01209', label: '广告'}, 62 | {value: 'CC01210', label: '体育'}, 63 | ]; 64 | ossOrderSource: IAppState['orderStatus'] = [ 65 | {value: '1', label: 'tv端'}, 66 | {value: '2', label: 'pc端'}, 67 | {value: '3', label: '自动续费'}, 68 | {value: '4', label: '移动端'}, 69 | {value: '5', label: 'v1版支付'}, 70 | ]; 71 | clientType: IAppState['clientType'] = [ 72 | // 下单设备类型(1TV,2天赐派,3微信,4tv-web版,5电视派APP,6IPtvWeb页面,7江苏广电线下订单,8教育订制机,9商城兑换,10试看出二维码已登录,11试看出二维码未登录) 73 | {value: '1', label: 'TV老版本'}, 74 | {value: '2', label: '天赐派'}, 75 | {value: '3', label: '微信'}, 76 | {value: '4', label: 'tv-web版'}, 77 | {value: '5', label: '电视派APP'}, 78 | {value: '6', label: 'IPtvWeb页面'}, 79 | {value: '7', label: '江苏广电线下订单'}, 80 | {value: '8', label: '教育订制机'}, 81 | {value: '9', label: '商城兑换'}, 82 | {value: '10', label: '试看出二维码已登录'}, 83 | {value: '11', label: '试看出二维码未登录'}, 84 | ]; 85 | orderType: IAppState['orderType'] = [ 86 | {value: '0', label: '未知类型'}, 87 | {value: '1', label: '普通支付'}, 88 | {value: '2', label: '卡密兑换'}, 89 | {value: '3', label: '用券兑换'}, 90 | {value: '4', label: '活动赠送'}, 91 | {value: '5', label: '线下导购'}, 92 | ]; 93 | cardInfoStatus: IAppState['cardInfoStatus'] = [ 94 | {value: '1', label: '正常'}, 95 | {value: '2', label: '开通中'}, 96 | {value: '3', label: '已开通'}, 97 | {value: '4', label: '作废'}, 98 | ]; 99 | cardBatchStatus: IAppState['cardBatchStatus'] = [ 100 | {value: '0', label: '未审核'}, 101 | {value: '1', label: '正常'}, 102 | {value: '2', label: '作废'}, 103 | ]; 104 | layoutStatus: IAppState['layoutStatus'] = [ 105 | {value: '0', label: '未审核'}, 106 | {value: '1', label: '测试中'}, 107 | {value: '2', label: '全网上线'}, 108 | {value: '3', label: '下线'}, 109 | ]; 110 | 111 | uploadAction: IAppState['uploadAction'] = process.env.VUE_APP_PASSPORT_API + 'api/authentication/file/upload'; 112 | 113 | @Mutation 114 | TOGGLE_SIDEBAR(withoutAnimation: boolean) { 115 | if (this.sidebar.opened) { 116 | Cookies.set('sidebarStatus', 'closed'); 117 | } else { 118 | Cookies.set('sidebarStatus', 'opened'); 119 | } 120 | this.sidebar.opened = !this.sidebar.opened; 121 | this.sidebar.withoutAnimation = withoutAnimation; 122 | } 123 | 124 | @Mutation 125 | CLOSE_SIDEBAR(withoutAnimation: boolean) { 126 | Cookies.set('sidebarStatus', 'closed'); 127 | this.sidebar.opened = false; 128 | this.sidebar.withoutAnimation = withoutAnimation; 129 | } 130 | 131 | @Mutation 132 | TOGGLE_DEVICE(device: DeviceType) { 133 | this.device = device; 134 | } 135 | 136 | @Action({commit: 'TOGGLE_SIDEBAR'}) 137 | ToggleSideBar(withoutAnimation: boolean) { 138 | return withoutAnimation; 139 | } 140 | 141 | @Action({commit: 'CLOSE_SIDEBAR'}) 142 | CloseSideBar(withoutAnimation: boolean) { 143 | return withoutAnimation; 144 | } 145 | 146 | @Action({commit: 'TOGGLE_DEVICE'}) 147 | ToggleDevice(device: DeviceType) { 148 | return device; 149 | } 150 | 151 | 152 | @MutationAction({mutate: ['formSize']}) 153 | async SetFormSize(size: ElementUIComponentSize) { 154 | return { 155 | formSize: size, 156 | }; 157 | } 158 | } 159 | 160 | export const AppModule = getModule(App.prototype); 161 | --------------------------------------------------------------------------------