├── public ├── robots.txt ├── favicon.ico ├── config.local.js ├── 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-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ └── msapplication-icon-144x144.png ├── vendor │ ├── element-icons.535877f.woff │ ├── element-icons.732389d.ttf │ ├── vendor-manifest.json │ └── vendor.dll.css ├── manifest.json └── index.html ├── .browserslistrc ├── .env.production ├── src ├── assets │ ├── img │ │ ├── logo.png │ │ ├── 401_images │ │ │ └── 401.gif │ │ └── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ ├── custom-theme │ │ └── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ ├── less │ │ ├── app.less │ │ ├── svg-icon.less │ │ ├── transition.less │ │ ├── element-ui.less │ │ ├── variable.less │ │ └── btn.less │ └── js │ │ ├── variable.ts │ │ └── export2Zip.js ├── icons │ ├── svg │ │ ├── chart.svg │ │ ├── size.svg │ │ ├── link.svg │ │ ├── guide.svg │ │ ├── component.svg │ │ ├── money.svg │ │ ├── email.svg │ │ ├── drag.svg │ │ ├── guide2.svg │ │ ├── documentation.svg │ │ ├── fullscreen.svg │ │ ├── user.svg │ │ ├── lock.svg │ │ ├── excel.svg │ │ ├── example.svg │ │ ├── star.svg │ │ ├── table.svg │ │ ├── search.svg │ │ ├── password.svg │ │ ├── tab.svg │ │ ├── message.svg │ │ ├── theme.svg │ │ ├── peoples.svg │ │ ├── edit.svg │ │ ├── nested.svg │ │ ├── tree-table.svg │ │ ├── eye.svg │ │ ├── clipboard.svg │ │ ├── list.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── wechat.svg │ │ ├── people.svg │ │ ├── language.svg │ │ ├── eye-open.svg │ │ ├── 404.svg │ │ ├── zip.svg │ │ ├── bug.svg │ │ ├── dog.svg │ │ ├── pdf.svg │ │ ├── exit-fullscreen.svg │ │ └── tree.svg │ └── components │ │ ├── chart.ts │ │ ├── size.ts │ │ ├── link.ts │ │ ├── guide.ts │ │ ├── component.ts │ │ ├── money.ts │ │ ├── drag.ts │ │ ├── email.ts │ │ ├── guide2.ts │ │ ├── documentation.ts │ │ ├── fullscreen.ts │ │ ├── user.ts │ │ ├── lock.ts │ │ ├── excel.ts │ │ ├── example.ts │ │ ├── star.ts │ │ ├── search.ts │ │ ├── table.ts │ │ ├── password.ts │ │ ├── tab.ts │ │ ├── message.ts │ │ ├── theme.ts │ │ ├── peoples.ts │ │ ├── edit.ts │ │ ├── nested.ts │ │ ├── index.ts │ │ ├── tree-table.ts │ │ ├── eye.ts │ │ ├── clipboard.ts │ │ ├── list.ts │ │ ├── icon.ts │ │ ├── international.ts │ │ ├── wechat.ts │ │ ├── people.ts │ │ ├── eye-open.ts │ │ ├── language.ts │ │ ├── 404.ts │ │ ├── zip.ts │ │ ├── bug.ts │ │ ├── dog.ts │ │ └── pdf.ts ├── components │ ├── ImageCropper │ │ └── utils │ │ │ ├── mimes.ts │ │ │ └── effectRipple.ts │ ├── Tinymce │ │ ├── toolbar.ts │ │ └── plugins.ts │ ├── MarkdownEditor │ │ └── defaultOptions.ts │ ├── Charts │ │ └── mixins │ │ │ └── resize.ts │ ├── ScreenFull │ │ └── index.vue │ ├── LangSelect │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── TreeTable │ │ └── eval.ts │ ├── SizeSelect │ │ └── index.vue │ └── DragSelect │ │ └── index.vue ├── shims-vue.d.ts ├── views │ ├── example │ │ ├── components │ │ │ ├── Dropdown │ │ │ │ ├── index.ts │ │ │ │ ├── Comment.vue │ │ │ │ ├── SourceUrl.vue │ │ │ │ └── Platform.vue │ │ │ └── Warning.vue │ │ ├── edit.vue │ │ └── create.vue │ ├── svgIcons │ │ └── requireIcons.ts │ ├── nested │ │ ├── menu1 │ │ │ ├── index.vue │ │ │ ├── menu1-3 │ │ │ │ └── index.vue │ │ │ ├── menu1-2 │ │ │ │ ├── menu1-2-2 │ │ │ │ │ └── index.vue │ │ │ │ ├── menu1-2-1 │ │ │ │ │ └── index.vue │ │ │ │ └── index.vue │ │ │ └── menu1-1 │ │ │ │ └── index.vue │ │ └── menu2 │ │ │ └── index.vue │ ├── login │ │ ├── authredirect.vue │ │ └── socialSignIn.vue │ ├── redirect │ │ └── index.vue │ ├── errorLog │ │ ├── errorTestA.vue │ │ ├── errorTestB.vue │ │ └── index.vue │ ├── permission │ │ ├── page.vue │ │ └── components │ │ │ └── SwitchRoles.vue │ ├── pdf │ │ └── index.vue │ ├── charts │ │ ├── mixChart.vue │ │ ├── line.vue │ │ └── keyboard.vue │ ├── table │ │ └── dynamicTable │ │ │ ├── index.vue │ │ │ └── unfixedThead.vue │ ├── excel │ │ ├── components │ │ │ ├── AutoWidthOption.vue │ │ │ ├── FilenameOption.vue │ │ │ └── BookTypeOption.vue │ │ └── uploadExcel.vue │ ├── dashboard │ │ ├── index.vue │ │ └── admin │ │ │ └── components │ │ │ └── TransactionTable.vue │ ├── treeTable │ │ ├── custom │ │ │ └── data.ts │ │ └── data.ts │ ├── guide │ │ ├── index.vue │ │ └── defineSteps.ts │ ├── componentsDemo │ │ ├── dropZone.vue │ │ ├── dragSelect.vue │ │ ├── dndList.vue │ │ ├── tinymce.vue │ │ ├── jsonEditor.vue │ │ ├── avatarUpload.vue │ │ ├── dragKanban.vue │ │ └── splitPane.vue │ ├── clipboard │ │ └── index.vue │ ├── tab │ │ └── index.vue │ └── i18n-demo │ │ └── local.ts ├── interface │ └── index.ts ├── layout │ ├── components │ │ ├── index.ts │ │ ├── Sidebar │ │ │ ├── Link.vue │ │ │ ├── FixiOSBug.ts │ │ │ └── index.vue │ │ └── AppMain.vue │ └── mixin │ │ └── ResizeHandler.ts ├── utils │ ├── getPageTitle.ts │ ├── permission.ts │ ├── clipboard.ts │ ├── validate.ts │ ├── auth.ts │ └── scrollTo.js ├── shims-tsx.d.ts ├── services │ ├── autoMatchBaseUrl.ts │ ├── RESTFULLURL.ts │ └── index.ts ├── constant.ts ├── directives │ ├── index.ts │ ├── permission.ts │ ├── waves │ │ ├── waves.css │ │ └── index.ts │ ├── el-table.ts │ └── clipboard.ts ├── App.vue ├── pwa │ ├── service-worker.js │ └── register-service-worker.ts ├── store │ ├── modules │ │ ├── errorLog.ts │ │ └── settings.ts │ ├── index.ts │ └── getters.ts ├── settings.ts ├── router │ └── modules │ │ ├── charts.ts │ │ └── table.ts ├── registerServiceWorker.ts ├── errorLog.ts ├── main.ts ├── lang │ └── index.ts └── filters │ └── index.ts ├── .travis.yml ├── .env.staging ├── babel.config.js ├── postcss.config.js ├── jsconfig.json ├── types ├── vue-global.d.ts └── index.d.ts ├── .gitignore ├── .env.development ├── .editorconfig ├── tslint.json ├── README.md ├── LICENSE ├── tsconfig.json └── mock ├── remoteSearch.ts └── user.ts /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/config.local.js: -------------------------------------------------------------------------------- 1 | window.LOCAL_CONFIG = { 2 | API_HOME: 'http://localhost:5577/dev-api' 3 | }; 4 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/prod-api' 6 | -------------------------------------------------------------------------------- /src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/img/logo.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /src/assets/img/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/img/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/img/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/img/404_images/404.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | '@babel/preset-typescript' 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /public/vendor/element-icons.535877f.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/vendor/element-icons.535877f.woff -------------------------------------------------------------------------------- /public/vendor/element-icons.732389d.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/vendor/element-icons.732389d.ttf -------------------------------------------------------------------------------- /src/assets/img/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/img/404_images/404_cloud.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cklwblove/vue-element-admin-ts/HEAD/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ImageCropper/utils/mimes.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | }; 8 | -------------------------------------------------------------------------------- /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 | 11 | -------------------------------------------------------------------------------- /src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/less/app.less: -------------------------------------------------------------------------------- 1 | @import "./variable"; 2 | @import "~mixins"; 3 | @import "./transition"; 4 | @import "./svg-icon"; 5 | @import "./element-ui"; 6 | @import "./sidebar"; 7 | @import "./btn"; 8 | 9 | @import "main"; 10 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | autoprefixer: {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": [ 6 | "src/*" 7 | ] 8 | } 9 | }, 10 | "exclude": [ 11 | "node_modules", 12 | "dist" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/views/example/components/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CommentDropdown } from './Comment.vue'; 2 | export { default as PlatformDropdown } from './Platform.vue'; 3 | export { default as SourceUrlDropdown } from './SourceUrl.vue'; 4 | -------------------------------------------------------------------------------- /types/vue-global.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | declare module 'vue/types/vue' { 4 | // 可以使用 `VueConstructor` 接口 5 | // 来声明全局属性 6 | interface Vue { 7 | readonly $services: any; 8 | readonly $ELEMENT: any; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/interface/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 定义接口类 3 | * 最佳实践 -> 接口名字一般建议 I 开头,便于别人阅读 4 | */ 5 | 6 | export interface IDataValues { 7 | year: string; 8 | value: number; 9 | } 10 | 11 | export interface IListQuery { 12 | page: number; 13 | limit: number; 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/js/variable.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | menuText: '#bfcbd9', 3 | menuActiveText: '#409EFF', 4 | subMenuActiveText: '#f4f4f5', 5 | menuBg: '#304156', 6 | menuHover: '#263445', 7 | subMenuBg: '#1f2d3d', 8 | subMenuHover: '#001528', 9 | sideBarWidth: '180px' 10 | }; 11 | -------------------------------------------------------------------------------- /src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | LOCAL_CONFIG?: any; 3 | tinymce?: any; 4 | webkitURL?: any; 5 | } 6 | 7 | declare module '*.png'; 8 | 9 | declare module '*.gif'; 10 | 11 | declare module 'vue-count-to'; 12 | 13 | declare module 'vuedraggable'; 14 | 15 | declare module 'vue-splitpane'; 16 | -------------------------------------------------------------------------------- /src/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar.vue'; 2 | export { default as Sidebar } from './Sidebar/index.vue'; 3 | export { default as TagsView } from './TagsView/index.vue'; 4 | export { default as AppMain } from './AppMain.vue'; 5 | export { default as Settings } from './Settings/index.vue'; 6 | -------------------------------------------------------------------------------- /src/views/svgIcons/requireIcons.ts: -------------------------------------------------------------------------------- 1 | const req = require.context('../../icons/svg', false, /\.svg$/); 2 | const requireAll = (requireContext) => requireContext.keys(); 3 | 4 | const re = /\.\/(.*)\.svg/; 5 | 6 | const icons = requireAll(req).map((i) => { 7 | return i.match(re)[1]; 8 | }); 9 | 10 | export default icons; 11 | -------------------------------------------------------------------------------- /src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/getPageTitle.ts: -------------------------------------------------------------------------------- 1 | import i18n from '@/lang'; 2 | import settings from '@/settings'; 3 | 4 | export default function getPageTitle(key: string) { 5 | const hasKey = i18n.te(`route.${key}`); 6 | if (hasKey) { 7 | const pageName = i18n.t(`route.${key}`); 8 | return `${pageName} - ${settings.title}`; 9 | } 10 | return `${settings.title}`; 11 | } 12 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue'; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/icons/components/chart.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'chart': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/guide2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/views/login/authredirect.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /src/icons/components/size.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'size': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /src/views/errorLog/errorTestA.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /src/views/errorLog/errorTestB.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/services/autoMatchBaseUrl.ts: -------------------------------------------------------------------------------- 1 | import {UPLOAD_PREFIX} from '../constant'; 2 | 3 | // 根据前缀,自动匹配基础的url 4 | export default function autoMatchBaseUrl(prefix: string) { 5 | let baseUrl = ''; 6 | switch (prefix) { 7 | case UPLOAD_PREFIX: 8 | prefix = ''; 9 | baseUrl = window.LOCAL_CONFIG.API_UPLOAD; 10 | break; 11 | default: 12 | baseUrl = window.LOCAL_CONFIG.API_HOME; 13 | } 14 | 15 | return `${baseUrl}`; 16 | } 17 | -------------------------------------------------------------------------------- /src/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @authors liwb (lwbhtml@gmail.com) 4 | * @date 2018/6/5 上午10:43 5 | * @description 定义项目所需常量 6 | */ 7 | 8 | const TIMEOUT: number = 25000; 9 | const PAGE_NUM: number = 15; 10 | const UPLOAD_PREFIX: string = 'upload/'; 11 | const HOME_PREFIX: string = 'home/'; 12 | const SUCCESS_STATUS = 20000; 13 | 14 | export { 15 | TIMEOUT, 16 | PAGE_NUM, 17 | UPLOAD_PREFIX, 18 | HOME_PREFIX, 19 | SUCCESS_STATUS 20 | }; 21 | -------------------------------------------------------------------------------- /src/services/RESTFULLURL.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | loginByUsername: '/user/login', 3 | logout: '/user/logout', 4 | getUserInfo: '/user/info/', 5 | getList: '/transaction/list', 6 | search: '/search/user', 7 | articleList: '/article/list', 8 | articleDetail: '/article/detail', 9 | articlePv: '/article/pv', 10 | articleCreate: '/article/create', 11 | articleUpdate: '/article/update', 12 | getRoutes: '/routes', 13 | roles: '/roles', 14 | }; 15 | -------------------------------------------------------------------------------- /src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import request from './request'; 3 | import urls from './RESTFULLURL'; 4 | 5 | const FUNS: any = {}; 6 | 7 | Object.keys(urls).forEach((key) => { 8 | FUNS[key] = (options = {}) => { 9 | return request(urls[key], options); 10 | }; 11 | }); 12 | 13 | // 将services挂载到vue的原型上 14 | // views引用的方法:this.$services.接口名(小驼峰) 15 | Object.defineProperty(Vue.prototype, '$services', {value: FUNS}); 16 | 17 | export default FUNS; 18 | -------------------------------------------------------------------------------- /src/directives/index.ts: -------------------------------------------------------------------------------- 1 | import Vue, { DirectiveOptions } from 'vue'; 2 | 3 | import permission from './permission'; 4 | import waves from './waves'; 5 | import elDragDialog from './el-dragDialog'; 6 | import clipboard from './clipboard'; 7 | 8 | const directives = { 9 | permission, 10 | waves, 11 | elDragDialog, 12 | clipboard 13 | }; 14 | 15 | Object.keys(directives).forEach((key) => { 16 | Vue.directive(key, (directives as { [key: string]: DirectiveOptions })[key]); 17 | }); 18 | -------------------------------------------------------------------------------- /src/views/example/edit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/views/example/create.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/icons/components/link.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'link': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Tinymce/toolbar.ts: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']; 5 | 6 | export default toolbar; 7 | -------------------------------------------------------------------------------- /src/icons/components/guide.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'guide': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/component.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'component': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/money.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'money': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/drag.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'drag': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/email.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'email': { 7 | width: 128, 8 | height: 96, 9 | viewBox: '0 0 128 96', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/permission/page.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli3-typescript-template", 3 | "short_name": "vue-cli3-typescript-template", 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 | } 21 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /src/views/pdf/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | 23 | # Optional npm cache directory 24 | .npm 25 | 26 | # Optional eslint cache 27 | .eslintcache 28 | 29 | # Optional REPL history 30 | .node_repl_history 31 | 32 | # Output of 'npm pack' 33 | *.tgz 34 | 35 | .project 36 | .happypack 37 | .history 38 | yarn.lock 39 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= webpackConfig.name %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/icons/components/guide2.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'guide2': { 7 | width: 1000, 8 | height: 1000, 9 | viewBox: '0 0 1000 1000', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/dev-api' 6 | 7 | # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, 8 | # to control whether the babel-plugin-dynamic-import-node plugin is enabled. 9 | # It only does one thing by converting all import() to require(). 10 | # This configuration can significantly increase the speed of hot updates, 11 | # when you have a large number of pages. 12 | # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js 13 | 14 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 15 | -------------------------------------------------------------------------------- /src/assets/less/svg-icon.less: -------------------------------------------------------------------------------- 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: middle; 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/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Tinymce/plugins.ts: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']; 6 | 7 | export default plugins; 8 | -------------------------------------------------------------------------------- /src/views/charts/mixChart.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /src/views/example/components/Warning.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/charts/line.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 29 | -------------------------------------------------------------------------------- /src/icons/components/documentation.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'documentation': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/fullscreen.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'fullscreen': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/pwa/service-worker.js: -------------------------------------------------------------------------------- 1 | // This is the code piece that GenerateSW mode can't provide for us. 2 | // This code listens for the user's confirmation to update the app. 3 | self.addEventListener('message', (e) => { 4 | if (e.data) { 5 | if (e.data === 'skipWaiting') { 6 | self.skipWaiting(); 7 | } 8 | } 9 | }); 10 | 11 | workbox.clientsClaim(); 12 | 13 | // The precaching code provided by Workbox. You don't need to change this part. 14 | self.__precacheManifest = [].concat(self.__precacheManifest || []); 15 | workbox.precaching.suppressWarnings(); 16 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); 17 | -------------------------------------------------------------------------------- /src/icons/components/user.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'user': { 7 | width: 130, 8 | height: 130, 9 | viewBox: '0 0 130 130', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/lock.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'lock': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/charts/keyboard.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /src/icons/components/excel.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'excel': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/modules/errorLog.ts: -------------------------------------------------------------------------------- 1 | import { getModule, Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'; 2 | import store from '@/store'; 3 | 4 | export interface IErrorLogState { 5 | logs: any[]; 6 | } 7 | 8 | @Module({dynamic: true, store, name: 'errorLog'}) 9 | class ErrorLog extends VuexModule implements IErrorLogState { 10 | logs: any[] = []; 11 | 12 | @Action({commit: 'ADD_ERROR_LOG'}) 13 | AddErrorLog(log) { 14 | return log; 15 | } 16 | 17 | @Mutation 18 | ADD_ERROR_LOG(log) { 19 | console.log('ADD_ERROR_LOG', this.logs); 20 | this.logs.push(log); 21 | } 22 | } 23 | 24 | export const ErrorLogModule = getModule(ErrorLog); 25 | -------------------------------------------------------------------------------- /src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/example.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'example': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/table/dynamicTable/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | -------------------------------------------------------------------------------- /src/assets/js/export2Zip.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { saveAs } from 'file-saver'; 3 | import JSZip from 'jszip'; 4 | 5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) { 6 | const zip = new JSZip(); 7 | const txt_name = txtName || 'file'; 8 | const zip_name = zipName || 'file'; 9 | const data = jsonData; 10 | let txtData = `${th}\r\n`; 11 | data.forEach((row) => { 12 | let tempStr = ''; 13 | tempStr = row.toString(); 14 | txtData += `${tempStr}\r\n`; 15 | }); 16 | zip.file(`${txt_name}.txt`, txtData); 17 | zip.generateAsync({ 18 | type: 'blob' 19 | }).then((blob) => { 20 | saveAs(blob, `${zip_name}.zip`); 21 | }, (err) => { 22 | alert('导出失败'); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/MarkdownEditor/defaultOptions.ts: -------------------------------------------------------------------------------- 1 | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor 2 | export default { 3 | minHeight: '200px', 4 | previewStyle: 'vertical', 5 | useCommandShortcut: true, 6 | useDefaultHTMLSanitizer: true, 7 | usageStatistics: false, 8 | hideModeSwitch: false, 9 | toolbarItems: [ 10 | 'heading', 11 | 'bold', 12 | 'italic', 13 | 'strike', 14 | 'divider', 15 | 'hr', 16 | 'quote', 17 | 'divider', 18 | 'ul', 19 | 'ol', 20 | 'task', 21 | 'indent', 22 | 'outdent', 23 | 'divider', 24 | 'table', 25 | 'image', 26 | 'link', 27 | 'divider', 28 | 'code', 29 | 'codeblock' 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /src/directives/permission.ts: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | 3 | export default { 4 | inserted(el, binding, vnode) { 5 | const {value} = binding; 6 | const roles = store.getters && store.getters.roles; 7 | console.log('permission directive roles', roles); 8 | if (value && value instanceof Array && value.length > 0) { 9 | const permissionRoles = value; 10 | 11 | const hasPermission = roles.some((role) => { 12 | return permissionRoles.includes(role); 13 | }); 14 | 15 | if (!hasPermission) { 16 | el.parentNode && el.parentNode.removeChild(el); 17 | } 18 | } else { 19 | throw new Error(`need roles! Like v-permission="['admin','editor']"`); 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/icons/components/star.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'star': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/excel/components/AutoWidthOption.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | -------------------------------------------------------------------------------- /src/views/excel/components/FilenameOption.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | -------------------------------------------------------------------------------- /src/icons/components/search.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'search': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 34 | -------------------------------------------------------------------------------- /src/utils/permission.ts: -------------------------------------------------------------------------------- 1 | import store from '@/store'; 2 | 3 | /** 4 | * @param {Array} value 5 | * @returns {Boolean} 6 | * @example see @/views/permission/directive.vue 7 | */ 8 | export default function checkPermission(value) { 9 | // console.log('checkPermission value', value); 10 | if (value && value instanceof Array && value.length > 0) { 11 | const roles = store.getters && store.getters.roles; 12 | const permissionRoles = value; 13 | // console.log('checkPermission permissionRoles', value); 14 | // console.log('checkPermission roles', value); 15 | return roles.some((role) => { 16 | return permissionRoles.includes(role); 17 | }); 18 | } else { 19 | console.error(`need roles! Like v-permission="['admin','editor']"`); 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/icons/components/table.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'table': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/password.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'password': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/tab.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'tab': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import { IAppState } from './modules/app'; 4 | import { IErrorLogState } from './modules/errorLog'; 5 | import { ITagsViewState } from './modules/tagsView'; 6 | import { IUserState } from './modules/user'; 7 | import { IPermissionState } from './modules/permission'; 8 | import { ISettingsState } from './modules/settings'; 9 | import getters from './getters'; 10 | 11 | Vue.use(Vuex); 12 | 13 | export interface IRootState { 14 | app: IAppState; 15 | user: IUserState; 16 | permission: IPermissionState; 17 | tagsView: ITagsViewState; 18 | errorLog: IErrorLogState; 19 | settings: ISettingsState; 20 | } 21 | 22 | // Declare empty store first, dynamically register all modules later. 23 | const store = new Vuex.Store({getters}); 24 | 25 | export default store; 26 | -------------------------------------------------------------------------------- /src/views/excel/components/BookTypeOption.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 32 | -------------------------------------------------------------------------------- /src/icons/components/message.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'message': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://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/layout/components/Sidebar/FixiOSBug.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator'; 2 | 3 | @Component 4 | export default class FixiOSBugMixin extends Vue { 5 | get device() { 6 | return this.$store.getters.device; 7 | } 8 | 9 | mounted() { 10 | // In order to fix the click on menu on the ios device will trigger the mouseeleave bug 11 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 12 | this.fixBugIniOS(); 13 | } 14 | 15 | fixBugIniOS() { 16 | const $submenu = this.$refs.submenu; 17 | if ($submenu) { 18 | // @ts-ignore 19 | const handleMouseleave = $submenu.handleMouseleave; 20 | // @ts-ignore 21 | $submenu.handleMouseleave = (e) => { 22 | if (this.device === 'mobile') { 23 | return; 24 | } 25 | handleMouseleave(e); 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/icons/components/theme.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'theme': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/directives/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } 27 | -------------------------------------------------------------------------------- /src/icons/components/peoples.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'peoples': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/clipboard.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Clipboard from 'clipboard'; 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }); 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }); 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard: any = new Clipboard(event.target, { 21 | text: () => text 22 | }); 23 | clipboard.on('success', () => { 24 | clipboardSuccess(); 25 | clipboard.off('error'); 26 | clipboard.off('success'); 27 | clipboard.destroy(); 28 | }); 29 | clipboard.on('error', () => { 30 | clipboardError(); 31 | clipboard.off('error'); 32 | clipboard.off('success'); 33 | clipboard.destroy(); 34 | }); 35 | clipboard.onClick(event); 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/less/transition.less: -------------------------------------------------------------------------------- 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 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /*breadcrumb transition*/ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /src/views/treeTable/custom/data.ts: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | name: '1', 4 | timeLine: 100, 5 | children: [ 6 | { 7 | name: '1-1', 8 | timeLine: 20 9 | }, 10 | { 11 | name: '1-2', 12 | timeLine: 60, 13 | children: [ 14 | { 15 | name: '1-2-1', 16 | timeLine: 35 17 | }, 18 | { 19 | name: '1-2-2', 20 | timeLine: 25 21 | } 22 | ] 23 | } 24 | ] 25 | }, 26 | { 27 | name: '2', 28 | timeLine: 80, 29 | children: [ 30 | { 31 | name: '2-1', 32 | timeLine: 30 33 | }, 34 | { 35 | name: '2-2', 36 | timeLine: 50 37 | }, 38 | { 39 | name: '2-3', 40 | timeLine: 60 41 | } 42 | ] 43 | }, 44 | { 45 | name: '3', 46 | timeLine: 40 47 | } 48 | ]; 49 | 50 | export default data; 51 | 52 | -------------------------------------------------------------------------------- /src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vendor/vendor-manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"vendor_54783f8a6561d148af4b","content":{"./node_modules/vue/dist/vue.runtime.esm.js":{"id":2,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/webpack/buildin/global.js":{"id":5,"buildMeta":{"providedExports":true}},"./node_modules/process/browser.js":{"id":18,"buildMeta":{"providedExports":true}},"./node_modules/timers-browserify/main.js":{"id":35,"buildMeta":{"providedExports":true}},"./node_modules/setimmediate/setImmediate.js":{"id":36,"buildMeta":{"providedExports":true}},"./node_modules/vue-router/dist/vue-router.esm.js":{"id":185,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/vuex/dist/vuex.esm.js":{"id":186,"buildMeta":{"exportsType":"namespace","providedExports":["default","Store","install","mapState","mapMutations","mapGetters","mapActions","createNamespacedHelpers"]}},"./node_modules/normalize.css/normalize.css":{"id":187,"buildMeta":{"providedExports":true}}}} -------------------------------------------------------------------------------- /src/views/guide/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/views/permission/components/SwitchRoles.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 38 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Vue Element Admin Typescript', 3 | 4 | theme: '#1890ff', 5 | 6 | /** 7 | * @type {boolean} true | false 8 | * @description Whether show the settings right-panel 9 | */ 10 | showSettings: true, 11 | 12 | /** 13 | * @type {boolean} true | false 14 | * @description Whether need tagsView 15 | */ 16 | tagsView: true, 17 | 18 | /** 19 | * @type {boolean} true | false 20 | * @description Whether fix the header 21 | */ 22 | fixedHeader: false, 23 | 24 | /** 25 | * @type {boolean} true | false 26 | * @description Whether show the logo in sidebar 27 | */ 28 | sidebarLogo: false, 29 | 30 | /** 31 | * @type {string | array} 'production' | ['production', 'development'] 32 | * @description Need show err logs component. 33 | * The default is only used in the production env 34 | * If you want to also use it in dev, you can pass ['production', 'development'] 35 | */ 36 | errorLog: 'production' 37 | }; 38 | -------------------------------------------------------------------------------- /src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/edit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'edit': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/nested.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'nested': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/router/modules/charts.ts: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules**/ 2 | 3 | import Layout from '@/layout/index.vue'; 4 | 5 | const chartsRouter = { 6 | path: '/charts', 7 | component: Layout, 8 | redirect: 'noredirect', 9 | name: 'Charts', 10 | meta: { 11 | title: 'charts', 12 | icon: 'chart' 13 | }, 14 | children: [ 15 | { 16 | path: 'keyboard', 17 | component: () => import('@/views/charts/keyboard.vue'), 18 | name: 'KeyboardChart', 19 | meta: {title: 'keyboardChart', noCache: true} 20 | }, 21 | { 22 | path: 'line', 23 | component: () => import('@/views/charts/line.vue'), 24 | name: 'LineChart', 25 | meta: {title: 'lineChart', noCache: true} 26 | }, 27 | { 28 | path: 'mixchart', 29 | component: () => import('@/views/charts/mixChart.vue'), 30 | name: 'MixChart', 31 | meta: {title: 'mixChart', noCache: true} 32 | } 33 | ] 34 | }; 35 | 36 | export default chartsRouter; 37 | -------------------------------------------------------------------------------- /src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | export function isValidUsername(str: string) { 2 | const validMap = ['admin', 'editor']; 3 | return validMap.indexOf(str.trim()) >= 0; 4 | } 5 | 6 | // 小写字母 7 | export function validateLowerCase(str: string) { 8 | const reg = /^[a-z]+$/; 9 | return reg.test(str); 10 | } 11 | 12 | // 大写字母 13 | export function validateUpperCase(str: string) { 14 | const reg = /^[A-Z]+$/; 15 | return reg.test(str); 16 | } 17 | 18 | // 大小写字母 19 | export function validateAlphabets(str: string) { 20 | const reg = /^[A-Za-z]+$/; 21 | return reg.test(str); 22 | } 23 | 24 | /** 25 | * @param {Array} arg 26 | * @returns {Boolean} 27 | */ 28 | export function isArray(arg: any) { 29 | if (typeof Array.isArray === 'undefined') { 30 | return Object.prototype.toString.call(arg) === '[object Array]'; 31 | } 32 | return Array.isArray(arg); 33 | } 34 | 35 | /** 36 | * @param {string} str 37 | * @returns {Boolean} 38 | */ 39 | export function isString(str) { 40 | return typeof str === 'string' || str instanceof String; 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Charts/mixins/resize.ts: -------------------------------------------------------------------------------- 1 | import { Vue, Component } from 'vue-property-decorator'; 2 | import { debounce } from '@/utils'; 3 | 4 | @Component 5 | export default class ResizeMixins extends Vue { 6 | sidebarElm: any = null; 7 | 8 | resizeHandler = debounce(() => { 9 | // @ts-ignore 10 | if (this.chart) { 11 | // @ts-ignore 12 | this.chart.resize(); 13 | } 14 | }, 100); 15 | 16 | mounted() { 17 | window.addEventListener('resize', this.resizeHandler); 18 | 19 | this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]; 20 | this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler); 21 | } 22 | 23 | beforeDestroy() { 24 | window.removeEventListener('resize', this.resizeHandler); 25 | 26 | this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler); 27 | } 28 | 29 | sidebarResizeHandler(e) { 30 | if (e.propertyName === 'width') { 31 | this.resizeHandler(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-console */ 2 | 3 | import { register } from 'register-service-worker'; 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB', 11 | ); 12 | }, 13 | registered() { 14 | console.log('Service worker has been registered.'); 15 | }, 16 | cached() { 17 | console.log('Content has been cached for offline use.'); 18 | }, 19 | updatefound() { 20 | console.log('New content is downloading.'); 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.'); 24 | }, 25 | offline() { 26 | console.log('No internet connection found. App is running in offline mode.'); 27 | }, 28 | error(error) { 29 | console.error('Error during service worker registration:', error); 30 | }, 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**", 9 | "build/**", 10 | "package.json" 11 | ] 12 | }, 13 | "rules": { 14 | "quotemark": [true, "single"], 15 | "indent": [true, "spaces", 2], 16 | "trailing-comma": false, 17 | "interface-name": false, 18 | "ordered-imports": false, 19 | "no-var-requires": false, 20 | "object-literal-sort-keys": false, 21 | "object-literal-key-quotes": false, 22 | "no-consecutive-blank-lines": false, 23 | "no-console": false, 24 | "completed-docs": false, 25 | "curly": [ 26 | true, 27 | "ignore-same-line" 28 | ], 29 | "jsdoc-format": false, 30 | "no-redundant-jsdoc": false, 31 | "max-line-length": [true, 500], 32 | "no-unused-expression": false, 33 | "no-shadowed-variable": false, 34 | "member-access": [true, "no-public"], 35 | "no-string-literal": false 36 | }, 37 | "jsRules": { 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/ScreenFull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 43 | -------------------------------------------------------------------------------- /src/views/example/components/Dropdown/Comment.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 34 | 35 | -------------------------------------------------------------------------------- /src/views/componentsDemo/dropZone.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | -------------------------------------------------------------------------------- /src/icons/components/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import './404' 3 | import './bug' 4 | import './cat' 5 | import './chart' 6 | import './clipboard' 7 | import './component' 8 | import './dashboard' 9 | import './documentation' 10 | import './dog' 11 | import './drag' 12 | import './edit' 13 | import './email' 14 | import './example' 15 | import './excel' 16 | import './exit-fullscreen' 17 | import './eye-open' 18 | import './eye' 19 | import './form' 20 | import './fullscreen' 21 | import './guide' 22 | import './guide2' 23 | import './icon' 24 | import './international' 25 | import './language' 26 | import './link' 27 | import './list' 28 | import './lock' 29 | import './message' 30 | import './money' 31 | import './nested' 32 | import './password' 33 | import './pdf' 34 | import './people' 35 | import './peoples' 36 | import './qq' 37 | import './search' 38 | import './shopping' 39 | import './size' 40 | import './star' 41 | import './tab' 42 | import './table' 43 | import './theme' 44 | import './tree-table' 45 | import './tree' 46 | import './user' 47 | import './wechat' 48 | import './zip' 49 | -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import pkg from '../../package.json'; 2 | import store from 'store'; 3 | 4 | const {name} = pkg; 5 | 6 | const TokenKey = `${name}/Admin-Token`; 7 | const LANGUAGE = `${name}/language`; 8 | const SIZE = `${name}/size`; 9 | const SIDEBAR_STATUS = `${name}/sidebarStatus`; 10 | 11 | export function getToken() { 12 | return store.get(TokenKey); 13 | } 14 | 15 | export function setToken(token: string) { 16 | return store.set(TokenKey, token); 17 | } 18 | 19 | export function removeToken() { 20 | return store.remove(TokenKey); 21 | } 22 | 23 | export function setLanguage(language: string) { 24 | return store.set(LANGUAGE, language); 25 | } 26 | 27 | // getLanguage -> @/lang/index 28 | 29 | export function getSize() { 30 | return store.get(SIZE); 31 | } 32 | 33 | export function setSize(size: string) { 34 | return store.set(SIZE, size); 35 | } 36 | 37 | export function getSidebarStatus() { 38 | return store.get(SIDEBAR_STATUS); 39 | } 40 | 41 | export function setSidebarStatus(sidebarStatus: string) { 42 | return store.set(SIDEBAR_STATUS, sidebarStatus); 43 | } 44 | -------------------------------------------------------------------------------- /src/views/errorLog/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /src/icons/components/tree-table.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'tree-table': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/store/modules/settings.ts: -------------------------------------------------------------------------------- 1 | import { getModule, Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'; 2 | import store from '@/store'; 3 | import defaultSettings from '@/settings'; 4 | 5 | const {showSettings, tagsView, fixedHeader, sidebarLogo, theme} = defaultSettings; 6 | 7 | export interface ISettingsState { 8 | theme: string; 9 | showSettings: boolean; 10 | tagsView: boolean; 11 | fixedHeader: boolean; 12 | sidebarLogo: boolean; 13 | } 14 | 15 | @Module({dynamic: true, store, name: 'settings'}) 16 | class Settings extends VuexModule implements ISettingsState { 17 | theme = theme; 18 | showSettings = showSettings; 19 | tagsView = tagsView; 20 | fixedHeader = fixedHeader; 21 | sidebarLogo = sidebarLogo; 22 | 23 | @Action({commit: 'CHANGE_SETTING'}) 24 | ChangeSetting(data: any) { 25 | console.log('data', data); 26 | return data; 27 | } 28 | 29 | @Mutation 30 | CHANGE_SETTING({key, value}) { 31 | if (this.hasOwnProperty(key)) { 32 | this[key] = value; 33 | } 34 | } 35 | } 36 | 37 | export const SettingsModule = getModule(Settings); 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-element-admin-ts 2 | 3 | > [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) 的 *typescript* 版本 by [vue-cli3](https://cli.vuejs.org/zh/) and [vue-cli3-typescript-template](https://github.com/cklwblove/vue-cli3-typescript-template) 4 | 5 | 6 | ## 效果 7 | 8 | ![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif) 9 | 10 | ### 安装 11 | 12 | ```node 13 | yarn install 14 | ``` 15 | 16 | ### 开发 17 | 18 | ```node 19 | yarn run serve 20 | ``` 21 | 22 | ### 线上构建 23 | 24 | ```node 25 | yarn run build 26 | ``` 27 | 28 | ### 代码检测 29 | 30 | ```node 31 | yarn run lint 32 | ``` 33 | 34 | ## 相关链接 35 | 36 | - [TypeScript 学习资源合集](https://juejin.im/entry/5b9e4a135188255c3a2d3695) 37 | - [Awesome TypeScript](https://github.com/semlinker/awesome-typescript) 38 | - [如何用 TypeScript 编写 Vue 项目](https://gitbook.cn/books/5a0fdd6a0321202f017b8eb7/index.html) 39 | - [原有vue项目接入typescript](https://blog.fundebug.com/2018/11/30/how-to-use-typescript-in-vue/) 40 | 41 | ## License 42 | 43 | [MIT](https://github.com/cklwblove/vue-element-admin-ts/blob/master/LICENSE) 44 | -------------------------------------------------------------------------------- /src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/example/components/Dropdown/SourceUrl.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/icons/components/eye.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'eye': { 7 | width: 128, 8 | height: 64, 9 | viewBox: '0 0 128 64', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 lwbgithub 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/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/clipboard.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'clipboard': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/errorLog.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { isArray, isString } from '@/utils/validate'; 3 | import { ErrorLogModule } from '@/store/modules/errorLog'; 4 | import settings from '@/settings'; 5 | 6 | // you can set in settings.js 7 | // errorLog:'production' | ['production', 'development'] 8 | const {errorLog: needErrorLog} = settings; 9 | 10 | const checkNeed = () => { 11 | const env = process.env.NODE_ENV as string; 12 | if (isString(needErrorLog)) { 13 | return env === needErrorLog; 14 | } 15 | if (isArray(needErrorLog)) { 16 | return needErrorLog.includes(env); 17 | } 18 | return false; 19 | }; 20 | 21 | // you can set only in production env show the error-log 22 | if (checkNeed()) { 23 | Vue.config.errorHandler = (err, vm, info) => { 24 | // Don't ask me why I use Vue.nextTick, it just a hack. 25 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 26 | Vue.nextTick(() => { 27 | ErrorLogModule.AddErrorLog({ 28 | err, 29 | vm, 30 | info, 31 | url: window.location.href 32 | }); 33 | console.error(err, info); 34 | }); 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 52 | -------------------------------------------------------------------------------- /src/icons/components/list.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'list': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/router/modules/table.ts: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules**/ 2 | 3 | import Layout from '@/layout/index.vue'; 4 | 5 | const tableRouter = { 6 | path: '/table', 7 | component: Layout, 8 | redirect: '/table/complex-table', 9 | name: 'Table', 10 | meta: { 11 | title: 'Table', 12 | icon: 'table' 13 | }, 14 | children: [ 15 | { 16 | path: 'dynamic-table', 17 | component: () => import('@/views/table/dynamicTable/index.vue'), 18 | name: 'DynamicTable', 19 | meta: {title: 'dynamicTable'} 20 | }, 21 | { 22 | path: 'drag-table', 23 | component: () => import('@/views/table/dragTable.vue'), 24 | name: 'DragTable', 25 | meta: {title: 'dragTable'} 26 | }, 27 | { 28 | path: 'inline-edit-table', 29 | component: () => import('@/views/table/inlineEditTable.vue'), 30 | name: 'InlineEditTable', 31 | meta: {title: 'inlineEditTable'} 32 | }, 33 | { 34 | path: 'complex-table', 35 | component: () => import('@/views/table/complexTable.vue'), 36 | name: 'ComplexTable', 37 | meta: {title: 'complexTable'} 38 | } 39 | ] 40 | }; 41 | export default tableRouter; 42 | -------------------------------------------------------------------------------- /src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/icon.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'icon': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/componentsDemo/dragSelect.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "strictNullChecks": true, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | // JSON parse 12 | "resolveJsonModule": true, 13 | "esModuleInterop": true, 14 | // error TS7017: Index signature of object type implicitly has an 'any' type. 15 | "noImplicitAny": false, 16 | // Don't emit; allow Babel to transform files. 17 | "noEmit": true, 18 | "sourceMap": true, 19 | "allowJs": true, 20 | "baseUrl": ".", 21 | "types": [ 22 | "node", 23 | "webpack-env" 24 | ], 25 | "paths": { 26 | "@/*": [ 27 | "src/*", 28 | "mock/*" 29 | ] 30 | }, 31 | "lib": [ 32 | "esnext", 33 | "dom", 34 | "dom.iterable", 35 | "scripthost" 36 | ] 37 | }, 38 | "include": [ 39 | "src/**/*.ts", 40 | "src/**/*.tsx", 41 | "src/**/*.vue", 42 | "mock/**/*.ts", 43 | "mock/*.ts", 44 | "tests/**/*.ts", 45 | "tests/**/*.tsx", 46 | "./types/*.d.ts" 47 | ], 48 | "exclude": [ 49 | "node_modules", 50 | "build/*.ts" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /src/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/example/components/Dropdown/Platform.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 39 | -------------------------------------------------------------------------------- /src/views/excel/uploadExcel.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 43 | -------------------------------------------------------------------------------- /src/directives/el-table.ts: -------------------------------------------------------------------------------- 1 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'; 2 | 3 | /** 4 | * How to use 5 | * ... 6 | * el-table height is must be set 7 | * bottomOffset: 30(default) // The height of the table from the bottom of the page. 8 | */ 9 | 10 | const doResize = (el, binding, vnode) => { 11 | const {componentInstance: $table} = vnode; 12 | 13 | const {value} = binding; 14 | 15 | if (!$table.height) { 16 | throw new Error(`el-$table must set the height. Such as height='100px'`); 17 | } 18 | const bottomOffset = (value && value.bottomOffset) || 30; 19 | 20 | if (!$table) return; 21 | 22 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset; 23 | $table.layout.setHeight(height); 24 | $table.doLayout(); 25 | }; 26 | 27 | export default { 28 | bind(el, binding, vnode) { 29 | el.resizeListener = () => { 30 | doResize(el, binding, vnode); 31 | }; 32 | 33 | addResizeListener(el, el.resizeListener); 34 | }, 35 | inserted(el, binding, vnode) { 36 | doResize(el, binding, vnode); 37 | }, 38 | unbind(el) { 39 | removeResizeListener(el, el.resizeListener); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/store/getters.ts: -------------------------------------------------------------------------------- 1 | import { IRootState } from '@/store/index'; 2 | 3 | const getters = { 4 | sidebar: (state: IRootState) => state.app.sidebar, 5 | language: (state: IRootState) => state.app.language, 6 | size: (state: IRootState) => state.app.size, 7 | device: (state: IRootState) => state.app.device, 8 | visitedViews: (state: IRootState) => state.tagsView.visitedViews, 9 | cachedViews: (state: IRootState) => state.tagsView.cachedViews, 10 | token: (state: IRootState) => state.user.token, 11 | avatar: (state: IRootState) => state.user.avatar, 12 | name: (state: IRootState) => state.user.name, 13 | introduction: (state: IRootState) => state.user.introduction, 14 | status: (state: IRootState) => state.user.status, 15 | roles: (state: IRootState) => state.user.roles, 16 | permission_routers: (state: IRootState) => state.permission.routes, 17 | errorLogs: (state: IRootState) => state.errorLog.logs, 18 | theme: (state: IRootState) => state.settings.theme, 19 | fixedHeader: (state: IRootState) => state.settings.fixedHeader, 20 | tagsView: (state: IRootState) => state.settings.tagsView, 21 | sidebarLogo: (state: IRootState) => state.settings.sidebarLogo, 22 | showSettings: (state: IRootState) => state.settings.showSettings 23 | }; 24 | 25 | export default getters; 26 | -------------------------------------------------------------------------------- /src/views/componentsDemo/dndList.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 46 | -------------------------------------------------------------------------------- /src/icons/components/international.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'international': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /mock/remoteSearch.ts: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs'; 2 | 3 | const NameList: any[] = []; 4 | const count = 100; 5 | 6 | for (let i = 0; i < count; i++) { 7 | NameList.push(Mock.mock({ 8 | name: '@first' 9 | })); 10 | } 11 | NameList.push({name: 'mockPan'}); 12 | 13 | export default [ 14 | // username search 15 | { 16 | url: '/search/user', 17 | type: 'get', 18 | response: (config) => { 19 | const {name} = config.query; 20 | const mockNameList = NameList.filter((item) => { 21 | const lowerCaseName = item.name.toLowerCase(); 22 | return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0); 23 | }); 24 | return { 25 | code: 20000, 26 | data: {items: mockNameList} 27 | }; 28 | } 29 | }, 30 | 31 | // transaction list 32 | { 33 | url: '/transaction/list', 34 | type: 'get', 35 | response: (_) => { 36 | return { 37 | code: 20000, 38 | data: { 39 | total: 20, 40 | 'items|20': [{ 41 | order_no: '@guid()', 42 | timestamp: +Mock.Random.date('T'), 43 | username: '@name()', 44 | price: '@float(1000, 15000, 0, 2)', 45 | 'status|1': ['success', 'pending'] 46 | }] 47 | } 48 | }; 49 | } 50 | } 51 | ]; 52 | 53 | -------------------------------------------------------------------------------- /src/views/guide/defineSteps.ts: -------------------------------------------------------------------------------- 1 | const steps = [ 2 | { 3 | element: '.hamburger-container', 4 | popover: { 5 | title: 'Hamburger', 6 | description: 'Open && Close sidebar', 7 | position: 'bottom' 8 | } 9 | }, 10 | { 11 | element: '.breadcrumb-container', 12 | popover: { 13 | title: 'Breadcrumb', 14 | description: 'Indicate the current page location', 15 | position: 'bottom' 16 | } 17 | }, 18 | { 19 | element: '.screenfull', 20 | popover: { 21 | title: 'Screenfull', 22 | description: 'Bring the page into fullscreen', 23 | position: 'left' 24 | } 25 | }, 26 | { 27 | element: '.international-icon', 28 | popover: { 29 | title: 'Switch language', 30 | description: 'Switch the system language', 31 | position: 'left' 32 | } 33 | }, 34 | { 35 | element: '.theme-switch', 36 | popover: { 37 | title: 'Theme Switch', 38 | description: 'Custom switch system theme', 39 | position: 'left' 40 | } 41 | }, 42 | { 43 | element: '.tags-view-container', 44 | popover: { 45 | title: 'Tags view', 46 | description: 'The history of the page you visited', 47 | position: 'bottom' 48 | }, 49 | padding: 0 50 | } 51 | ]; 52 | 53 | export default steps; 54 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import Element from 'element-ui'; 4 | import { getSize } from '@/utils/auth'; 5 | import SvgIcon from 'vue-svgicon'; 6 | import App from './App.vue'; 7 | import router from './router'; 8 | import store from './store'; 9 | import i18n from './lang'; 10 | import './errorLog'; 11 | import './icons/components'; 12 | import './router/router.interceptor'; 13 | import './filters'; 14 | import './directives'; 15 | import './services'; 16 | import './pwa/register-service-worker'; 17 | // mock 18 | import { mockXHR } from '../mock'; 19 | 20 | // 注册钩子函数 21 | Component.registerHooks([ 22 | 'beforeRouteEnter', 23 | 'beforeRouteLeave', 24 | 'beforeRouteUpdate' 25 | ]); 26 | 27 | // mock api in github pages site build 28 | if (process.env.NODE_ENV === 'production') { 29 | mockXHR(); 30 | } 31 | 32 | Vue.use(Element, { 33 | size: getSize() || 'medium', // set element-ui default size 34 | i18n: (key, value) => i18n.t(key, value) 35 | }); 36 | 37 | Vue.use(SvgIcon, { 38 | tagName: 'svg-icon', 39 | defaultWidth: '1em', 40 | defaultHeight: '1em' 41 | }); 42 | 43 | Vue.config.productionTip = process.env.NODE_ENV === 'production'; 44 | 45 | new Vue({ 46 | router, 47 | store, 48 | i18n, 49 | render: (h) => h(App) 50 | }).$mount('#app'); 51 | -------------------------------------------------------------------------------- /src/icons/components/wechat.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'wechat': { 7 | width: 128, 8 | height: 110, 9 | viewBox: '0 0 128 110', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/people.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'people': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/components/LangSelect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | 37 | 44 | 45 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | 27 | 40 | -------------------------------------------------------------------------------- /src/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/components/eye-open.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'eye-open': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 1024 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/table/dynamicTable/unfixedThead.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 47 | -------------------------------------------------------------------------------- /src/icons/components/language.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'language': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/clipboard/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 40 | -------------------------------------------------------------------------------- /src/lang/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueI18n from 'vue-i18n'; 3 | import elementEnLocale from 'element-ui/lib/locale/lang/en'; // element-ui lang 4 | import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'; // element-ui lang 5 | import elementEsLocale from 'element-ui/lib/locale/lang/es'; // element-ui lang 6 | import enLocale from './en'; 7 | import zhLocale from './zh'; 8 | import esLocale from './es'; 9 | import store from 'store'; 10 | 11 | Vue.use(VueI18n); 12 | 13 | const messages = { 14 | en: { 15 | ...enLocale, 16 | ...elementEnLocale 17 | }, 18 | zh: { 19 | ...zhLocale, 20 | ...elementZhLocale 21 | }, 22 | es: { 23 | ...esLocale, 24 | ...elementEsLocale 25 | } 26 | }; 27 | 28 | export function getLanguage() { 29 | const chooseLanguage = store.get('language'); 30 | if (chooseLanguage) return chooseLanguage; 31 | const navigatorNew = window.navigator as any; 32 | 33 | // if has not choose language 34 | const language = (navigatorNew.language || navigatorNew.browserLanguage).toLowerCase(); 35 | const locales = Object.keys(messages); 36 | for (const locale of locales) { 37 | if (language.indexOf(locale) > -1) { 38 | return locale; 39 | } 40 | } 41 | return 'en'; 42 | } 43 | 44 | const i18n = new VueI18n({ 45 | // set locale 46 | // options: en | zh | es 47 | locale: getLanguage(), 48 | // set locale messages 49 | messages 50 | }); 51 | 52 | export default i18n; 53 | -------------------------------------------------------------------------------- /src/views/tab/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 40 | 41 | 46 | -------------------------------------------------------------------------------- /src/assets/less/element-ui.less: -------------------------------------------------------------------------------- 1 | //覆盖一些element-ui样式 2 | 3 | .el-breadcrumb__inner, 4 | .el-breadcrumb__inner a { 5 | font-weight: 400 !important; 6 | } 7 | 8 | .el-upload { 9 | input[type="file"] { 10 | display: none !important; 11 | } 12 | } 13 | 14 | .el-upload__input { 15 | display: none; 16 | } 17 | 18 | .cell { 19 | .el-tag { 20 | margin-right: 0px; 21 | } 22 | } 23 | 24 | .small-padding { 25 | .cell { 26 | padding-left: 5px; 27 | padding-right: 5px; 28 | } 29 | } 30 | 31 | .fixed-width { 32 | .el-button--mini { 33 | padding: 7px 10px; 34 | width: 60px; 35 | } 36 | } 37 | 38 | .status-col { 39 | .cell { 40 | padding: 0 10px; 41 | text-align: center; 42 | 43 | .el-tag { 44 | margin-right: 0px; 45 | } 46 | } 47 | } 48 | 49 | //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461 50 | .el-dialog { 51 | transform: none; 52 | left: 0; 53 | position: relative; 54 | margin: 0 auto; 55 | } 56 | 57 | //文章页textarea修改样式 58 | .article-textarea { 59 | textarea { 60 | padding-right: 40px; 61 | resize: none; 62 | border: none; 63 | border-radius: 0px; 64 | border-bottom: 1px solid #bfcbd9; 65 | } 66 | } 67 | 68 | //element ui upload 69 | .upload-container { 70 | .el-upload { 71 | width: 100%; 72 | 73 | .el-upload-dragger { 74 | width: 100%; 75 | height: 200px; 76 | } 77 | } 78 | } 79 | 80 | //dropdown 81 | .el-dropdown-menu { 82 | a { 83 | display: block 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/layout/mixin/ResizeHandler.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue, Watch } from 'vue-property-decorator'; 2 | import { AppModule, DeviceType } from '@/store/modules/app'; 3 | 4 | const {body} = document; 5 | const WIDTH = 1024; 6 | const RATIO = 3; 7 | 8 | @Component({ 9 | name: 'ResizeHandlerMixin' 10 | }) 11 | export default class ResizeHandlerMixin extends Vue { 12 | get sidebar() { 13 | return this.$store.getters.sidebar; 14 | } 15 | 16 | get device() { 17 | return this.$store.getters.device; 18 | } 19 | 20 | @Watch('$route') 21 | onRouteChange() { 22 | if (this.device === DeviceType.Mobile && this.sidebar.opened) { 23 | AppModule.CloseSideBar(false); 24 | } 25 | } 26 | 27 | beforeMount() { 28 | window.addEventListener('resize', this.resizeHandler); 29 | } 30 | 31 | beforeDestroy() { 32 | window.removeEventListener('resize', this.resizeHandler); 33 | } 34 | 35 | mounted() { 36 | const isMobile = this.isMobile(); 37 | if (isMobile) { 38 | AppModule.ToggleDevice(DeviceType.Mobile); 39 | AppModule.CloseSideBar(true); 40 | } 41 | } 42 | 43 | isMobile() { 44 | const rect = body.getBoundingClientRect(); 45 | return rect.width - RATIO < WIDTH; 46 | } 47 | 48 | resizeHandler() { 49 | if (!document.hidden) { 50 | const isMobile = this.isMobile(); 51 | AppModule.ToggleDevice(isMobile ? DeviceType.Mobile : DeviceType.Desktop); 52 | 53 | if (isMobile) { 54 | AppModule.CloseSideBar(true); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/less/variable.less: -------------------------------------------------------------------------------- 1 | //公共变量 2 | @lessPath: "../assets/less/"; 3 | @pageImgPath: "../assets/img/"; 4 | @fontPath: "../fonts/"; 5 | 6 | // colora 7 | @colorBlueMain: #43a9f1; 8 | @colorBlueLight: #49b2f5; 9 | @colorBlueText: #0076ff; 10 | 11 | @colorGrayMain: #414141; 12 | @colorGrayLight: #f3f3f3; 13 | @colorGrayAssist: #e1e1e1; 14 | @colorBorder: #e9e9e9; 15 | @colorGray: #c8c8c8; 16 | @colorAfter: #eee; 17 | @tabBorder: #d9d9d9; 18 | 19 | @colorWhite: #fff; 20 | @colorBlack: #333; 21 | 22 | // font 23 | @fontColor: @colorGrayMain; 24 | @fontColorBlack: @colorBlack; 25 | @fontColorLight: @colorGrayLight; 26 | @fontColorAssist: @colorGrayAssist; 27 | 28 | // bg 29 | @bgColor: #f2f2f2; 30 | @bgColorLight: #f7f7f7; 31 | 32 | //line 33 | @colorLine: @colorGrayAssist; 34 | 35 | //colorAssist 36 | @colorRed: #ff5648; 37 | @colorGreen: #47b34f; 38 | @colorOrange: #ffaf32; 39 | @colorOrangeLight: #ffb42f; 40 | 41 | //fontSize 42 | @fontSizeH1: 20px; 43 | @fontSizeH2: 16px; 44 | @fontSizeH3: 14px; 45 | @fontSizeH4: 12px; 46 | 47 | // vue-element-admin 48 | // base color 49 | @blue: #324157; 50 | @light-blue: #3A71A8; 51 | @red: #C03639; 52 | @pink: #E65D6E; 53 | @green: #30B08F; 54 | @tiffany: #4AB7BD; 55 | @yellow: #FEC171; 56 | @panGreen: #30B08F; 57 | 58 | //sidebar 59 | @menuText: #bfcbd9; 60 | @menuActiveText: #409EFF; 61 | @subMenuActiveText: #f4f4f5; //https://github.com/ElemeFE/element/issues/12951 62 | 63 | @menuBg: #304156; 64 | @menuHover: #263445; 65 | 66 | @subMenuBg: #1f2d3d; 67 | @subMenuHover: #001528; 68 | 69 | @sideBarWidth: 210px; 70 | -------------------------------------------------------------------------------- /src/components/ImageCropper/utils/effectRipple.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 点击波纹效果 3 | * 4 | * @param {[event]} e [description] 5 | * @param {[Object]} arg_opts [description] 6 | * @return {[bollean]} [description] 7 | */ 8 | export default function(e, argOpts?: object) { 9 | const opts = Object.assign({ 10 | ele: e.target, // 波纹作用元素 11 | type: 'hit', // hit点击位置扩散center中心点扩展 12 | bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 13 | }, argOpts); 14 | const target = opts.ele; 15 | if (target) { 16 | const rect = target.getBoundingClientRect(); 17 | let ripple = target.querySelector('.e-ripple'); 18 | if (!ripple) { 19 | ripple = document.createElement('span'); 20 | ripple.className = 'e-ripple'; 21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'; 22 | target.appendChild(ripple); 23 | } else { 24 | ripple.className = 'e-ripple'; 25 | } 26 | switch (opts.type) { 27 | case 'center': 28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'; 29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'; 30 | break; 31 | default: 32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'; 33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'; 34 | } 35 | ripple.style.backgroundColor = opts.bgc; 36 | ripple.className = 'e-ripple z-active'; 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/icons/svg/dog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/TreeTable/eval.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | // Flattened array 4 | export default function treeToArray(data, children = 'children') { 5 | let tmp: any[] = []; 6 | data.forEach((item, index) => { 7 | Vue.set(item, '_index', index); 8 | tmp.push(item); 9 | if (item[children] && item[children].length > 0) { 10 | const res = treeToArray(item[children], children); 11 | tmp = tmp.concat(res); 12 | } 13 | }); 14 | return tmp; 15 | } 16 | 17 | export function addAttrs(data, {parent = null, preIndex = false, level = 0, expand = false, children = 'children', show = true, select = false}: any = {}) { 18 | data.forEach((item, index) => { 19 | const id = (preIndex ? `${preIndex}-${index}` : index) + ''; 20 | Vue.set(item, '_id', id); 21 | Vue.set(item, '_level', level); 22 | Vue.set(item, '_expand', expand); 23 | Vue.set(item, '_parent', parent); 24 | Vue.set(item, '_show', show); 25 | Vue.set(item, '_select', select); 26 | if (item[children] && item[children].length > 0) { 27 | addAttrs(item[children], { 28 | parent: item, 29 | level: level + 1, 30 | expand, 31 | preIndex: id, 32 | children, 33 | status, 34 | select 35 | }); 36 | } 37 | }); 38 | } 39 | 40 | export function cleanParentAttr(data, children = 'children') { 41 | data.forEach((item) => { 42 | item._parent = null; 43 | if (item[children] && item[children].length > 0) { 44 | addAttrs(item[children], children); 45 | } 46 | }); 47 | return data; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/views/componentsDemo/tinymce.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /src/views/login/socialSignIn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 65 | -------------------------------------------------------------------------------- /src/pwa/register-service-worker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker'; 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ); 12 | }, 13 | registered(registration) { 14 | console.log('Service worker has been registered.'); 15 | // Routinely check for app updates by testing for a new service worker. 16 | setInterval(() => { 17 | registration.update(); 18 | }, 1000 * 60 * 60); // hourly checks 19 | }, 20 | cached() { 21 | console.log('Content has been cached for offline use.'); 22 | }, 23 | updatefound() { 24 | console.log('New content is downloading.'); 25 | }, 26 | updated(registration) { 27 | console.log('New content is available; please refresh.'); 28 | // Add a custom event and dispatch it. 29 | // Used to display of a 'refresh' banner following a service worker update. 30 | // Set the event payload to the service worker registration object. 31 | document.dispatchEvent( 32 | new CustomEvent('swUpdated', {detail: registration}) 33 | ); 34 | }, 35 | offline() { 36 | console.log('No internet connection found. App is running in offline mode.'); 37 | }, 38 | error(error) { 39 | console.error('Error during service worker registration:', error); 40 | } 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /src/icons/components/404.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | '404': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/zip.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'zip': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/componentsDemo/jsonEditor.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 34 | -------------------------------------------------------------------------------- /src/icons/svg/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/SizeSelect/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 58 | -------------------------------------------------------------------------------- /src/components/DragSelect/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 45 | 46 | 57 | -------------------------------------------------------------------------------- /src/icons/components/bug.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'bug': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/dog.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'dog': { 7 | width: 225, 8 | height: 200, 9 | viewBox: '0 0 1152 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/views/dashboard/admin/components/TransactionTable.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 54 | 55 | 58 | -------------------------------------------------------------------------------- /src/views/i18n-demo/local.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | zh: { 3 | i18nView: { 4 | title: '切换语言', 5 | note: '本项目国际化基于 vue-i18n', 6 | datePlaceholder: '请选择日期', 7 | selectPlaceholder: '请选择', 8 | tableDate: '日期', 9 | tableName: '姓名', 10 | tableAddress: '地址', 11 | default: '默认按钮', 12 | primary: '主要按钮', 13 | success: '成功按钮', 14 | info: '信息按钮', 15 | warning: '警告按钮', 16 | danger: '危险按钮', 17 | one: '一', 18 | two: '二', 19 | three: '三' 20 | } 21 | }, 22 | en: { 23 | i18nView: { 24 | title: 'Switch Language', 25 | note: 'The internationalization of this project is based on vue-i18n', 26 | datePlaceholder: 'Pick a day', 27 | selectPlaceholder: 'Select', 28 | tableDate: 'tableDate', 29 | tableName: 'tableName', 30 | tableAddress: 'tableAddress', 31 | default: 'default:', 32 | primary: 'primary', 33 | success: 'success', 34 | info: 'info', 35 | warning: 'warning', 36 | danger: 'danger', 37 | one: 'One', 38 | two: 'Two', 39 | three: 'Three' 40 | } 41 | }, 42 | es: { 43 | i18nView: { 44 | title: 'Switch Language', 45 | note: 'The internationalization of this project is based on vue-i18n', 46 | datePlaceholder: 'Pick a day', 47 | selectPlaceholder: 'Select', 48 | tableDate: 'tableDate', 49 | tableName: 'tableName', 50 | tableAddress: 'tableAddress', 51 | default: 'default:', 52 | primary: 'primary', 53 | success: 'success', 54 | info: 'info', 55 | warning: 'warning', 56 | danger: 'danger', 57 | one: 'One', 58 | two: 'Two', 59 | three: 'Three' 60 | } 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /src/icons/svg/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/componentsDemo/avatarUpload.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /src/views/treeTable/data.ts: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | id: 0, 4 | event: 'Event-0', 5 | timeLine: 50 6 | }, 7 | { 8 | id: 1, 9 | event: 'Event-1', 10 | timeLine: 100, 11 | children: [ 12 | { 13 | id: 2, 14 | event: 'Event-2', 15 | timeLine: 10 16 | 17 | }, 18 | { 19 | id: 3, 20 | event: 'Event-3', 21 | timeLine: 90, 22 | children: [ 23 | { 24 | id: 4, 25 | event: 'Event-4', 26 | timeLine: 5 27 | 28 | }, 29 | { 30 | id: 5, 31 | event: 'Event-5', 32 | timeLine: 10 33 | 34 | }, 35 | { 36 | id: 6, 37 | event: 'Event-6', 38 | timeLine: 75, 39 | 40 | children: [ 41 | { 42 | id: 7, 43 | event: 'Event-7', 44 | timeLine: 50, 45 | 46 | children: [ 47 | { 48 | id: 71, 49 | event: 'Event-7-1', 50 | timeLine: 25 51 | 52 | }, 53 | { 54 | id: 72, 55 | event: 'Event-7-2', 56 | timeLine: 5 57 | 58 | }, 59 | { 60 | id: 73, 61 | event: 'Event-7-3', 62 | timeLine: 20 63 | } 64 | ] 65 | }, 66 | { 67 | id: 8, 68 | event: 'Event-8', 69 | timeLine: 25 70 | } 71 | ] 72 | } 73 | ] 74 | } 75 | ] 76 | } 77 | ]; 78 | 79 | export default data; 80 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/directives/waves/index.ts: -------------------------------------------------------------------------------- 1 | import './waves.css'; 2 | 3 | export default { 4 | bind(el, binding) { 5 | el.addEventListener('click', (e) => { 6 | const customOpts = Object.assign({}, binding.value); 7 | const opts = Object.assign({ 8 | ele: el, // 波纹作用元素 9 | type: 'hit', // hit 点击位置扩散 center中心点扩展 10 | color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 11 | }, customOpts); 12 | const target = opts.ele; 13 | if (target) { 14 | target.style.position = 'relative'; 15 | target.style.overflow = 'hidden'; 16 | const rect = target.getBoundingClientRect(); 17 | let ripple = target.querySelector('.waves-ripple'); 18 | if (!ripple) { 19 | ripple = document.createElement('span'); 20 | ripple.className = 'waves-ripple'; 21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'; 22 | target.appendChild(ripple); 23 | } else { 24 | ripple.className = 'waves-ripple'; 25 | } 26 | switch (opts.type) { 27 | case 'center': 28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'; 29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'; 30 | break; 31 | default: 32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px'; 33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'; 34 | } 35 | ripple.style.backgroundColor = opts.color; 36 | ripple.className = 'waves-ripple z-active'; 37 | return false; 38 | } 39 | }, false); 40 | } 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /src/icons/components/pdf.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'pdf': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 1024 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/filters/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @authors liwb (lwbhtml@gmail.com) 4 | * @date 2018/6/5 上午10:43 5 | * @description 定义过滤器模块 6 | */ 7 | 8 | import Vue from 'vue'; 9 | 10 | import { formatDate } from '@liwb/cloud-utils'; 11 | 12 | import { 13 | parseTime, 14 | formatTime 15 | } from '@/utils'; 16 | 17 | function pluralize(time, label) { 18 | if (time === 1) { 19 | return time + label; 20 | } 21 | return time + label + 's'; 22 | } 23 | 24 | /* 数字 格式化*/ 25 | export function numberFormatter(num, digits) { 26 | const si = [ 27 | {value: 1E18, symbol: 'E'}, 28 | {value: 1E15, symbol: 'P'}, 29 | {value: 1E12, symbol: 'T'}, 30 | {value: 1E9, symbol: 'G'}, 31 | {value: 1E6, symbol: 'M'}, 32 | {value: 1E3, symbol: 'k'} 33 | ]; 34 | for (const s of si) { 35 | if (num >= s.value) { 36 | return (num / s.value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + s.symbol; 37 | } 38 | } 39 | return num.toString(); 40 | } 41 | 42 | export function toThousandFilter(num) { 43 | return (+num || 0).toString().replace(/^-?\d+/g, (m) => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')); 44 | } 45 | 46 | // Filter for article status 47 | export const articleStatusFilter = (status: string) => { 48 | const statusMap: { [key: string]: string } = { 49 | published: 'success', 50 | success: 'success', 51 | draft: 'info', 52 | deleted: 'danger', 53 | pending: 'danger' 54 | }; 55 | return statusMap[status]; 56 | }; 57 | 58 | // register global utility filters. 59 | const filters = { 60 | formatDate, 61 | parseTime, 62 | formatTime, 63 | numberFormatter, 64 | toThousandFilter, 65 | articleStatusFilter 66 | }; 67 | 68 | Object.keys(filters).forEach((key) => { 69 | // tslint:disable-next-line:ban-types 70 | Vue.filter(key, (filters as { [key: string]: Function })[key]); 71 | }); 72 | -------------------------------------------------------------------------------- /src/assets/less/btn.less: -------------------------------------------------------------------------------- 1 | @import './variable'; 2 | 3 | .colorBtn(@color) { 4 | background: @color; 5 | 6 | &:hover { 7 | color: @color; 8 | 9 | &:before, 10 | &:after { 11 | background: @color; 12 | } 13 | } 14 | } 15 | 16 | .blue-btn { 17 | .colorBtn(@blue); 18 | } 19 | 20 | .light-blue-btn { 21 | .colorBtn(@light-blue); 22 | } 23 | 24 | .red-btn { 25 | .colorBtn(@red); 26 | } 27 | 28 | .pink-btn { 29 | .colorBtn(@pink); 30 | } 31 | 32 | .green-btn { 33 | .colorBtn(@green); 34 | } 35 | 36 | .tiffany-btn { 37 | .colorBtn(@tiffany); 38 | } 39 | 40 | .yellow-btn { 41 | .colorBtn(@yellow); 42 | } 43 | 44 | .pan-btn { 45 | font-size: 14px; 46 | color: #fff; 47 | padding: 14px 36px; 48 | border-radius: 8px; 49 | border: none; 50 | outline: none; 51 | transition: 600ms ease all; 52 | position: relative; 53 | display: inline-block; 54 | 55 | &:hover { 56 | background: #fff; 57 | 58 | &:before, 59 | &:after { 60 | width: 100%; 61 | transition: 600ms ease all; 62 | } 63 | } 64 | 65 | &:before, 66 | &:after { 67 | content: ''; 68 | position: absolute; 69 | top: 0; 70 | right: 0; 71 | height: 2px; 72 | width: 0; 73 | transition: 400ms ease all; 74 | } 75 | 76 | &::after { 77 | right: inherit; 78 | top: inherit; 79 | left: 0; 80 | bottom: 0; 81 | } 82 | } 83 | 84 | .custom-button { 85 | display: inline-block; 86 | line-height: 1; 87 | white-space: nowrap; 88 | cursor: pointer; 89 | background: #fff; 90 | color: #fff; 91 | -webkit-appearance: none; 92 | text-align: center; 93 | box-sizing: border-box; 94 | outline: 0; 95 | margin: 0; 96 | padding: 10px 15px; 97 | font-size: 14px; 98 | border-radius: 4px; 99 | } 100 | -------------------------------------------------------------------------------- /mock/user.ts: -------------------------------------------------------------------------------- 1 | const tokens = { 2 | admin: { 3 | token: 'admin-token' 4 | }, 5 | editor: { 6 | token: 'editor-token' 7 | } 8 | }; 9 | 10 | const users = { 11 | 'admin-token': { 12 | roles: ['admin'], 13 | introduction: 'I am a super administrator', 14 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 15 | name: 'Super Admin' 16 | }, 17 | 'editor-token': { 18 | roles: ['editor'], 19 | introduction: 'I am an editor', 20 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 21 | name: 'Normal Editor' 22 | } 23 | }; 24 | 25 | export default [ 26 | // user login 27 | { 28 | url: '/user/login', 29 | type: 'post', 30 | response: (config) => { 31 | const {username} = config.body; 32 | const token = tokens[username]; 33 | 34 | // mock error 35 | if (!token) { 36 | return { 37 | code: 60204, 38 | message: 'Account and password are incorrect.' 39 | }; 40 | } 41 | 42 | return { 43 | code: 20000, 44 | data: token 45 | }; 46 | } 47 | }, 48 | 49 | // get user info 50 | { 51 | url: '/user/info\.*', 52 | type: 'get', 53 | response: (config) => { 54 | const {token} = config.query; 55 | const info = users[token]; 56 | 57 | // mock error 58 | if (!info) { 59 | return { 60 | code: 50008, 61 | message: 'Login failed, unable to get user details.' 62 | }; 63 | } 64 | 65 | return { 66 | code: 20000, 67 | data: info 68 | }; 69 | } 70 | }, 71 | 72 | // user logout 73 | { 74 | url: '/user/logout', 75 | type: 'post', 76 | response: (_) => { 77 | return { 78 | code: 20000, 79 | data: 'success' 80 | }; 81 | } 82 | } 83 | ]; 84 | -------------------------------------------------------------------------------- /src/utils/scrollTo.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | Math.easeInOutQuad = function (t, b, c, d) { 3 | t /= d / 2; 4 | if (t < 1) { 5 | return c / 2 * t * t + b; 6 | } 7 | t--; 8 | return -c / 2 * (t * (t - 2) - 1) + b; 9 | }; 10 | 11 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts 12 | var requestAnimFrame = (function () { 13 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { 14 | window.setTimeout(callback, 1000 / 60); 15 | }; 16 | })(); 17 | 18 | // because it's so fucking difficult to detect the scrolling element, just move them all 19 | function move(amount) { 20 | document.documentElement.scrollTop = amount; 21 | document.body.parentNode.scrollTop = amount; 22 | document.body.scrollTop = amount; 23 | } 24 | 25 | function position() { 26 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop; 27 | } 28 | 29 | export function scrollTo(to, duration, callback) { 30 | const start = position(); 31 | const change = to - start; 32 | const increment = 20; 33 | let currentTime = 0; 34 | duration = (typeof (duration) === 'undefined') ? 500 : duration; 35 | var animateScroll = function () { 36 | // increment the time 37 | currentTime += increment; 38 | // find the value with the quadratic in-out easing function 39 | var val = Math.easeInOutQuad(currentTime, start, change, duration); 40 | // move the document.body 41 | move(val); 42 | // do the animation unless its over 43 | if (currentTime < duration) { 44 | requestAnimFrame(animateScroll); 45 | } else { 46 | if (callback && typeof (callback) === 'function') { 47 | // the animation is done so lets callback 48 | callback(); 49 | } 50 | } 51 | }; 52 | animateScroll(); 53 | } 54 | -------------------------------------------------------------------------------- /src/directives/clipboard.ts: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/Inndy/vue-clipboard2 2 | const Clipboard = require('clipboard'); 3 | if (!Clipboard) { 4 | throw new Error('you should npm install `clipboard` --save at first '); 5 | } 6 | 7 | export default { 8 | bind(el, binding) { 9 | if (binding.arg === 'success') { 10 | el._v_clipboard_success = binding.value; 11 | } else if (binding.arg === 'error') { 12 | el._v_clipboard_error = binding.value; 13 | } else { 14 | const clipboard = new Clipboard(el, { 15 | text() { 16 | return binding.value; 17 | }, 18 | action() { 19 | return binding.arg === 'cut' ? 'cut' : 'copy'; 20 | } 21 | }); 22 | clipboard.on('success', (e) => { 23 | const callback = el._v_clipboard_success; 24 | callback && callback(e); // eslint-disable-line 25 | }); 26 | clipboard.on('error', (e) => { 27 | const callback = el._v_clipboard_error; 28 | callback && callback(e); // eslint-disable-line 29 | }); 30 | el._v_clipboard = clipboard; 31 | } 32 | }, 33 | update(el, binding) { 34 | if (binding.arg === 'success') { 35 | el._v_clipboard_success = binding.value; 36 | } else if (binding.arg === 'error') { 37 | el._v_clipboard_error = binding.value; 38 | } else { 39 | el._v_clipboard.text = () => { 40 | return binding.value; 41 | }; 42 | el._v_clipboard.action = () => { 43 | return binding.arg === 'cut' ? 'cut' : 'copy'; 44 | }; 45 | } 46 | }, 47 | unbind(el, binding) { 48 | if (binding.arg === 'success') { 49 | delete el._v_clipboard_success; 50 | } else if (binding.arg === 'error') { 51 | delete el._v_clipboard_error; 52 | } else { 53 | el._v_clipboard.destroy(); 54 | delete el._v_clipboard; 55 | } 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/views/componentsDemo/dragKanban.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 40 | 41 | 71 | -------------------------------------------------------------------------------- /public/vendor/vendor.dll.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none} -------------------------------------------------------------------------------- /src/views/componentsDemo/splitPane.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 41 | 42 | 70 | 71 | --------------------------------------------------------------------------------