diff --git a/.babelrc b/.babelrc index 41789cac..b4050098 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,8 @@ { - "presets": ["es2015", "stage-2"], + "presets": [ + ["env", { "modules": false }], + "stage-2" + ], "plugins": ["transform-runtime"], "comments": false } diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ea6e20f5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js index a388ba27..006e9824 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,7 +6,8 @@ module.exports = { }, env: { browser: true, - node: true + node: true, + es6: true, }, extends: 'eslint:recommended', // required to lint *.vue files @@ -22,297 +23,122 @@ module.exports = { } }, // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue 'rules': { - // don't require .vue extension when importing - // 'import/extensions': ['error', 'always', { - // 'js': 'never', - // 'vue': 'never' - // }], - // allow debugger during development - 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, - /* - * Possible Errors - */ - - // disallow unnecessary parentheses - 'no-extra-parens': ['error', 'all', {'nestedBinaryExpressions': false}], - - // disallow negating the left operand of relational operators - 'no-unsafe-negation': 'error', - - // enforce valid JSDoc comments - 'valid-jsdoc': 'off', - - /* - * Best Practices - */ - - // enforce return statements in callbacks of array methods - 'array-callback-return': 'error', - - // enforce consistent brace style for all control statements - curly: ['error', 'multi-line'], - - // enforce consistent newlines before and after dots - 'dot-location': ['error', 'property'], - - // enforce dot notation whenever possible - 'dot-notation': 'error', - - // require the use of === and !== - 'eqeqeq': ['error', 'smart'], - - // disallow the use of arguments.caller or arguments.callee - 'no-caller': 'error', - - // disallow empty functions - 'no-empty-function': 'error', - - // disallow unnecessary calls to .bind() - 'no-extra-bind': 'error', - - // disallow unnecessary labels - 'no-extra-label': 'error', - - // disallow leading or trailing decimal points in numeric literals - 'no-floating-decimal': 'error', - - // disallow assignments to native objects or read-only global variables - 'no-global-assign': 'error', - - // disallow the use of eval()-like methods - 'no-implied-eval': 'error', - - // disallow the use of the __iterator__ property - 'no-iterator': 'error', - - // disallow unnecessary nested blocks - 'no-lone-blocks': 'error', - - // disallow multiple spaces - 'no-multi-spaces': 'error', - - // disallow new operators with the String, Number, and Boolean objects - 'no-new-wrappers': 'error', - - // disallow octal escape sequences in string literals - 'no-octal-escape': 'error', - - // disallow the use of the __proto__ property - 'no-proto': 'error', - - // disallow comparisons where both sides are exactly the same - 'no-self-compare': 'error', - - // disallow throwing literals as exceptions - 'no-throw-literal': 'error', - - // disallow unused expressions - 'no-unused-expressions': 'error', - - // disallow unnecessary calls to .call() and .apply() - 'no-useless-call': 'error', - - // disallow unnecessary concatenation of literals or template literals - 'no-useless-concat': 'error', - - // disallow unnecessary escape characters - 'no-useless-escape': 'error', - - // disallow void operators - 'no-void': 'error', - - // require parentheses around immediate function invocations - 'wrap-iife': 'error', - - // require or disallow “Yoda” conditions - yoda: 'error', - - /* - * Variables - */ - - // disallow labels that share a name with a variable - 'no-label-var': 'error', - - // disallow initializing variables to undefined - 'no-undef-init': 'error', - 'no-undef': 'off', - // disallow the use of variables before they are defined - 'no-use-before-define': 'error', - - /* - * Node.js and CommonJS - */ - - // disallow new operators with calls to require - 'no-new-require': 'error', - - /* - * Stylistic Issues - */ - - // enforce consistent spacing inside array brackets - 'array-bracket-spacing': 'error', - - // enforce consistent spacing inside single-line blocks - 'block-spacing': 'error', - - // enforce consistent brace style for blocks - 'brace-style': ['error', '1tbs', {'allowSingleLine': true}], - - // require or disallow trailing commas - 'comma-dangle': 'error', - - // enforce consistent spacing before and after commas - 'comma-spacing': 'error', - - // enforce consistent comma style - 'comma-style': 'error', - - // enforce consistent spacing inside computed property brackets - 'computed-property-spacing': 'error', - - // require or disallow spacing between function identifiers and their invocations - 'func-call-spacing': 'error', - - // enforce consistent indentation - indent: ['error', 2, {SwitchCase: 1}], - - // enforce the consistent use of either double or single quotes in JSX attributes - 'jsx-quotes': 'error', - - // enforce consistent spacing between keys and values in object literal properties - 'key-spacing': 'error', - - // enforce consistent spacing before and after keywords - 'keyword-spacing': 'error', - - // enforce consistent linebreak style - 'linebreak-style': 'error', - - // require or disallow newlines around directives - 'lines-around-directive': 'error', - - // require constructor names to begin with a capital letter - 'new-cap': 'off', - - // require parentheses when invoking a constructor with no arguments - 'new-parens': 'error', - - // disallow Array constructors - 'no-array-constructor': 'error', - - // disallow Object constructors - 'no-new-object': 'error', - - // disallow trailing whitespace at the end of lines - 'no-trailing-spaces': 'error', - - // disallow ternary operators when simpler alternatives exist - 'no-unneeded-ternary': 'error', - - // disallow whitespace before properties - 'no-whitespace-before-property': 'error', - - // enforce consistent spacing inside braces - 'object-curly-spacing': ['error', 'always'], - - // require or disallow padding within blocks - 'padded-blocks': ['error', 'never'], - - // require quotes around object literal property names - 'quote-props': ['error', 'as-needed'], - - // enforce the consistent use of either backticks, double, or single quotes - quotes: ['error', 'single'], - - // enforce consistent spacing before and after semicolons - 'semi-spacing': 'error', - - // require or disallow semicolons instead of ASI - // semi: ['error', 'never'], - - // enforce consistent spacing before blocks - 'space-before-blocks': 'error', - + 'accessor-pairs': 2, + 'arrow-spacing': [2, { 'before': true, 'after': true }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], + 'camelcase': [0, { 'properties': 'always' }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { 'before': false, 'after': true }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': [2, 'allow-null'], + 'generator-star-spacing': [2, { 'before': true, 'after': true }], + 'handle-callback-err': [2, '^(err|error)$' ], + 'indent': [2, 2, { 'SwitchCase': 1 }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }], + 'keyword-spacing': [2, { 'before': true, 'after': true }], + 'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, 'no-console': 'off', - - // enforce consistent spacing before function definition opening parenthesis - 'space-before-function-paren': ['error', 'never'], - - // enforce consistent spacing inside parentheses - 'space-in-parens': 'error', - - // require spacing around infix operators - 'space-infix-ops': 'error', - - // enforce consistent spacing before or after unary operators - 'space-unary-ops': 'error', - - // enforce consistent spacing after the // or /* in a comment - 'spaced-comment': 'error', - - // require or disallow Unicode byte order mark (BOM) - 'unicode-bom': 'error', - - - /* - * ECMAScript 6 - */ - - // require braces around arrow function bodies - 'arrow-body-style': 'error', - - // require parentheses around arrow function arguments - 'arrow-parens': ['error', 'as-needed'], - - // enforce consistent spacing before and after the arrow in arrow functions - 'arrow-spacing': 'error', - - // enforce consistent spacing around * operators in generator functions - 'generator-star-spacing': ['error', 'after'], - - // disallow duplicate module imports - 'no-duplicate-imports': 'error', - - // disallow unnecessary computed property keys in object literals - 'no-useless-computed-key': 'error', - - // disallow unnecessary constructors - 'no-useless-constructor': 'error', - - // disallow renaming import, export, and destructured assignments to the same name - 'no-useless-rename': 'error', - - // require let or const instead of var - 'no-var': 'error', - - // require or disallow method and property shorthand syntax for object literals - 'object-shorthand': 'error', - - // require arrow functions as callbacks - 'prefer-arrow-callback': 'error', - - // require const declarations for variables that are never reassigned after declared - 'prefer-const': 'error', - - // disallow parseInt() in favor of binary, octal, and hexadecimal literals - 'prefer-numeric-literals': 'error', - - // require rest parameters instead of arguments - 'prefer-rest-params': 'error', - - // require spread operators instead of .apply() - 'prefer-spread': 'error', - - // enforce spacing between rest and spread operators and their expressions - 'rest-spread-spacing': 'error', - - // require or disallow spacing around embedded expressions of template strings - 'template-curly-spacing': 'error', - - // require or disallow spacing around the * in yield* expressions - 'yield-star-spacing': 'error' + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 2, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { 'allowLoop': false, 'allowSwitch': false }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { 'max': 1 }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { 'defaultAssignment': false }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { 'initialized': 'never' }], + 'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { 'before': false, 'after': true }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { 'words': true, 'nonwords': false }], + 'spaced-comment': [2, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { objectsInObjects: false }], + 'array-bracket-spacing': [2, 'never'] } } diff --git a/.gitignore b/.gitignore index 19131cc3..a5060b02 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules/ dist/ static/ckeditor - +gifs/ npm-debug.log test/unit/coverage test/e2e/reports diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 00000000..ea9a5ab8 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + // to edit target browsers: use "browserlist" field in package.json + "autoprefixer": {} + } +} diff --git a/README-en.md b/README-en.md index 6ec251fc..39d4c892 100644 --- a/README-en.md +++ b/README-en.md @@ -1,29 +1,19 @@ +[![vue](https://img.shields.io/badge/vue-2.4.2-brightgreen.svg)](https://github.com/vuejs/vue) +[![element-ui](https://img.shields.io/badge/element--ui-1.4.2-brightgreen.svg)](https://github.com/ElemeFE/element) +[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg)]() + ## Intro > In the past half year, I have been building a backend for management dashboard using Vue. Though the backend has contained greater than 70 pages and over 10 permissions, it still takes insignificant effort to maintain the project. So I decide to make it open source so as to share my development experience and progress on backend. The tech stack is mainly [Vue.js](https://github.com/vuejs/vue)+[Element](https://github.com/ElemeFE/element)+[axios](https://github.com/mzabriskie/axios). Since it's a personal project, all data requests are simulated with [Mock.js](https://github.com/nuysoft/Mock). **Note:** if anyone wants to modify or develop based on this project, please remove the mock files. **Live demo:** http://panjiachen.github.io/vue-element-admin -**Note: element-ui@1.3.3 is used in the project, so vue 2.3.0+ is required.** +**Note: element-ui@1.4.2 is used in the project, so vue 2.3.0+ is required.** -More tutorials incoming. Including articles on: - -- How to build structure of a backend dashboard project from scratch -- How to make a complete user system (e.g. permission authentication, two-factor authentication) -- How to package components (e.g. rich text) -- How to integrate with [Qiniu](https://www.qiniu.com/) -- Other development experience on backend - -Join the group on QQ 591724180. - -**Tutorials:** - -- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) -- [Step by step instructions on playing with backend using Vue Part 1 - Fundamentals](https://juejin.im/post/59097cd7a22b9d0065fb61d2) -- [Step by step instructions on playing with backend using Vue Part 2 - Login permission](https://juejin.im/post/591aa14f570c35006961acac) -- [Step by step instructions on packaging a Vue component](https://segmentfault.com/a/1190000009090836) - -**Please read the Wiki and articles above before creating any issue. Feel free to contribute by making a pull request.** + - vueAdmin-template: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)   + - electron-vue-admin: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) + - Donate:[donate](https://github.com/PanJiaChen/vue-element-admin/blob/master/README-en.md#donate) ## Features @@ -43,6 +33,7 @@ Join the group on QQ 591724180. - 401, 404 error page - Error log - Exporting to Excel +- Upload Excel - Table example - Interactive table example - Drag & drop table example @@ -52,6 +43,10 @@ Join the group on QQ 591724180. - Two-factor authentication - Collapsing sidebar (support nested routes) - Mock data +- cache tabs example +- screenfull +- markdown2html +- views-tab ## Development @@ -101,7 +96,6 @@ npm run build:prod │   ├── App.vue // entry view │   └── main.js // entry for loading components, initialization ├── static // third-party libraries not packed with Webpack -│   ├── jquery │   └── Tinymce // rich text ├── .babelrc // babel-loader config ├── eslintrc.js // eslint config @@ -114,6 +108,10 @@ npm run build:prod ## Changelog Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases). +## Donate +If you find this project useful, you can buy me a cup of coffee +![donate](https://panjiachen.github.io/donate/donation.png) + ## State Management Only status of user and app configuration is managed by Vuex. Other data are managed by their own business pages. @@ -128,6 +126,10 @@ Only status of user and app configuration is managed by Vuex. Other data are man ![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/theme.gif) +#### tabs + +![tabs](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/tabs.gif)
+ #### Collapsing sidebar ![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/leftmenu.gif) @@ -167,3 +169,7 @@ Only status of user and app configuration is managed by Vuex. Other data are man #### More http://panjiachen.github.io/vue-element-admin + +## License + +MIT diff --git a/README.md b/README.md index 7bd62b5d..34e78b92 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,50 @@ # vue-element-admin # + +[![vue](https://img.shields.io/badge/vue-2.4.2-brightgreen.svg)](https://github.com/vuejs/vue) +[![element-ui](https://img.shields.io/badge/element--ui-1.4.2-brightgreen.svg)](https://github.com/ElemeFE/element) +[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg)]() + + [线上地址](http://panjiachen.github.io/vue-element-admin) [English Document](https://github.com/PanJiaChen/vue-element-admin/blob/master/README-en.md) [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) -**注意:该项目目前使用element-ui@1.3.3版本,所以最低兼容 Vue 2.3.0** +[donate](https://github.com/PanJiaChen/vue-element-admin#donate) + +**本项目的定位是后台集成方案,不适合当基础模板来开发。** + - 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)   + - 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) + + + +**注意:该项目目前使用element-ui@1.4.2版本,所以最低兼容 Vue 2.3.0** ## 前言 -> 这半年来一直在用vue写管理后台,目前后台已经有七十多个页面,十几种权限,但维护成本依然很低,所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios.由于是个人项目,所以数据请求都是用了mockjs模拟。注意:在次项目基础上改造开发时请移除mock文件。 +> 这半年来一直在用vue写管理后台,目前后台已经有百来个页面,十几种权限,但维护成本依然很低,所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios由webpack2打包。由于是个人项目,所以数据请求都是用了mockjs模拟。注意:在此项目基础上改造开发时请移除mock文件。 -后续会出一系列的教程配套文章,如如何从零构建后台项目框架,如何做完整的用户系统(如权限验证,二次登录等),如何二次开发组件(如富文本),如何整合七牛等等文章,各种后台开发经验等等。莫急~~ -相应需求,开了一个qq群 591724180 方便大家交流 +写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目: - [wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) - [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2) - [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac) - - [ 手摸手,带你封装一个vue component](https://segmentfault.com/a/1190000009090836) + - [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35) + - [手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56) + - [手摸手,带你封装一个vue component](https://segmentfault.com/a/1190000009090836) + 相应需求,开了一个qq群 `591724180` 方便大家交流 + + 或者可以加入该 **[圈子](https://jianshiapp.com/circles/1209)** 讨论问题 + + **如有问题请先看上述文章和Wiki,若不能满足,欢迎 issue 和 pr** + + **该项目并不是一个脚手架,更倾向于是一个集成解决方案** + + **该项目不支持低版本游览器(如ie),有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)** - **如有问题请先看上述问题和Wiki,不能满足,欢迎issue和pr~** ## 功能 - 登录/注销 @@ -36,18 +60,24 @@ - Sticky - CountTo - echarts图表 -- 401,401错误页面 +- 401,404错误页面 - 错误日志 - 导出excel +- 前端可视化excel - table example - 动态table example - 拖拽table example +- 内联编辑table example - form example - 多环境发布 - dashboard - 二次登录 - 动态侧边栏(支持多级路由) - mock数据 +- cache tabs example +- screenfull +- markdown2html +- views-tab ## 开发 @@ -93,7 +123,6 @@ │   ├── App.vue // 入口页面 │   └── main.js // 入口 加载组件 初始化等 ├── static // 第三方不打包资源 -│   ├── jquery │   └── Tinymce // 富文本 ├── .babelrc // babel-loader 配置 ├── eslintrc.js // eslint 配置项 @@ -107,6 +136,10 @@ ## Changelog Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases). +## Donate +If you find this project useful, you can buy me a cup of coffee +![donate](https://panjiachen.github.io/donate/donation.png) + ## 状态管理 后台只有user和app配置相关状态使用vuex存在全局,其它数据都由每个业务页面自己管理。 @@ -121,6 +154,10 @@ Detailed changes for each release are documented in the [release notes](https:// ![真正的动态换肤](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/theme.gif)
+#### tabs + +![tabs](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/tabs.gif)
+ #### 可收起侧边栏 @@ -165,5 +202,8 @@ Detailed changes for each release are documented in the [release notes](https:// ![enter image description here](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/excel.png) -## [更多demo](http://panjiachen.github.io/vue-element-admin) +## [查看更多demo](http://panjiachen.github.io/vue-element-admin) +## License + +MIT diff --git a/build/utils.js b/build/utils.js index d3aaebb0..b1d54b4d 100644 --- a/build/utils.js +++ b/build/utils.js @@ -3,69 +3,69 @@ var config = require('../config') var ExtractTextPlugin = require('extract-text-webpack-plugin') exports.assetsPath = function (_path) { - var assetsSubDirectory = process.env.NODE_ENV === 'production' - ? config.build.assetsSubDirectory - : config.dev.assetsSubDirectory - return path.posix.join(assetsSubDirectory, _path) + var assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + return path.posix.join(assetsSubDirectory, _path) } exports.cssLoaders = function (options) { - options = options || {} + options = options || {} - var cssLoader = { - loader: 'css-loader', - options: { - minimize: process.env.NODE_ENV === 'production', - sourceMap: options.sourceMap - } + var cssLoader = { + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production', + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + var loaders = [cssLoader] + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) } - // generate loader string to be used with extract text plugin - function generateLoaders(loader, loaderOptions) { - var loaders = [cssLoader] - if (loader) { - loaders.push({ - loader: loader + '-loader', - options: Object.assign({}, loaderOptions, { - sourceMap: options.sourceMap - }) - }) - } - - // Extract CSS when that option is specified - // (which is the case during production build) - if (options.extract) { - return ExtractTextPlugin.extract({ - use: loaders, - fallback: 'vue-style-loader' - }) - } else { - return ['vue-style-loader'].concat(loaders) - } + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) } + } - // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html - return { - css: generateLoaders(), - postcss: generateLoaders(), - less: generateLoaders('less'), - sass: generateLoaders('sass', {indentedSyntax: true}), - scss: generateLoaders('sass'), - stylus: generateLoaders('stylus'), - styl: generateLoaders('stylus') - } + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } } // Generate loaders for standalone style files (outside of .vue) exports.styleLoaders = function (options) { - var output = [] - var loaders = exports.cssLoaders(options) - for (var extension in loaders) { - var loader = loaders[extension] - output.push({ - test: new RegExp('\\.' + extension + '$'), - use: loader - }) - } - return output + var output = [] + var loaders = exports.cssLoaders(options) + for (var extension in loaders) { + var loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + return output } diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index 64fe3303..d82a880c 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -1,85 +1,92 @@ -var path = require('path'); -var utils = require('./utils'); -var config = require('../config'); -var vueLoaderConfig = require('./vue-loader.conf'); +var path = require('path') +var utils = require('./utils') +var config = require('../config') +var vueLoaderConfig = require('./vue-loader.conf') function resolve(dir) { - return path.join(__dirname, '..', dir) + return path.join(__dirname, '..', dir) } -var src = path.resolve(__dirname, '../src'); module.exports = { - entry: { - app: './src/main.js' - }, - output: { - path: config.build.assetsRoot, - filename: '[name].js', - publicPath: process.env.NODE_ENV !== 'development' ? config.build.assetsPublicPath: config.dev.assetsPublicPath - }, - resolve: { - extensions: ['.js', '.vue', '.json'], - alias: { - 'vue$': 'vue/dist/vue.esm.js', - '@': resolve('src'), - 'src': path.resolve(__dirname, '../src'), - 'assets': path.resolve(__dirname, '../src/assets'), - 'components': path.resolve(__dirname, '../src/components'), - 'views': path.resolve(__dirname, '../src/views'), - 'styles': path.resolve(__dirname, '../src/styles'), - 'api': path.resolve(__dirname, '../src/api'), - 'utils': path.resolve(__dirname, '../src/utils'), - 'store': path.resolve(__dirname, '../src/store'), - 'router': path.resolve(__dirname, '../src/router'), - 'mock': path.resolve(__dirname, '../src/mock'), - 'vendor': path.resolve(__dirname, '../src/vendor'), - 'static': path.resolve(__dirname, '../static') + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV !== 'development' ? config.build.assetsPublicPath : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + 'src': path.resolve(__dirname, '../src'), + 'assets': path.resolve(__dirname, '../src/assets'), + 'components': path.resolve(__dirname, '../src/components'), + 'views': path.resolve(__dirname, '../src/views'), + 'styles': path.resolve(__dirname, '../src/styles'), + 'api': path.resolve(__dirname, '../src/api'), + 'utils': path.resolve(__dirname, '../src/utils'), + 'store': path.resolve(__dirname, '../src/store'), + 'router': path.resolve(__dirname, '../src/router'), + 'mock': path.resolve(__dirname, '../src/mock'), + 'vendor': path.resolve(__dirname, '../src/vendor'), + 'static': path.resolve(__dirname, '../static') + } + }, + module: { + rules: [ + { + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: "pre", + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter') + } + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader?cacheDirectory', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' } - }, - externals: { - jquery: 'jQuery' - }, - module: { - rules: [ - // { - // test: /\.(js|vue)$/, - // loader: 'eslint-loader', - // enforce: "pre", - // include: [resolve('src'), resolve('test')], - // options: { - // formatter: require('eslint-friendly-formatter') - // } - // }, - { test: /\.vue$/, - loader: 'vue-loader', - options: vueLoaderConfig - }, - { - test: /\.js$/, - loader: 'babel-loader?cacheDirectory', - include: [resolve('src'), resolve('test')] - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url-loader', - query: { - limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]') - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url-loader', - query: { - limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') - } - } - ] - }, - //注入全局mixin - // sassResources: path.join(__dirname, '../src/styles/mixin.scss'), - // sassLoader: { - // data: path.join(__dirname, '../src/styles/index.scss') - // }, + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + //注入全局mixin + // sassResources: path.join(__dirname, '../src/styles/mixin.scss'), + // sassLoader: { + // data: path.join(__dirname, '../src/styles/index.scss') + // }, } + diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 1cc0a90b..5aec2faf 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -9,39 +9,38 @@ var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // add hot-reload related code to entry chunks Object.keys(baseWebpackConfig.entry).forEach(function (name) { - baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) + baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) function resolveApp(relativePath) { - return path.resolve(relativePath); + return path.resolve(relativePath); } module.exports = merge(baseWebpackConfig, { - module: { - rules: utils.styleLoaders({sourceMap: config.dev.cssSourceMap}) - }, - // cheap-source-map is faster for development - devtool: '#cheap-source-map', - cache: true, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': config.dev.env - }), - new webpack.ProvidePlugin({ - $: 'jquery', - 'jQuery': 'jquery' - }), - // https://github.com/glenjamin/webpack-hot-middleware#installation--usage - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin(), - // https://github.com/ampedandwired/html-webpack-plugin - new HtmlWebpackPlugin({ - filename: 'index.html', - template: 'index.html', - favicon: resolveApp('favicon.ico'), - inject: true, - path:config.dev.staticPath - }), - new FriendlyErrorsPlugin() - ] + module: { + rules: utils.styleLoaders({ + sourceMap: config.dev.cssSourceMap + }) + }, + // cheap-source-map is faster for development + devtool: '#cheap-source-map', + cache: true, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': config.dev.env + }), + // https://github.com/glenjamin/webpack-hot-middleware#installation--usage + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + favicon: resolveApp('favicon.ico'), + inject: true, + path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory + }), + new FriendlyErrorsPlugin() + ] }) + diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 6f01fa59..05f84874 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -12,102 +12,106 @@ var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') var env = process.env.NODE_ENV === 'production' ? config.build.prodEnv : config.build.sitEnv function resolveApp(relativePath) { - return path.resolve(relativePath); + return path.resolve(relativePath); } var webpackConfig = merge(baseWebpackConfig, { - module: { - rules: utils.styleLoaders({ - sourceMap: config.build.productionSourceMap, - extract: true - }) - }, - devtool: config.build.productionSourceMap ? '#source-map' : false, - output: { - path: config.build.assetsRoot, - filename: utils.assetsPath('js/[name].[chunkhash].js'), - chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') - }, - plugins: [ - // http://vuejs.github.io/vue-loader/en/workflow/production.html - new webpack.DefinePlugin({ - 'process.env': env - }), - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false - }, - sourceMap: true - }), - // extract css into its own file - new ExtractTextPlugin({ - filename: utils.assetsPath('css/[name].[contenthash].css') - }), - // Compress extracted CSS. We are using this plugin so that possible - // duplicated CSS from different components can be deduped. - new OptimizeCSSPlugin(), - // generate dist index.html with correct asset hash for caching. - // you can customize output by editing /index.html - // see https://github.com/ampedandwired/html-webpack-plugin - new HtmlWebpackPlugin({ - filename: process.env.NODE_ENV === 'testing' - ? 'index.html' - : config.build.index, - template: 'index.html', - inject: true, - favicon: resolveApp('favicon.ico'), - minify: { - removeComments: true, - collapseWhitespace: true, - removeRedundantAttributes: true, - useShortDoctype: true, - removeEmptyAttributes: true, - removeStyleLinkTypeAttributes: true, - keepClosingSlash: true, - minifyJS: true, - minifyCSS: true, - minifyURLs: true - }, - path:config.build.staticPath, - // necessary to consistently work with multiple chunks via CommonsChunkPlugin - chunksSortMode: 'dependency' - }), - // split vendor js into its own file - new webpack.optimize.CommonsChunkPlugin({ - name: 'vendor', - minChunks: function (module, count) { - // any required modules inside node_modules are extracted to vendor - return ( - module.resource && - /\.js$/.test(module.resource) && - module.resource.indexOf( - path.join(__dirname, '../node_modules') - ) === 0 - ) - } - }), - // extract webpack runtime and module manifest to its own file in order to - // prevent vendor hash from being updated whenever app bundle is updated - new webpack.optimize.CommonsChunkPlugin({ - name: 'manifest', - chunks: ['vendor'] - }), - // copy custom static assets - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../static'), - to: config.build.assetsSubDirectory, - ignore: ['.*'] - } - ]), - new webpack.ProvidePlugin({ - $: 'jquery', - 'jQuery': 'jquery' - }) - ] + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true + }) + }, + devtool: config.build.productionSourceMap ? '#source-map' : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'), + publicPath: config.build.assetsPublicPath + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false + }, + sourceMap: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css') + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin(), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true, + favicon: resolveApp('favicon.ico'), + minify: { + removeComments: true, + collapseWhitespace: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeStyleLinkTypeAttributes: true, + keepClosingSlash: true, + minifyJS: true, + minifyCSS: true, + minifyURLs: true + }, + path: config.build.assetsPublicPath + config.build.assetsSubDirectory, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // cache Module Identifiers + new webpack.HashedModuleIdsPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks: function (module, count) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // split echarts into its own file + new webpack.optimize.CommonsChunkPlugin({ + async: 'echarts', + minChunks(module) { + var context = module.context; + return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0); + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + chunks: ['vendor'] + }), + // copy custom static assets + new CopyWebpackPlugin([{ + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + }]) + ] }) if (config.build.bundleAnalyzerReport) { - var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin - webpackConfig.plugins.push(new BundleAnalyzerPlugin()) + var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig + diff --git a/config/index.js b/config/index.js index 9178385b..581d53a3 100644 --- a/config/index.js +++ b/config/index.js @@ -7,10 +7,9 @@ module.exports = { prodEnv: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), - assetsSubDirectory: '', - assetsPublicPath: './', //生产环境assetsPublicPath: '/' - staticPath:'./', //生产环境 staticPath:'' - productionSourceMap: true, + assetsSubDirectory: 'static', + assetsPublicPath: './', //请根据自己路径配置更改 + productionSourceMap: false, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: @@ -29,7 +28,6 @@ module.exports = { autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/', - staticPath:'/static/', proxyTable: {}, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README diff --git a/documentImg/code1.png b/documentImg/code1.png deleted file mode 100644 index e29c67b9..00000000 Binary files a/documentImg/code1.png and /dev/null differ diff --git a/documentImg/iconfont.png b/documentImg/iconfont.png deleted file mode 100644 index a2159172..00000000 Binary files a/documentImg/iconfont.png and /dev/null differ diff --git a/gifs/tabs.gif b/gifs/tabs.gif new file mode 100644 index 00000000..331d192f Binary files /dev/null and b/gifs/tabs.gif differ diff --git a/index.html b/index.html index 67f2ed3e..830bf1f8 100644 --- a/index.html +++ b/index.html @@ -8,10 +8,8 @@ Juicy - - +
- diff --git a/package.json b/package.json index b0c425d7..ae01ad50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "juicy", - "version": "1.0.1", + "version": "2.1.0", "description": "A Vue.js admin", "author": "Pan ", "license": "MIT", @@ -13,57 +13,59 @@ "lint": "eslint --ext .js,.vue src" }, "dependencies": { - "axios": "0.15.3", - "codemirror": "5.25.2", - "dropzone": "4.3.0", - "echarts": "3.4.0", - "element-ui": "1.3.3", + "axios": "0.16.2", + "codemirror": "5.26.0", + "dropzone": "5.1.0", + "echarts": "3.6.2", + "element-ui": "1.4.2", "file-saver": "1.3.3", - "jquery": "3.1.1", - "js-cookie": "2.1.3", + "js-cookie": "2.1.4", "jsonlint": "1.6.2", "mockjs": "1.0.1-beta3", - "normalize.css": "3.0.2", + "normalize.css": "7.0.0", "nprogress": "0.2.0", + "screenfull": "3.2.2", + "showdown": "1.7.1", "simplemde": "1.11.2", "sortablejs": "1.5.1", - "vue": "2.3.3", + "vue": "2.4.2", "vue-count-to": "1.0.5", - "vue-multiselect": "2.0.0-beta.15", - "vue-router": "2.5.3", - "vuedraggable": "2.8.4", + "vue-multiselect": "2.0.2", + "vue-router": "2.7.0", + "vue-splitpane": "^1.0.0", + "vuedraggable": "2.14.1", "vuex": "2.3.1", - "xlsx": "0.8.1" + "xlsx": "^0.10.8" }, "devDependencies": { - "autoprefixer": "6.7.2", - "babel-core": "6.22.1", - "babel-eslint": "7.1.1", - "babel-loader": "6.2.10", - "babel-plugin-transform-runtime": "6.22.0", - "babel-preset-es2015": "6.22.0", - "babel-preset-stage-2": "6.22.0", - "babel-register": "6.22.0", + "autoprefixer": "7.1.1", + "babel-core": "6.25.0", + "babel-eslint": "7.2.3", + "babel-loader": "7.0.0", + "babel-plugin-transform-runtime": "6.23.0", + "babel-preset-env": "1.5.2", + "babel-preset-stage-2": "6.24.1", + "babel-register": "6.24.1", "chalk": "1.1.3", "connect-history-api-fallback": "1.3.0", "copy-webpack-plugin": "4.0.1", - "cross-env": "4.0.0", - "css-loader": "0.28.0", + "cross-env": "5.0.1", + "css-loader": "0.28.4", "eslint": "3.19.0", - "eslint-friendly-formatter": "2.0.7", + "eslint-friendly-formatter": "3.0.0", "eslint-import-resolver-webpack": "0.8.1", "eslint-loader": "1.7.1", - "eslint-plugin-html": "2.0.1", - "eslint-plugin-import": "2.2.0", + "eslint-plugin-html": "3.0.0", + "eslint-plugin-import": "2.3.0", "eventsource-polyfill": "0.9.6", - "express": "4.14.1", - "extract-text-webpack-plugin": "2.0.0", - "file-loader": "0.10.0", - "friendly-errors-webpack-plugin": "1.1.3", + "express": "4.15.3", + "extract-text-webpack-plugin": "2.1.2", + "file-loader": "0.11.2", + "friendly-errors-webpack-plugin": "1.6.1", "function-bind": "1.1.0", "html-webpack-plugin": "2.28.0", - "http-proxy-middleware": "0.17.3", - "node-sass": "4.5.2", + "http-proxy-middleware": "0.17.4", + "node-sass": "^4.5.0", "opn": "4.0.2", "optimize-css-assets-webpack-plugin": "1.3.0", "ora": "1.1.0", @@ -73,14 +75,14 @@ "script-loader": "0.7.0", "semver": "5.3.0", "style-loader": "0.17.0", + "svg-sprite-loader": "3.2.4", "url-loader": "0.5.8", - "vue-loader": "12.0.4", - "vue-style-loader": "2.0.5", - "vue-template-compiler": "2.3.3", - "webpack": "2.5.1", - "webpack-bundle-analyzer": "2.2.1", - "webpack-dashboard": "0.2.1", - "webpack-dev-middleware": "1.10.0", + "vue-loader": "13.0.4", + "vue-style-loader": "3.0.1", + "vue-template-compiler": "2.4.2", + "webpack": "2.6.1", + "webpack-bundle-analyzer": "2.8.2", + "webpack-dev-middleware": "1.10.2", "webpack-hot-middleware": "2.18.0", "webpack-merge": "4.1.0" }, diff --git a/src/App.vue b/src/App.vue index 823cad8a..440937a9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,16 @@ + + diff --git a/src/api/article.js b/src/api/article.js index fad7e132..2583f0d0 100644 --- a/src/api/article.js +++ b/src/api/article.js @@ -1,16 +1,25 @@ -import fetch from 'utils/fetch'; +import fetch from '@/utils/fetch' -export function getList() { +export function fetchList(query) { return fetch({ url: '/article/list', - method: 'get' - }); + method: 'get', + params: query + }) } -export function getArticle() { +export function fetchArticle() { return fetch({ url: '/article/detail', method: 'get' - }); + }) +} + +export function fetchPv(pv) { + return fetch({ + url: '/article/pv', + method: 'get', + params: { pv } + }) } diff --git a/src/api/article_table.js b/src/api/article_table.js deleted file mode 100644 index 2e3edf52..00000000 --- a/src/api/article_table.js +++ /dev/null @@ -1,17 +0,0 @@ -import fetch from 'utils/fetch'; - -export function fetchList(query) { - return fetch({ - url: '/article_table/list', - method: 'get', - params: query - }); -} - -export function fetchPv(pv) { - return fetch({ - url: '/article_table/pv', - method: 'get', - params: { pv } - }); -} diff --git a/src/api/login.js b/src/api/login.js index cc8599b5..16775960 100644 --- a/src/api/login.js +++ b/src/api/login.js @@ -1,29 +1,29 @@ -import fetch from 'utils/fetch'; +import fetch from '@/utils/fetch' -export function loginByEmail(email, password) { +export function loginByUsername(username, password) { const data = { - email, + username, password - }; + } return fetch({ - url: '/login/loginbyemail', + url: '/login/login', method: 'post', data - }); + }) } export function logout() { return fetch({ url: '/login/logout', method: 'post' - }); + }) } -export function getInfo(token) { +export function getUserInfo(token) { return fetch({ url: '/user/info', method: 'get', params: { token } - }); + }) } diff --git a/src/api/qiniu.js b/src/api/qiniu.js index ce998a59..a5facdc0 100644 --- a/src/api/qiniu.js +++ b/src/api/qiniu.js @@ -1,28 +1,8 @@ -// import fetch, { tpFetch } from 'utils/fetch'; +import fetch from '@/utils/fetch' -// export function getToken() { -// return fetch({ -// url: '/qiniu/upload/token', -// method: 'get' -// }); -// } -// export function upload(data) { -// return tpFetch({ -// url: 'https://upload.qbox.me', -// method: 'post', -// data -// }); -// } - - -// /* 外部uri转七牛uri*/ -// export function netUpload(token, net_url) { -// const imgData = { -// net_url -// }; -// return fetch({ -// url: '/qiniu/upload/net/async', -// method: 'post', -// data: imgData -// }); -// } +export function getToken() { + return fetch({ + url: '/qiniu/upload/token', // 假地址 自行替换 + method: 'get' + }) +} diff --git a/src/api/remoteSearch.js b/src/api/remoteSearch.js index ed45bd74..01ca9109 100644 --- a/src/api/remoteSearch.js +++ b/src/api/remoteSearch.js @@ -1,9 +1,9 @@ -import fetch from 'utils/fetch'; +import fetch from '@/utils/fetch' export function userSearch(name) { return fetch({ url: '/search/user', method: 'get', params: { name } - }); + }) } diff --git a/src/assets/401.gif b/src/assets/401_images/401.gif similarity index 100% rename from src/assets/401.gif rename to src/assets/401_images/401.gif diff --git a/src/assets/404.png b/src/assets/404_images/404.png similarity index 100% rename from src/assets/404.png rename to src/assets/404_images/404.png diff --git a/src/assets/404_cloud.png b/src/assets/404_images/404_cloud.png similarity index 100% rename from src/assets/404_cloud.png rename to src/assets/404_images/404_cloud.png diff --git a/src/assets/echarts-macarons.js b/src/assets/echarts-macarons.js new file mode 100644 index 00000000..0678ef83 --- /dev/null +++ b/src/assets/echarts-macarons.js @@ -0,0 +1,199 @@ +/* eslint-disable */ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports', 'echarts'], factory); + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + factory(exports, require('echarts')); + } else { + // Browser globals + factory({}, root.echarts); + } +}(this, function (exports, echarts) { + var log = function (msg) { + if (typeof console !== 'undefined') { + console && console.error && console.error(msg); + } + }; + if (!echarts) { + log('ECharts is not Loaded'); + return; + } + + var colorPalette = [ + '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80', + '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa', + '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050', + '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089' + ]; + + + var theme = { + color: colorPalette, + + title: { + textStyle: { + fontWeight: 'normal', + color: '#008acd' + } + }, + + visualMap: { + itemWidth: 15, + color: ['#5ab1ef','#e0ffff'] + }, + + toolbox: { + iconStyle: { + normal: { + borderColor: colorPalette[0] + } + } + }, + + tooltip: { + backgroundColor: 'rgba(50,50,50,0.5)', + axisPointer : { + type : 'line', + lineStyle : { + color: '#008acd' + }, + crossStyle: { + color: '#008acd' + }, + shadowStyle : { + color: 'rgba(200,200,200,0.2)' + } + } + }, + + dataZoom: { + dataBackgroundColor: '#efefff', + fillerColor: 'rgba(182,162,222,0.2)', + handleColor: '#008acd' + }, + + grid: { + borderColor: '#eee' + }, + + categoryAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + valueAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitArea : { + show : true, + areaStyle : { + color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)'] + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + timeline : { + lineStyle : { + color : '#008acd' + }, + controlStyle : { + normal : { color : '#008acd'}, + emphasis : { color : '#008acd'} + }, + symbol : 'emptyCircle', + symbolSize : 3 + }, + + line: { + smooth : true, + symbol: 'emptyCircle', + symbolSize: 3 + }, + + candlestick: { + itemStyle: { + normal: { + color: '#d87a80', + color0: '#2ec7c9', + lineStyle: { + color: '#d87a80', + color0: '#2ec7c9' + } + } + } + }, + + scatter: { + symbol: 'circle', + symbolSize: 4 + }, + + map: { + label: { + normal: { + textStyle: { + color: '#d87a80' + } + } + }, + itemStyle: { + normal: { + borderColor: '#eee', + areaColor: '#ddd' + }, + emphasis: { + areaColor: '#fe994e' + } + } + }, + + graph: { + color: colorPalette + }, + + gauge : { + axisLine: { + lineStyle: { + color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']], + width: 10 + } + }, + axisTick: { + splitNumber: 10, + length :15, + lineStyle: { + color: 'auto' + } + }, + splitLine: { + length :22, + lineStyle: { + color: 'auto' + } + }, + pointer : { + width : 5 + } + } + }; + + echarts.registerTheme('macarons', theme); +})); diff --git a/src/assets/iconfont/iconfont.js b/src/assets/iconfont/iconfont.js deleted file mode 100644 index 6b10563b..00000000 --- a/src/assets/iconfont/iconfont.js +++ /dev/null @@ -1 +0,0 @@ -(function(window){var svgSprite=""+""+''+""+''+""+""+""+''+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+''+""+""+""+''+""+''+""+''+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+""+""+''+""+''+""+''+""+""+""+''+""+''+""+""+""+"";var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file diff --git a/src/components/BackToTop/index.vue b/src/components/BackToTop/index.vue new file mode 100644 index 00000000..cbb1f21d --- /dev/null +++ b/src/components/BackToTop/index.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/components/Charts/barPercent.vue b/src/components/Charts/barPercent.vue deleted file mode 100644 index e82422fe..00000000 --- a/src/components/Charts/barPercent.vue +++ /dev/null @@ -1,104 +0,0 @@ - - diff --git a/src/components/Charts/keyboard.vue b/src/components/Charts/keyboard.vue index 8b349763..59aca4dc 100644 --- a/src/components/Charts/keyboard.vue +++ b/src/components/Charts/keyboard.vue @@ -1,112 +1,113 @@ + diff --git a/src/components/Charts/keyboard2.vue b/src/components/Charts/keyboard2.vue index 605d7c1d..4c2a0bbd 100644 --- a/src/components/Charts/keyboard2.vue +++ b/src/components/Charts/keyboard2.vue @@ -1,149 +1,150 @@ + diff --git a/src/components/Charts/line.vue b/src/components/Charts/line.vue deleted file mode 100644 index ab416c76..00000000 --- a/src/components/Charts/line.vue +++ /dev/null @@ -1,146 +0,0 @@ - - diff --git a/src/components/Charts/lineMarker.vue b/src/components/Charts/lineMarker.vue index cbba78bc..80e6b7ec 100644 --- a/src/components/Charts/lineMarker.vue +++ b/src/components/Charts/lineMarker.vue @@ -1,219 +1,222 @@ + diff --git a/src/components/Charts/mixChart.vue b/src/components/Charts/mixChart.vue new file mode 100644 index 00000000..05260b9f --- /dev/null +++ b/src/components/Charts/mixChart.vue @@ -0,0 +1,267 @@ + + + diff --git a/src/components/Charts/mixchart.vue b/src/components/Charts/mixchart.vue deleted file mode 100644 index c7c9fe58..00000000 --- a/src/components/Charts/mixchart.vue +++ /dev/null @@ -1,266 +0,0 @@ - - diff --git a/src/components/Dropzone/index.vue b/src/components/Dropzone/index.vue index a6b86227..d83e4ee7 100644 --- a/src/components/Dropzone/index.vue +++ b/src/components/Dropzone/index.vue @@ -1,62 +1,63 @@ + diff --git a/src/components/Github/index.vue b/src/components/Github/index.vue new file mode 100644 index 00000000..cd2677ca --- /dev/null +++ b/src/components/Github/index.vue @@ -0,0 +1,13 @@ + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue index fc330374..6ec90362 100644 --- a/src/components/Hamburger/index.vue +++ b/src/components/Hamburger/index.vue @@ -1,36 +1,46 @@ diff --git a/src/components/Icon-svg/index.js b/src/components/Icon-svg/index.js deleted file mode 100644 index 55d342a9..00000000 --- a/src/components/Icon-svg/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import Vue from 'vue' - -function registerAllComponents(requireContext) { - return requireContext.keys().forEach(comp => { - const vueComp = requireContext(comp) - const compName = vueComp.name ? vueComp.name.toLowerCase() : /\/([\w-]+)\.vue$/.exec(comp)[1] - Vue.component(compName, vueComp) - }) -} - -registerAllComponents(require.context('./', false, /\.vue$/)) diff --git a/src/components/Icon-svg/index.vue b/src/components/Icon-svg/index.vue new file mode 100644 index 00000000..5ac1ec60 --- /dev/null +++ b/src/components/Icon-svg/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/components/Icon-svg/wscn-icon-stack.vue b/src/components/Icon-svg/wscn-icon-stack.vue deleted file mode 100644 index bf7b07e1..00000000 --- a/src/components/Icon-svg/wscn-icon-stack.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/src/components/Icon-svg/wscn-icon-svg.vue b/src/components/Icon-svg/wscn-icon-svg.vue deleted file mode 100644 index 04b01f4a..00000000 --- a/src/components/Icon-svg/wscn-icon-svg.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/src/components/ImageCropper/index.vue b/src/components/ImageCropper/index.vue index f9415a3d..1e5c28b2 100644 --- a/src/components/ImageCropper/index.vue +++ b/src/components/ImageCropper/index.vue @@ -328,6 +328,7 @@ // 关闭控件 off() { this.show = false; + this.$emit('close'); }, // 设置步骤 setStep(step) { diff --git a/src/components/ImageCropper/lang.js b/src/components/ImageCropper/lang.js index f2fa9210..264f914c 100644 --- a/src/components/ImageCropper/lang.js +++ b/src/components/ImageCropper/lang.js @@ -37,5 +37,5 @@ const langBag = { lowestPx: 'The lowest pixel in the image: ' } } -}; -export default langBag; +} +export default langBag diff --git a/src/components/MDinput/index.vue b/src/components/MDinput/index.vue index c0164cc4..72658598 100644 --- a/src/components/MDinput/index.vue +++ b/src/components/MDinput/index.vue @@ -1,147 +1,147 @@ diff --git a/src/components/PanThumb/index.vue b/src/components/PanThumb/index.vue index 00b2b2e0..7b507805 100644 --- a/src/components/PanThumb/index.vue +++ b/src/components/PanThumb/index.vue @@ -1,145 +1,140 @@ + diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue new file mode 100644 index 00000000..ceca3c81 --- /dev/null +++ b/src/components/Screenfull/index.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/src/components/SplitPane/Pane.vue b/src/components/SplitPane/Pane.vue index c6765df0..d13d1286 100644 --- a/src/components/SplitPane/Pane.vue +++ b/src/components/SplitPane/Pane.vue @@ -1,41 +1,44 @@ - diff --git a/src/components/SplitPane/Resizer.vue b/src/components/SplitPane/Resizer.vue index e6499c9b..07b6fb3b 100644 --- a/src/components/SplitPane/Resizer.vue +++ b/src/components/SplitPane/Resizer.vue @@ -1,71 +1,72 @@ + + diff --git a/src/components/SplitPane/index.vue b/src/components/SplitPane/index.vue index 40b2b57f..477405b6 100644 --- a/src/components/SplitPane/index.vue +++ b/src/components/SplitPane/index.vue @@ -1,111 +1,111 @@ - + + diff --git a/src/components/Sticky/index.vue b/src/components/Sticky/index.vue index 4cf6af45..15e18f94 100644 --- a/src/components/Sticky/index.vue +++ b/src/components/Sticky/index.vue @@ -1,73 +1,74 @@ - diff --git a/src/components/Tinymce/components/editorAudio.vue b/src/components/Tinymce/components/editorAudio.vue deleted file mode 100644 index 9e0799b1..00000000 --- a/src/components/Tinymce/components/editorAudio.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - diff --git a/src/components/Tinymce/components/editorImage.vue b/src/components/Tinymce/components/editorImage.vue index a4225746..f390c134 100644 --- a/src/components/Tinymce/components/editorImage.vue +++ b/src/components/Tinymce/components/editorImage.vue @@ -6,13 +6,13 @@ + :on-success="handleSuccess" + :before-upload="beforeUpload"> 点击上传 取 消 @@ -21,62 +21,76 @@ diff --git a/src/components/Tinymce/components/editorVideo.vue b/src/components/Tinymce/components/editorVideo.vue deleted file mode 100644 index 544f8fce..00000000 --- a/src/components/Tinymce/components/editorVideo.vue +++ /dev/null @@ -1,167 +0,0 @@ - - - - diff --git a/src/components/Tinymce/index.vue b/src/components/Tinymce/index.vue index f8dde5e0..0dd164b3 100644 --- a/src/components/Tinymce/index.vue +++ b/src/components/Tinymce/index.vue @@ -1,110 +1,87 @@ @@ -231,19 +167,16 @@ .tinymce-container { position: relative } - .tinymce-textarea { visibility: hidden; z-index: -1; } - .editor-custom-btn-container { position: absolute; right: 15px; /*z-index: 2005;*/ top: 18px; } - .editor-upload-btn { display: inline-block; } diff --git a/src/components/TodoList/Todo.vue b/src/components/TodoList/Todo.vue new file mode 100644 index 00000000..028acfc7 --- /dev/null +++ b/src/components/TodoList/Todo.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/components/TodoList/index.scss b/src/components/TodoList/index.scss new file mode 100644 index 00000000..8ed13d73 --- /dev/null +++ b/src/components/TodoList/index.scss @@ -0,0 +1,318 @@ +.todoapp { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 40PX auto 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 300; + button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + :focus { + outline: 0; + } + .hidden { + display: none; + } + .todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); + } + .todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; + } + .todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; + } + .todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; + } + .todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; + } + .new-todo, + .edit { + position: relative; + margin: 0; + width: 100%; + font-size: 18px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + .new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03); + } + .main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; + } + .toggle-all { + text-align: center; + border: none; + /* Mobile Safari */ + opacity: 0; + position: absolute; + } + .toggle-all+label { + width: 60px; + height: 34px; + font-size: 0; + position: absolute; + top: -52px; + left: -13px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + } + .toggle-all+label:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; + } + .toggle-all:checked+label:before { + color: #737373; + } + .todo-list { + margin: 0; + padding: 0; + list-style: none; + } + .todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; + } + .todo-list li:last-child { + border-bottom: none; + } + .todo-list li.editing { + border-bottom: none; + padding: 0; + } + .todo-list li.editing .edit { + display: block; + width: 506px; + padding: 12px 16px; + margin: 0 0 0 43px; + } + .todo-list li.editing .view { + display: none; + } + .todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; + /* Mobile Safari */ + -webkit-appearance: none; + appearance: none; + } + .todo-list li .toggle { + opacity: 0; + } + .todo-list li .toggle+label { + /* + Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 + IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ + */ + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); + background-repeat: no-repeat; + background-position: center left; + background-size: 36px; + } + .todo-list li .toggle:checked+label { + background-size: 36px; + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); + } + .todo-list li label { + word-break: break-all; + padding: 15px 15px 15px 50px; + display: block; + line-height: 1.0; + font-size: 14px; + transition: color 0.4s; + } + .todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; + } + .todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + transition: color 0.2s ease-out; + } + .todo-list li .destroy:hover { + color: #af5b5e; + } + .todo-list li .destroy:after { + content: '×'; + } + .todo-list li:hover .destroy { + display: block; + } + .todo-list li .edit { + display: none; + } + .todo-list li.editing:last-child { + margin-bottom: -1px; + } + .footer { + color: #777; + position: relative; + padding: 10px 15px; + height: 40px; + text-align: center; + border-top: 1px solid #e6e6e6; + } + .footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2); + } + .todo-count { + float: left; + text-align: left; + } + .todo-count strong { + font-weight: 300; + } + .filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: -20px; + } + .filters li { + display: inline; + } + .filters li a { + color: inherit; + margin: 3px; + font-size: 12px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; + } + .filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); + } + .filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); + } + .clear-completed, + html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; + } + .clear-completed:hover { + text-decoration: underline; + } + .info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; + } + .info p { + line-height: 1; + } + .info a { + color: inherit; + text-decoration: none; + font-weight: 400; + } + .info a:hover { + text-decoration: underline; + } + /* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox +*/ + @media screen and (-webkit-min-device-pixel-ratio:0) { + .toggle-all, + .todo-list li .toggle { + background: none; + } + .todo-list li .toggle { + height: 40px; + } + } + @media (max-width: 430px) { + .footer { + height: 50px; + } + .filters { + bottom: 10px; + } + } +} diff --git a/src/components/TodoList/index.vue b/src/components/TodoList/index.vue new file mode 100644 index 00000000..8ce6af46 --- /dev/null +++ b/src/components/TodoList/index.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/components/Upload/singleImage.vue b/src/components/Upload/singleImage.vue index f4a0a546..18b892fe 100644 --- a/src/components/Upload/singleImage.vue +++ b/src/components/Upload/singleImage.vue @@ -1,14 +1,7 @@ + diff --git a/src/components/Upload/singleImage3.vue b/src/components/Upload/singleImage3.vue index 2a01d0e1..4183b88c 100644 --- a/src/components/Upload/singleImage3.vue +++ b/src/components/Upload/singleImage3.vue @@ -1,154 +1,146 @@ + diff --git a/src/components/UploadExcel/index.vue b/src/components/UploadExcel/index.vue new file mode 100644 index 00000000..f02aeae7 --- /dev/null +++ b/src/components/UploadExcel/index.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/components/jsonEditor/index.vue b/src/components/jsonEditor/index.vue index c49d482f..dcb5ee2d 100644 --- a/src/components/jsonEditor/index.vue +++ b/src/components/jsonEditor/index.vue @@ -4,61 +4,61 @@ - diff --git a/src/components/twoDndList/index.vue b/src/components/twoDndList/index.vue index c7c3577e..09e1fe0d 100644 --- a/src/components/twoDndList/index.vue +++ b/src/components/twoDndList/index.vue @@ -3,21 +3,20 @@

{{list1Title}}

-
+
[{{element.author}}] {{element.title}}
- - + +
-

{{list2Title}}

-
+
[{{element.author}}] {{element.title}}
@@ -26,131 +25,134 @@ diff --git a/src/directive/sticky.js b/src/directive/sticky.js index 7eb9e0f4..d7da0891 100644 --- a/src/directive/sticky.js +++ b/src/directive/sticky.js @@ -1,99 +1,91 @@ -(function() { - const vueSticky = {}; - let listenAction; - vueSticky.install = Vue => { - Vue.directive('sticky', { - inserted(el, binding) { - const params = binding.value || {}, - stickyTop = params.stickyTop || 0, - zIndex = params.zIndex || 1000, - elStyle = el.style; +const vueSticky = {} +let listenAction +vueSticky.install = Vue => { + Vue.directive('sticky', { + inserted(el, binding) { + const params = binding.value || {} + const stickyTop = params.stickyTop || 0 + const zIndex = params.zIndex || 1000 + const elStyle = el.style - elStyle.position = '-webkit-sticky'; - elStyle.position = 'sticky'; + elStyle.position = '-webkit-sticky' + elStyle.position = 'sticky' // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary) // if (~elStyle.position.indexOf('sticky')) { // elStyle.top = `${stickyTop}px`; // elStyle.zIndex = zIndex; // return // } - const elHeight = el.getBoundingClientRect().height; - const elWidth = el.getBoundingClientRect().width; - elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`; + const elHeight = el.getBoundingClientRect().height + const elWidth = el.getBoundingClientRect().width + elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` - const parentElm = el.parentNode || document.documentElement; - const placeholder = document.createElement('div'); - placeholder.style.display = 'none'; - placeholder.style.width = `${elWidth}px`; - placeholder.style.height = `${elHeight}px`; - parentElm.insertBefore(placeholder, el) + const parentElm = el.parentNode || document.documentElement + const placeholder = document.createElement('div') + placeholder.style.display = 'none' + placeholder.style.width = `${elWidth}px` + placeholder.style.height = `${elHeight}px` + parentElm.insertBefore(placeholder, el) - let active = false; + let active = false - const getScroll = (target, top) => { - const prop = top ? 'pageYOffset' : 'pageXOffset'; - const method = top ? 'scrollTop' : 'scrollLeft'; - let ret = target[prop]; - if (typeof ret !== 'number') { - ret = window.document.documentElement[method]; - } - return ret; - }; - - const sticky = () => { - if (active) { - return - } - if (!elStyle.height) { - elStyle.height = `${el.offsetHeight}px` - } - - elStyle.position = 'fixed'; - elStyle.width = `${elWidth}px`; - placeholder.style.display = 'inline-block'; - active = true - }; - - const reset = () => { - if (!active) { - return - } - - elStyle.position = ''; - placeholder.style.display = 'none'; - active = false; - }; - - const check = () => { - const scrollTop = getScroll(window, true); - const offsetTop = el.getBoundingClientRect().top; - if (offsetTop < stickyTop) { - sticky(); - } else { - if (scrollTop < elHeight + stickyTop) { - reset() - } - } - }; - listenAction = () => { - check() - }; - - window.addEventListener('scroll', listenAction) - }, - - unbind() { - window.removeEventListener('scroll', listenAction) + const getScroll = (target, top) => { + const prop = top ? 'pageYOffset' : 'pageXOffset' + const method = top ? 'scrollTop' : 'scrollLeft' + let ret = target[prop] + if (typeof ret !== 'number') { + ret = window.document.documentElement[method] + } + return ret } - }) - }; - if (typeof exports == 'object') { - module.exports = vueSticky - } else if (typeof define == 'function' && define.amd) { - define([], () => vueSticky) - } else if (window.Vue) { - window.vueSticky = vueSticky; - Vue.use(vueSticky) - } -}()); + + const sticky = () => { + if (active) { + return + } + if (!elStyle.height) { + elStyle.height = `${el.offsetHeight}px` + } + + elStyle.position = 'fixed' + elStyle.width = `${elWidth}px` + placeholder.style.display = 'inline-block' + active = true + } + + const reset = () => { + if (!active) { + return + } + + elStyle.position = '' + placeholder.style.display = 'none' + active = false + } + + const check = () => { + const scrollTop = getScroll(window, true) + const offsetTop = el.getBoundingClientRect().top + if (offsetTop < stickyTop) { + sticky() + } else { + if (scrollTop < elHeight + stickyTop) { + reset() + } + } + } + listenAction = () => { + check() + } + + window.addEventListener('scroll', listenAction) + }, + + unbind() { + window.removeEventListener('scroll', listenAction) + } + }) +} + +export default vueSticky diff --git a/src/directive/waves.js b/src/directive/waves.js index 0f10bd81..ac1d8611 100644 --- a/src/directive/waves.js +++ b/src/directive/waves.js @@ -1,54 +1,42 @@ -import './waves.css'; -(function() { - const vueWaves = {}; - vueWaves.install = (Vue, options = {}) => { - Vue.directive('waves', { - bind(el, binding) { - el.addEventListener('click', e => { - const customOpts = Object.assign(options, binding.value); - const opts = Object.assign({ - ele: el, // 波纹作用元素 - type: 'hit', // hit点击位置扩散center中心点扩展 - color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 - }, customOpts), - target = opts.ele; - if (target) { - target.style.position = 'relative'; - target.style.overflow = 'hidden'; - const rect = target.getBoundingClientRect(); - let ripple = target.querySelector('.waves-ripple'); - if (!ripple) { - ripple = document.createElement('span'); - ripple.className = 'waves-ripple'; - ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'; - target.appendChild(ripple); - } else { - ripple.className = 'waves-ripple'; - } - switch (opts.type) { - case 'center': - ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'; - ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'; - break; - default: - ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'; - ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'; - } - ripple.style.backgroundColor = opts.color; - ripple.className = 'waves-ripple z-active'; - return false; - } - }, false); - } - }) - }; - if (typeof exports == 'object') { - module.exports = vueWaves - } else if (typeof define == 'function' && define.amd) { - define([], () => vueWaves) - } else if (window.Vue) { - window.vueWaves = vueWaves; - Vue.use(vueWaves) - } -}()); +import './waves.css' + +export default{ + bind(el, binding) { + el.addEventListener('click', e => { + const customOpts = Object.assign({}, binding.value) + const opts = Object.assign({ + ele: el, // 波纹作用元素 + type: 'hit', // hit点击位置扩散center中心点扩展 + color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 + }, customOpts) + const target = opts.ele + if (target) { + target.style.position = 'relative' + target.style.overflow = 'hidden' + const rect = target.getBoundingClientRect() + let ripple = target.querySelector('.waves-ripple') + if (!ripple) { + ripple = document.createElement('span') + ripple.className = 'waves-ripple' + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' + target.appendChild(ripple) + } else { + ripple.className = 'waves-ripple' + } + switch (opts.type) { + case 'center': + ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' + ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' + break + default: + ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' + ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' + } + ripple.style.backgroundColor = opts.color + ripple.className = 'waves-ripple z-active' + return false + } + }, false) + } +} diff --git a/src/errorLog.js b/src/errorLog.js new file mode 100644 index 00000000..d52c9cab --- /dev/null +++ b/src/errorLog.js @@ -0,0 +1,14 @@ +import Vue from 'vue' +import errLog from '@/store/errLog' + +// 生产环境错误日志 +if (process.env.NODE_ENV === 'production') { + Vue.config.errorHandler = function(err, vm) { + console.log(err, window.location.href) + errLog.pushLog({ + err, + url: window.location.href, + vm + }) + } +} diff --git a/src/filters/index.js b/src/filters/index.js index 36cb3e7c..15334cb9 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -5,7 +5,7 @@ function pluralize(time, label) { return time + label + 's' } export function timeAgo(time) { - const between = Date.now() / 1000 - Number(time); + const between = Date.now() / 1000 - Number(time) if (between < 3600) { return pluralize(~~(between / 60), ' minute') } else if (between < 86400) { @@ -17,20 +17,19 @@ export function timeAgo(time) { export function parseTime(time, cFormat) { if (arguments.length === 0) { - return null; + return null } if ((time + '').length === 10) { time = +time * 1000 } - - const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'; - let date; - if (typeof time == 'object') { - date = time; + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time } else { - date = new Date(parseInt(time)); + date = new Date(parseInt(time)) } const formatObj = { y: date.getFullYear(), @@ -40,24 +39,24 @@ export function parseTime(time, cFormat) { i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() - }; + } const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { - let value = formatObj[key]; - if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]; + let value = formatObj[key] + if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] if (result.length > 0 && value < 10) { - value = '0' + value; + value = '0' + value } - return value || 0; - }); - return time_str; + return value || 0 + }) + return time_str } export function formatTime(time, option) { - time = +time * 1000; - const d = new Date(time); - const now = Date.now(); + time = +time * 1000 + const d = new Date(time) + const now = Date.now() - const diff = (now - d) / 1000; + const diff = (now - d) / 1000 if (diff < 30) { return '刚刚' @@ -75,8 +74,6 @@ export function formatTime(time, option) { } } - - /* 数字 格式化*/ export function nFormatter(num, digits) { const si = [ @@ -86,23 +83,21 @@ export function nFormatter(num, digits) { { value: 1E9, symbol: 'G' }, { value: 1E6, symbol: 'M' }, { value: 1E3, symbol: 'k' } - ]; + ] for (let i = 0; i < si.length; i++) { if (num >= si[i].value) { - return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol; + return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol } } - return num.toString(); + return num.toString() } - export function html2Text(val) { - const div = document.createElement('div'); - div.innerHTML = val; - return div.textContent || div.innerText; + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText } - export function toThousandslsFilter(num) { - return (+num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,'); + return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) } diff --git a/src/icons/index.js b/src/icons/index.js new file mode 100644 index 00000000..c8e36b9b --- /dev/null +++ b/src/icons/index.js @@ -0,0 +1,12 @@ +import Vue from 'vue' +import IconSvg from '@/components/Icon-svg'// svg组件 +import generateIconsView from '@/views/svg-icons/generateIconsView.js'// just for views/icons , you can delete it +// register globally + +Vue.component('icon-svg', IconSvg) + +const requireAll = requireContext => requireContext.keys().map(requireContext) +const req = require.context('./svg', false, /\.svg$/) +const iconMap = requireAll(req) + +generateIconsView.generate(iconMap) // just for views/icons , you can delete it diff --git a/src/icons/svg/404.svg b/src/icons/svg/404.svg new file mode 100644 index 00000000..bc5bc9fa --- /dev/null +++ b/src/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/EXCEL.svg b/src/icons/svg/EXCEL.svg new file mode 100644 index 00000000..e5dd5cec --- /dev/null +++ b/src/icons/svg/EXCEL.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/QQ.svg b/src/icons/svg/QQ.svg new file mode 100644 index 00000000..97aee717 --- /dev/null +++ b/src/icons/svg/QQ.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/a.svg b/src/icons/svg/a.svg new file mode 100644 index 00000000..6297fe8a --- /dev/null +++ b/src/icons/svg/a.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/b.svg b/src/icons/svg/b.svg new file mode 100644 index 00000000..0c08ff08 --- /dev/null +++ b/src/icons/svg/b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/bug.svg b/src/icons/svg/bug.svg new file mode 100644 index 00000000..a12a9394 --- /dev/null +++ b/src/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/c.svg b/src/icons/svg/c.svg new file mode 100644 index 00000000..17124d90 --- /dev/null +++ b/src/icons/svg/c.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/email.svg b/src/icons/svg/email.svg new file mode 100644 index 00000000..8a87e147 --- /dev/null +++ b/src/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/from.svg b/src/icons/svg/from.svg new file mode 100644 index 00000000..7a4bd166 --- /dev/null +++ b/src/icons/svg/from.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/icons.svg b/src/icons/svg/icons.svg new file mode 100644 index 00000000..906af96a --- /dev/null +++ b/src/icons/svg/icons.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/mima.svg b/src/icons/svg/mima.svg new file mode 100644 index 00000000..920b500b --- /dev/null +++ b/src/icons/svg/mima.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/quanxian.svg b/src/icons/svg/quanxian.svg new file mode 100644 index 00000000..37c60701 --- /dev/null +++ b/src/icons/svg/quanxian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/shouce.svg b/src/icons/svg/shouce.svg new file mode 100644 index 00000000..94c68bb6 --- /dev/null +++ b/src/icons/svg/shouce.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/tab.svg b/src/icons/svg/tab.svg new file mode 100644 index 00000000..657057df --- /dev/null +++ b/src/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/table.svg b/src/icons/svg/table.svg new file mode 100644 index 00000000..083bc8cc --- /dev/null +++ b/src/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/theme.svg b/src/icons/svg/theme.svg new file mode 100644 index 00000000..9c0873c1 --- /dev/null +++ b/src/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/tubiao.svg b/src/icons/svg/tubiao.svg new file mode 100644 index 00000000..b1b31336 --- /dev/null +++ b/src/icons/svg/tubiao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/tuozhuai.svg b/src/icons/svg/tuozhuai.svg new file mode 100644 index 00000000..819c8d50 --- /dev/null +++ b/src/icons/svg/tuozhuai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/weixin.svg b/src/icons/svg/weixin.svg new file mode 100644 index 00000000..d88a64bc --- /dev/null +++ b/src/icons/svg/weixin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/wujiaoxing.svg b/src/icons/svg/wujiaoxing.svg new file mode 100644 index 00000000..685a301d --- /dev/null +++ b/src/icons/svg/wujiaoxing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/xinrenzhinan.svg b/src/icons/svg/xinrenzhinan.svg new file mode 100644 index 00000000..3985ab51 --- /dev/null +++ b/src/icons/svg/xinrenzhinan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/yanjing.svg b/src/icons/svg/yanjing.svg new file mode 100644 index 00000000..194aa45c --- /dev/null +++ b/src/icons/svg/yanjing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/yonghuming.svg b/src/icons/svg/yonghuming.svg new file mode 100644 index 00000000..5971deeb --- /dev/null +++ b/src/icons/svg/yonghuming.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/zonghe.svg b/src/icons/svg/zonghe.svg new file mode 100644 index 00000000..681422ea --- /dev/null +++ b/src/icons/svg/zonghe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/zujian.svg b/src/icons/svg/zujian.svg new file mode 100644 index 00000000..d183e56c --- /dev/null +++ b/src/icons/svg/zujian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main.js b/src/main.js index aa81cb3b..121aef26 100644 --- a/src/main.js +++ b/src/main.js @@ -1,120 +1,28 @@ -// The Vue build version to load with the `import` command -// (runtime-only or standalone) has been set in webpack.base.conf with an alias. -import Vue from 'vue'; -import App from './App'; -import router from './router'; -import store from './store'; -import ElementUI from 'element-ui'; -import 'element-ui/lib/theme-default/index.css'; -import 'assets/custom-theme/index.css'; // 换肤版本element-ui css https://github.com/PanJiaChen/custom-element-theme -import NProgress from 'nprogress'; // Progress 进度条 -import 'nprogress/nprogress.css';// Progress 进度条 样式 -import 'normalize.css/normalize.css';// normalize.css 样式格式化 -import 'styles/index.scss'; // 全局自定义的css样式 -import 'components/Icon-svg/index'; // 封装的svg组件 -import 'assets/iconfont/iconfont'; // iconfont 具体图标见https://github.com/PanJiaChen/vue-element-admin/wiki -import * as filters from './filters'; // 全局vue filter -import Multiselect from 'vue-multiselect';// 使用的一个多选框组件,element-ui的select不能满足所有需求 -import 'vue-multiselect/dist/vue-multiselect.min.css';// 多选框组件css -import Sticky from 'components/Sticky'; // 粘性header组件 -import vueWaves from './directive/waves';// 水波纹指令 -import errLog from 'store/errLog';// error log组件 -import './mock/index.js'; // 该项目所有请求使用mockjs模拟 +import Vue from 'vue' +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-default/index.css' +import App from './App' +import router from './router' +import store from './store' +import * as filters from './filters' // 全局filter +import './icons' // icon +import './errorLog'// error log +import './permission' // 权限 +import './mock' // 该项目所有请求使用mockjs模拟 -// register globally -Vue.component('multiselect', Multiselect); -Vue.component('Sticky', Sticky); -Vue.use(ElementUI); -Vue.use(vueWaves); +Vue.use(ElementUI) // register global utility filters. Object.keys(filters).forEach(key => { Vue.filter(key, filters[key]) -}); +}) -// permissiom judge -function hasPermission(roles, permissionRoles) { - if (roles.indexOf('admin') >= 0) return true; // admin权限 直接通过 - if (!permissionRoles) return true; - return roles.some(role => permissionRoles.indexOf(role) >= 0) -} - -// register global progress. -const whiteList = ['/login', '/authredirect', '/reset', '/sendpwd'];// 不重定向白名单 -router.beforeEach((to, from, next) => { - NProgress.start(); // 开启Progress - if (store.getters.token) { // 判断是否有token - if (to.path === '/login') { - next({ path: '/' }); - } else { - if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 - store.dispatch('GetInfo').then(res => { // 拉取user_info - const roles = res.data.role; - store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表 - router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 - next(to.path); // hack方法 确保addRoutes已完成 - }) - }).catch(err => { - console.log(err); - }); - } else { - // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ - if (hasPermission(store.getters.roles, to.meta.role)) { - next();// - } else { - next({ path: '/401', query: { noGoBack: true } }); - } - // 可删 ↑ - } - } - } else { - if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 - next() - } else { - next('/login'); // 否则全部重定向到登录页 - NProgress.done(); // 在hash模式下 改变手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行! - } - } -}); - - -router.afterEach(() => { - NProgress.done(); // 结束Progress -}); - -// window.onunhandledrejection = e => { -// console.log('unhandled', e.reason, e.promise); -// e.preventDefault() -// }; - -// 生产环境错误日志 -if (process.env === 'production') { - Vue.config.errorHandler = function(err, vm) { - console.log(err, window.location.href); - errLog.pushLog({ - err, - url: window.location.href, - vm - }) - }; -} - -// window.onerror = function (msg, url, lineNo, columnNo, error) { -// console.log('window') -// }; -// -// console.error = (function (origin) { -// return function (errorlog) { -// // handler();//基于业务的日志记录及数据报错 -// console.log('console'+errorlog) -// origin.call(console, errorlog); -// } -// })(console.error); +Vue.config.productionTip = false new Vue({ + el: '#app', router, store, - render: h => h(App) -}).$mount('#app'); - - + template: '', + components: { App } +}) diff --git a/src/mock/article.js b/src/mock/article.js index 539338f1..f6ed1751 100644 --- a/src/mock/article.js +++ b/src/mock/article.js @@ -1,23 +1,50 @@ -import Mock from 'mockjs'; - - -const List = []; -const count = 20; +import Mock from 'mockjs' +import { param2Obj } from '@/utils' +const List = [] +const count = 100 for (let i = 0; i < count; i++) { List.push(Mock.mock({ - id: '@id', - title: '@ctitle(10, 20)', - 'status|1': ['published', 'draft'], + id: '@increment', + timestamp: +Mock.Random.date('T'), author: '@cname', + auditor: '@cname', + title: '@ctitle(10, 20)', + forecast: '@float(0, 100, 2, 2)', + importance: '@integer(1, 3)', + 'type|1': ['CN', 'US', 'JP', 'EU'], + 'status|1': ['published', 'draft', 'deleted'], display_time: '@datetime', pageviews: '@integer(300, 5000)' - })); + })) } export default { - getList: () => List, + getList: config => { + const { importance, type, title, page = 1, limit = 20, sort } = param2Obj(config.url) + + let mockList = List.filter(item => { + if (importance && item.importance !== +importance) return false + if (type && item.type !== type) return false + if (title && item.title.indexOf(title) < 0) return false + return true + }) + + if (sort === '-id') { + mockList = mockList.reverse() + } + + const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) + + return { + total: mockList.length, + items: pageList + } + }, + getPv: () => ({ + pvData: [{ key: 'PC网站', pv: 1024 }, { key: 'mobile网站', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }] + }), getArticle: () => ({ id: 120000000001, author: { key: 'mockPan' }, @@ -34,4 +61,4 @@ export default { tags: [], title: '' }) -}; +} diff --git a/src/mock/article_table.js b/src/mock/article_table.js deleted file mode 100644 index 7d9ecd1e..00000000 --- a/src/mock/article_table.js +++ /dev/null @@ -1,44 +0,0 @@ -import Mock from 'mockjs'; -import { param2Obj } from 'utils'; - -const List = []; -const count = 100; - -for (let i = 0; i < count; i++) { - List.push(Mock.mock({ - id: '@increment', - timestamp: +Mock.Random.date('T'), - author: '@cname', - auditor: '@cname', - title: '@ctitle(10, 20)', - forecast: '@float(0, 100, 2, 2)', - importance: '@integer(1, 3)', - 'type|1': ['FD', 'FE', 'BI', 'VN'], - 'status|1': ['published', 'draft', 'deleted'], - pageviews: '@integer(300, 5000)' - })); -} - -export default { - getList: config => { - const { importance, type, title, page, limit, sort } = param2Obj(config.url); - let mockList = List.filter(item => { - if (importance && item.importance !== +importance) return false; - if (type && item.type !== type) return false; - if (title && item.title.indexOf(title) < 0) return false; - return true; - }); - if (sort === '-id') { - mockList = mockList.reverse() - } - - const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)); - return { - total: mockList.length, - items: pageList - } - }, - getPv: () => ({ - pvData: [{ key: 'PC网站', pv: 1024 }, { key: 'mobile网站', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }] - }) -}; diff --git a/src/mock/index.js b/src/mock/index.js index dea59284..15cea12b 100644 --- a/src/mock/index.js +++ b/src/mock/index.js @@ -1,25 +1,23 @@ -import Mock from 'mockjs'; -import loginAPI from './login'; -import articleAPI from './article'; -import article_tableAPI from './article_table'; -import remoteSearchAPI from './remoteSearch'; +import Mock from 'mockjs' +import loginAPI from './login' +import articleAPI from './article' +import remoteSearchAPI from './remoteSearch' +Mock.setup({ + timeout: '350-600' +}) // 登录相关 -Mock.mock(/\/login\/loginbyemail/, 'post', loginAPI.loginByEmail); -Mock.mock(/\/login\/logout/, 'post', loginAPI.logout); -Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getInfo) +Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername) +Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) +Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo) -// // 文章相关 -Mock.mock(/\/article\/list/, 'get', articleAPI.getList); -Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle); +// 文章相关 +Mock.mock(/\/article\/list/, 'get', articleAPI.getList) +Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle) +Mock.mock(/\/article\/pv/, 'get', articleAPI.getPv) -// // table example相关 -Mock.mock(/\/article_table\/list/, 'get', article_tableAPI.getList); -Mock.mock(/\/article_table\/p/, 'get', article_tableAPI.getPv); +// 搜索相关 +Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser) -// // 搜索相关 -Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser); - - -export default Mock; +export default Mock diff --git a/src/mock/login.js b/src/mock/login.js index fa0f330b..00ce9bde 100644 --- a/src/mock/login.js +++ b/src/mock/login.js @@ -1,4 +1,4 @@ -import { param2Obj } from 'utils'; +import { param2Obj } from '@/utils' const userMap = { admin: { @@ -6,40 +6,36 @@ const userMap = { token: 'admin', introduction: '我是超级管理员', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', - name: '超级管理员小潘', - uid: '001' + name: 'Super Admin' }, editor: { role: ['editor'], token: 'editor', introduction: '我是编辑', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', - name: '普通编辑小张', - uid: '002' - + name: 'Normal Editor' }, developer: { role: ['develop'], token: 'develop', introduction: '我是开发', avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', - name: '工程师小王', - uid: '003' + name: '工程师小王' } } export default { - loginByEmail: config => { - const { email } = JSON.parse(config.body); - return userMap[email.split('@')[0]]; + loginByUsername: config => { + const { username } = JSON.parse(config.body) + return userMap[username] }, - getInfo: config => { - const { token } = param2Obj(config.url); + getUserInfo: config => { + const { token } = param2Obj(config.url) if (userMap[token]) { - return userMap[token]; + return userMap[token] } else { - return Promise.reject('a'); + return Promise.reject('error') } }, logout: () => 'success' -}; +} diff --git a/src/mock/remoteSearch.js b/src/mock/remoteSearch.js index 3d06a4f1..b70f6f7d 100644 --- a/src/mock/remoteSearch.js +++ b/src/mock/remoteSearch.js @@ -1,24 +1,24 @@ -import Mock from 'mockjs'; -import { param2Obj } from 'utils'; +import Mock from 'mockjs' +import { param2Obj } from '@/utils' -const NameList = []; -const count = 100; +const NameList = [] +const count = 100 for (let i = 0; i < count; i++) { NameList.push(Mock.mock({ name: '@first' - })); + })) } NameList.push({ name: 'mockPan' }) export default { searchUser: config => { - const { name } = param2Obj(config.url); + const { name } = param2Obj(config.url) const mockNameList = NameList.filter(item => { const lowerCaseName = item.name.toLowerCase() - if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false; - return true; - }); + if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false + return true + }) return { items: mockNameList } } -}; +} diff --git a/src/permission.js b/src/permission.js new file mode 100644 index 00000000..1070700a --- /dev/null +++ b/src/permission.js @@ -0,0 +1,56 @@ +import router from './router' +import store from './store' +import NProgress from 'nprogress' // Progress 进度条 +import 'nprogress/nprogress.css'// Progress 进度条样式 +import { getToken } from '@/utils/auth' // 验权 + +// permissiom judge +function hasPermission(roles, permissionRoles) { + if (roles.indexOf('admin') >= 0) return true // admin权限 直接通过 + if (!permissionRoles) return true + return roles.some(role => permissionRoles.indexOf(role) >= 0) +} + +// register global progress. +const whiteList = ['/login', '/authredirect']// 不重定向白名单 +router.beforeEach((to, from, next) => { + NProgress.start() // 开启Progress + if (getToken()) { // 判断是否有token + if (to.path === '/login') { + next({ path: '/' }) + } else { + if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetUserInfo').then(res => { // 拉取user_info + const roles = res.data.role + store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表 + router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 + next({ ...to }) // hack方法 确保addRoutes已完成 + }) + }).catch(() => { + store.dispatch('FedLogOut').then(() => { + next({ path: '/login' }) + }) + }) + } else { + // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ + if (hasPermission(store.getters.roles, to.meta.role)) { + next()// + } else { + next({ path: '/401', query: { noGoBack: true }}) + } + // 可删 ↑ + } + } + } else { + if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 + next() + } else { + next('/login') // 否则全部重定向到登录页 + NProgress.done() // 在hash模式下 改变手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行! + } + } +}) + +router.afterEach(() => { + NProgress.done() // 结束Progress +}) diff --git a/src/router/_import_development.js b/src/router/_import_development.js new file mode 100644 index 00000000..b0f53d2c --- /dev/null +++ b/src/router/_import_development.js @@ -0,0 +1 @@ +module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+ diff --git a/src/router/_import_production.js b/src/router/_import_production.js new file mode 100644 index 00000000..331acba4 --- /dev/null +++ b/src/router/_import_production.js @@ -0,0 +1 @@ +module.exports = file => () => import('@/views/' + file + '.vue') diff --git a/src/router/index.js b/src/router/index.js index dc07a721..c235005a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,91 +1,32 @@ -import Vue from 'vue'; -import Router from 'vue-router'; +import Vue from 'vue' +import Router from 'vue-router' +const _import = require('./_import_' + process.env.NODE_ENV) +// in development env not use Lazy Loading,because Lazy Loading too many pages will cause webpack hot update too slow.so only in production use Lazy Loading + +Vue.use(Router) /* layout */ -import Layout from '../views/layout/Layout'; - -/* login */ -import Login from '../views/login/'; -const authRedirect = () => import('../views/login/authredirect'); -const sendPWD = () => import('../views/login/sendpwd'); -const reset = () => import('../views/login/reset'); - -/* dashboard */ -const dashboard = () => import('../views/dashboard/index'); - -/* Introduction */ -const Introduction = () => import('../views/introduction/index'); - -/* components */ -const componentsIndex = () => import('../views/components/index'); -const Tinymce = () => import('../views/components/tinymce'); -const Markdown = () => import('../views/components/markdown'); -const JsonEditor = () => import('../views/components/jsoneditor'); -const DndList = () => import('../views/components/dndlist'); -const AvatarUpload = () => import('../views/components/avatarUpload'); -const Dropzone = () => import('../views/components/dropzone'); -const Sticky = () => import('../views/components/sticky'); -const SplitPane = () => import('../views/components/splitpane'); -const CountTo = () => import('../views/components/countTo'); -const Mixin = () => import('../views/components/mixin'); - - -/* charts */ -const chartIndex = () => import('../views/charts/index'); -const KeyboardChart = () => import('../views/charts/keyboard'); -const KeyboardChart2 = () => import('../views/charts/keyboard2'); -const LineMarker = () => import('../views/charts/line'); -const MixChart = () => import('../views/charts/mixchart'); - -/* error page */ -const Err404 = () => import('../views/error/404'); -const Err401 = () => import('../views/error/401'); - -/* error log */ -const ErrorLog = () => import('../views/errlog/index'); - -/* excel */ -const ExcelDownload = () => import('../views/excel/index'); - -/* theme */ -const Theme = () => import('../views/theme/index'); - -/* example*/ -const TableLayout = () => import('../views/example/table/index'); -const DynamicTable = () => import('../views/example/table/dynamictable'); -const Table = () => import('../views/example/table/table'); -const DragTable = () => import('../views/example/table/dragTable'); -const InlineEditTable = () => import('../views/example/table/inlineEditTable'); -const Form1 = () => import('../views/example/form1'); - -/* permission */ -const Permission = () => import('../views/permission/index'); - - -Vue.use(Router); - - /** - * icon : the icon show in the sidebar - * hidden : if hidden:true will not show in the sidebar - * redirect : if redirect:noredirect will not redirct in the levelbar - * noDropdown : if noDropdown:true will not has submenu - * meta : { role: ['admin'] } will control the page role - **/ +import Layout from '../views/layout/Layout' +/** +* icon : the icon show in the sidebar +* hidden : if `hidden:true` will not show in the sidebar +* redirect : if `redirect:noredirect` will no redirct in the levelbar +* noDropdown : if `noDropdown:true` will has no submenu +* meta : { role: ['admin'] } will control the page role +**/ export const constantRouterMap = [ - { path: '/login', component: Login, hidden: true }, - { path: '/authredirect', component: authRedirect, hidden: true }, - { path: '/sendpwd', component: sendPWD, hidden: true }, - { path: '/reset', component: reset, hidden: true }, - { path: '/404', component: Err404, hidden: true }, - { path: '/401', component: Err401, hidden: true }, + { path: '/login', component: _import('login/index'), hidden: true }, + { path: '/authredirect', component: _import('login/authredirect'), hidden: true }, + { path: '/404', component: _import('errorPage/404'), hidden: true }, + { path: '/401', component: _import('errorPage/401'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', name: '首页', hidden: true, - children: [{ path: 'dashboard', component: dashboard }] + children: [{ path: 'dashboard', component: _import('dashboard/index') }] }, { path: '/introduction', @@ -93,7 +34,7 @@ export const constantRouterMap = [ redirect: '/introduction/index', icon: 'xinrenzhinan', noDropdown: true, - children: [{ path: 'index', component: Introduction, name: '简述' }] + children: [{ path: 'index', component: _import('introduction/index'), name: '简述' }] } ] @@ -101,7 +42,7 @@ export default new Router({ // mode: 'history', //后端支持可开 scrollBehavior: () => ({ y: 0 }), routes: constantRouterMap -}); +}) export const asyncRouterMap = [ { @@ -112,7 +53,14 @@ export const asyncRouterMap = [ icon: 'quanxian', meta: { role: ['admin'] }, noDropdown: true, - children: [{ path: 'index', component: Permission, name: '权限测试页', meta: { role: ['admin'] } }] + children: [{ path: 'index', component: _import('permission/index'), name: '权限测试页', meta: { role: ['admin'] }}] + }, + { + path: '/icon', + component: Layout, + icon: 'icons', + noDropdown: true, + children: [{ path: 'index', component: _import('svg-icons/index'), name: 'icons' }] }, { path: '/components', @@ -121,17 +69,18 @@ export const asyncRouterMap = [ name: '组件', icon: 'zujian', children: [ - { path: 'index', component: componentsIndex, name: '介绍 ' }, - { path: 'tinymce', component: Tinymce, name: '富文本编辑器' }, - { path: 'markdown', component: Markdown, name: 'Markdown' }, - { path: 'jsoneditor', component: JsonEditor, name: 'JSON编辑器' }, - { path: 'dndlist', component: DndList, name: '列表拖拽' }, - { path: 'splitpane', component: SplitPane, name: 'SplitPane' }, - { path: 'avatarupload', component: AvatarUpload, name: '头像上传' }, - { path: 'dropzone', component: Dropzone, name: 'Dropzone' }, - { path: 'sticky', component: Sticky, name: 'Sticky' }, - { path: 'countto', component: CountTo, name: 'CountTo' }, - { path: 'mixin', component: Mixin, name: '小组件' } + { path: 'index', component: _import('components/index'), name: '介绍 ' }, + { path: 'tinymce', component: _import('components/tinymce'), name: '富文本编辑器' }, + { path: 'markdown', component: _import('components/markdown'), name: 'Markdown' }, + { path: 'jsoneditor', component: _import('components/jsonEditor'), name: 'JSON编辑器' }, + { path: 'dndlist', component: _import('components/dndList'), name: '列表拖拽' }, + { path: 'splitpane', component: _import('components/splitpane'), name: 'SplitPane' }, + { path: 'avatarupload', component: _import('components/avatarUpload'), name: '头像上传' }, + { path: 'dropzone', component: _import('components/dropzone'), name: 'Dropzone' }, + { path: 'sticky', component: _import('components/sticky'), name: 'Sticky' }, + { path: 'countto', component: _import('components/countTo'), name: 'CountTo' }, + { path: 'mixin', component: _import('components/mixin'), name: '小组件' }, + { path: 'backtotop', component: _import('components/backToTop'), name: '返回顶部' } ] }, { @@ -139,53 +88,15 @@ export const asyncRouterMap = [ component: Layout, redirect: '/charts/index', name: '图表', - icon: 'tubiaoleixingzhengchang', + icon: 'tubiao', children: [ - { path: 'index', component: chartIndex, name: '介绍' }, - { path: 'keyboard', component: KeyboardChart, name: '键盘图表' }, - { path: 'keyboard2', component: KeyboardChart2, name: '键盘图表2' }, - { path: 'line', component: LineMarker, name: '折线图' }, - { path: 'mixchart', component: MixChart, name: '混合图表' } + { path: 'index', component: _import('charts/index'), name: '介绍' }, + { path: 'keyboard', component: _import('charts/keyboard'), name: '键盘图表' }, + { path: 'keyboard2', component: _import('charts/keyboard2'), name: '键盘图表2' }, + { path: 'line', component: _import('charts/line'), name: '折线图' }, + { path: 'mixchart', component: _import('charts/mixChart'), name: '混合图表' } ] }, - { - path: '/errorpage', - component: Layout, - redirect: 'noredirect', - name: '错误页面', - icon: '404', - children: [ - { path: '401', component: Err401, name: '401' }, - { path: '404', component: Err404, name: '404' } - ] - }, - { - path: '/errlog', - component: Layout, - redirect: 'noredirect', - name: 'errlog', - icon: 'bug', - noDropdown: true, - children: [{ path: 'log', component: ErrorLog, name: '错误日志' }] - }, - { - path: '/excel', - component: Layout, - redirect: 'noredirect', - name: 'excel', - icon: 'EXCEL', - noDropdown: true, - children: [{ path: 'download', component: ExcelDownload, name: '导出excel' }] - }, - { - path: '/theme', - component: Layout, - redirect: 'noredirect', - name: 'theme', - icon: 'theme', - noDropdown: true, - children: [{ path: 'index', component: Theme, name: '换肤' }] - }, { path: '/example', component: Layout, @@ -194,19 +105,64 @@ export const asyncRouterMap = [ icon: 'zonghe', children: [ { - path: '/table', - component: TableLayout, - redirect: '/table/table', - name: 'table', + path: '/example/table', + component: _import('example/table/index'), + redirect: '/example/table/table', + name: 'Table', + icon: 'table', children: [ - { path: 'dynamictable', component: DynamicTable, name: '动态table' }, - { path: 'dragtable', component: DragTable, name: '拖拽table' }, - { path: 'inline_edit_table', component: InlineEditTable, name: 'table内编辑' }, - { path: 'table', component: Table, name: '综合table' } + { path: 'dynamictable', component: _import('example/table/dynamictable/index'), name: '动态table' }, + { path: 'dragtable', component: _import('example/table/dragTable'), name: '拖拽table' }, + { path: 'inline_edit_table', component: _import('example/table/inlineEditTable'), name: 'table内编辑' }, + { path: 'table', component: _import('example/table/table'), name: '综合table' } ] }, - { path: 'form1', component: Form1, name: '综合form1' } + { path: 'form/edit', icon: 'shouce', component: _import('example/form'), name: '编辑Form', meta: { isEdit: true }}, + { path: 'form/create', icon: 'from', component: _import('example/form'), name: '创建Form' }, + { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'Tab' } ] }, + { + path: '/error', + component: Layout, + redirect: 'noredirect', + name: '错误页面', + icon: '404', + children: [ + { path: '401', component: _import('errorPage/401'), name: '401' }, + { path: '404', component: _import('errorPage/404'), name: '404' } + ] + }, + { + path: '/errlog', + component: Layout, + redirect: 'noredirect', + name: 'errlog', + icon: 'bug', + noDropdown: true, + children: [{ path: 'log', component: _import('errlog/index'), name: '错误日志' }] + }, + { + path: '/excel', + component: Layout, + redirect: '/excel/download', + name: 'excel', + icon: 'EXCEL', + children: [ + { path: 'download', component: _import('excel/index'), name: '导出excel' }, + { path: 'download2', component: _import('excel/selectExcel'), name: '导出已选择项' }, + { path: 'upload', component: _import('excel/uploadExcel'), name: 'upload excel' } + ] + }, + { + path: '/theme', + component: Layout, + redirect: 'noredirect', + name: 'theme', + icon: 'theme', + noDropdown: true, + children: [{ path: 'index', component: _import('theme/index'), name: '换肤' }] + }, + { path: '*', redirect: '/404', hidden: true } -]; +] diff --git a/src/store/errLog.js b/src/store/errLog.js index 4401babc..6cfbe3cb 100644 --- a/src/store/errLog.js +++ b/src/store/errLog.js @@ -6,8 +6,8 @@ const errLog = { this.state.errLog.unshift(log) }, clearLog() { - this.state.errLog = []; + this.state.errLog = [] } -}; +} -export default errLog; +export default errLog diff --git a/src/store/getters.js b/src/store/getters.js index 4677e6d6..6d4c72d0 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -1,16 +1,14 @@ const getters = { sidebar: state => state.app.sidebar, + visitedViews: state => state.app.visitedViews, token: state => state.user.token, avatar: state => state.user.avatar, name: state => state.user.name, - uid: state => state.user.uid, - email: state => state.user.email, introduction: state => state.user.introduction, - auth_type: state => state.user.auth_type, status: state => state.user.status, roles: state => state.user.roles, setting: state => state.user.setting, permission_routers: state => state.permission.routers, addRouters: state => state.permission.addRouters -}; +} export default getters diff --git a/src/store/index.js b/src/store/index.js index ee7d313e..9bfa6a77 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,11 +1,11 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import app from './modules/app'; -import user from './modules/user'; -import permission from './modules/permission'; -import getters from './getters'; +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import user from './modules/user' +import permission from './modules/permission' +import getters from './getters' -Vue.use(Vuex); +Vue.use(Vuex) const store = new Vuex.Store({ modules: { @@ -14,6 +14,6 @@ const store = new Vuex.Store({ permission }, getters -}); +}) export default store diff --git a/src/store/modules/app.js b/src/store/modules/app.js index 83049ff5..95cdf5f1 100644 --- a/src/store/modules/app.js +++ b/src/store/modules/app.js @@ -1,28 +1,50 @@ -import Cookies from 'js-cookie'; +import Cookies from 'js-cookie' const app = { state: { sidebar: { opened: !+Cookies.get('sidebarStatus') }, - theme: 'default', - livenewsChannels: Cookies.get('livenewsChannels') || '[]' + visitedViews: [] }, mutations: { TOGGLE_SIDEBAR: state => { if (state.sidebar.opened) { - Cookies.set('sidebarStatus', 1); + Cookies.set('sidebarStatus', 1) } else { - Cookies.set('sidebarStatus', 0); + Cookies.set('sidebarStatus', 0) } - state.sidebar.opened = !state.sidebar.opened; + state.sidebar.opened = !state.sidebar.opened + }, + ADD_VISITED_VIEWS: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push({ name: view.name, path: view.path }) + }, + DEL_VISITED_VIEWS: (state, view) => { + let index + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + index = i + break + } + } + state.visitedViews.splice(index, 1) } }, actions: { - ToggleSideBar: ({ commit }) => { + ToggleSideBar({ commit }) { commit('TOGGLE_SIDEBAR') + }, + addVisitedViews({ commit }, view) { + commit('ADD_VISITED_VIEWS', view) + }, + delVisitedViews({ commit, state }, view) { + return new Promise((resolve) => { + commit('DEL_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) } } -}; +} -export default app; +export default app diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index 926c89b0..50e2bbe8 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -1,5 +1,10 @@ -import { asyncRouterMap, constantRouterMap } from 'src/router'; +import { asyncRouterMap, constantRouterMap } from '@/router' +/** + * 通过meta.role判断是否与当前用户权限匹配 + * @param roles + * @param route + */ function hasPermission(roles, route) { if (route.meta && route.meta.role) { return roles.some(role => route.meta.role.indexOf(role) >= 0) @@ -8,46 +13,50 @@ function hasPermission(roles, route) { } } +/** + * 递归过滤异步路由表,返回符合用户角色权限的路由表 + * @param asyncRouterMap + * @param roles + */ +function filterAsyncRouter(asyncRouterMap, roles) { + const accessedRouters = asyncRouterMap.filter(route => { + if (hasPermission(roles, route)) { + if (route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, roles) + } + return true + } + return false + }) + return accessedRouters +} + const permission = { state: { routers: constantRouterMap, addRouters: [] }, - mutations: { SET_ROUTERS: (state, routers) => { - state.addRouters = routers; - state.routers = constantRouterMap.concat(routers); + state.addRouters = routers + state.routers = constantRouterMap.concat(routers) } }, - actions: { GenerateRoutes({ commit }, data) { return new Promise(resolve => { - const { roles } = data; - const accessedRouters = asyncRouterMap.filter(v => { - if (roles.indexOf('admin') >= 0) return true; - if (hasPermission(roles, v)) { - if (v.children && v.children.length > 0) { - v.children = v.children.filter(child => { - if (hasPermission(roles, child)) { - return child - } - return false; - }); - return v - } else { - return v - } - } - return false; - }); - commit('SET_ROUTERS', accessedRouters); - resolve(); + const { roles } = data + let accessedRouters + if (roles.indexOf('admin') >= 0) { + accessedRouters = asyncRouterMap + } else { + accessedRouters = filterAsyncRouter(asyncRouterMap, roles) + } + commit('SET_ROUTERS', accessedRouters) + resolve() }) } } -}; +} - -export default permission; +export default permission diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 384a1323..9c706978 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,15 +1,12 @@ -import { loginByEmail, logout, getInfo } from 'api/login'; -import Cookies from 'js-cookie'; +import { loginByUsername, logout, getUserInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' const user = { state: { user: '', status: '', - email: '', code: '', - uid: undefined, - auth_type: '', - token: Cookies.get('Admin-Token'), + token: getToken(), name: '', avatar: '', introduction: '', @@ -20,120 +17,117 @@ const user = { }, mutations: { - SET_AUTH_TYPE: (state, type) => { - state.auth_type = type; - }, SET_CODE: (state, code) => { - state.code = code; + state.code = code }, SET_TOKEN: (state, token) => { - state.token = token; - }, - SET_UID: (state, uid) => { - state.uid = uid; - }, - SET_EMAIL: (state, email) => { - state.email = email; + state.token = token }, SET_INTRODUCTION: (state, introduction) => { - state.introduction = introduction; + state.introduction = introduction }, SET_SETTING: (state, setting) => { - state.setting = setting; + state.setting = setting }, SET_STATUS: (state, status) => { - state.status = status; + state.status = status }, SET_NAME: (state, name) => { - state.name = name; + state.name = name }, SET_AVATAR: (state, avatar) => { - state.avatar = avatar; + state.avatar = avatar }, SET_ROLES: (state, roles) => { - state.roles = roles; - }, - LOGIN_SUCCESS: () => { - console.log('login success') - }, - LOGOUT_USER: state => { - state.user = ''; + state.roles = roles } }, actions: { - // 邮箱登录 - LoginByEmail({ commit }, userInfo) { - const email = userInfo.email.trim(); + // 用户名登录 + LoginByUsername({ commit }, userInfo) { + const username = userInfo.username.trim() return new Promise((resolve, reject) => { - loginByEmail(email, userInfo.password).then(response => { - const data = response.data; - Cookies.set('Admin-Token', response.data.token); - commit('SET_TOKEN', data.token); - commit('SET_EMAIL', email); - resolve(); + loginByUsername(username, userInfo.password).then(response => { + const data = response.data + setToken(response.data.token) + commit('SET_TOKEN', data.token) + resolve() }).catch(error => { - reject(error); - }); - }); + reject(error) + }) + }) }, - // 获取用户信息 - GetInfo({ commit, state }) { + GetUserInfo({ commit, state }) { return new Promise((resolve, reject) => { - getInfo(state.token).then(response => { - const data = response.data; - commit('SET_ROLES', data.role); - commit('SET_NAME', data.name); - commit('SET_AVATAR', data.avatar); - commit('SET_UID', data.uid); - commit('SET_INTRODUCTION', data.introduction); - resolve(response); + getUserInfo(state.token).then(response => { + const data = response.data + commit('SET_ROLES', data.role) + commit('SET_NAME', data.name) + commit('SET_AVATAR', data.avatar) + commit('SET_INTRODUCTION', data.introduction) + resolve(response) }).catch(error => { - reject(error); - }); - }); + reject(error) + }) + }) }, // 第三方验证登录 - LoginByThirdparty({ commit, state }, code) { - return new Promise((resolve, reject) => { - commit('SET_CODE', code); - loginByThirdparty(state.status, state.email, state.code, state.auth_type).then(response => { - commit('SET_TOKEN', response.data.token); - Cookies.set('Admin-Token', response.data.token); - resolve(); - }).catch(error => { - reject(error); - }); - }); - }, - + // LoginByThirdparty({ commit, state }, code) { + // return new Promise((resolve, reject) => { + // commit('SET_CODE', code) + // loginByThirdparty(state.status, state.email, state.code).then(response => { + // commit('SET_TOKEN', response.data.token) + // setToken(response.data.token) + // resolve() + // }).catch(error => { + // reject(error) + // }) + // }) + // }, // 登出 LogOut({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { - commit('SET_TOKEN', ''); - commit('SET_ROLES', []); - Cookies.remove('Admin-Token'); - resolve(); + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() + resolve() }).catch(error => { - reject(error); - }); - }); + reject(error) + }) + }) }, // 前端 登出 FedLogOut({ commit }) { return new Promise(resolve => { - commit('SET_TOKEN', ''); - Cookies.remove('Admin-Token'); - resolve(); - }); + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + }, + + // 动态修改权限 + ChangeRole({ commit }, role) { + return new Promise(resolve => { + commit('SET_TOKEN', role) + setToken(role) + getUserInfo(role).then(response => { + const data = response.data + commit('SET_ROLES', data.role) + commit('SET_NAME', data.name) + commit('SET_AVATAR', data.avatar) + commit('SET_INTRODUCTION', data.introduction) + resolve() + }) + }) } } -}; +} -export default user; +export default user diff --git a/src/styles/btn.scss b/src/styles/btn.scss index edd2f318..39b42dee 100644 --- a/src/styles/btn.scss +++ b/src/styles/btn.scss @@ -5,29 +5,27 @@ $pink: #E65D6E; $green: #30B08F; $tiffany: #4AB7BD; $yellow:#FEC171; - $panGreen: #30B08F; @mixin colorBtn($color) { background: $color; &:hover { color: $color; - &:before, &:after { + &:before, + &:after { background: $color; } } } - .blue-btn { @include colorBtn($blue) } -.light-blue-btn{ +.light-blue-btn { @include colorBtn($light-blue) } - .red-btn { @include colorBtn($red) } @@ -40,12 +38,10 @@ $panGreen: #30B08F; @include colorBtn($green) } - .tiffany-btn { @include colorBtn($tiffany) } - .yellow-btn { @include colorBtn($yellow) } @@ -63,12 +59,14 @@ $panGreen: #30B08F; display: inline-block; &:hover { background: #fff; - &:before, &:after { + &:before, + &:after { width: 100%; transition: 600ms ease all; } } - &:before, &:after { + &:before, + &:after { content: ''; position: absolute; top: 0; @@ -85,19 +83,20 @@ $panGreen: #30B08F; } } -.custom-button{ - display: inline-block; - line-height: 1; - white-space: nowrap; - cursor: pointer; - background: #fff; - color: #fff; - -webkit-appearance: none; - text-align: center; - box-sizing: border-box; - outline: 0; - margin: 0; - padding: 10px 15px; - font-size: 14px; - border-radius: 4px; +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; } + diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss index 382aff13..3b005d07 100644 --- a/src/styles/element-ui.scss +++ b/src/styles/element-ui.scss @@ -1,83 +1,82 @@ //覆盖一些element-ui样式 -.block-checkbox { - display: block; -} + .block-checkbox { + display: block; + } -.operation-container { - .cell { - padding: 10px !important; - } - .el-button { - &:nth-child(3) { - margin-top: 10px; - margin-left: 0px; - } - &:nth-child(4) { - margin-top: 10px; - } - } -} + .operation-container { + .cell { + padding: 10px !important; + } + .el-button { + &:nth-child(3) { + margin-top: 10px; + margin-left: 0px; + } + &:nth-child(4) { + margin-top: 10px; + } + } + } -.el-upload { - input[type="file"] { - display: none !important; - } -} + .el-upload { + input[type="file"] { + display: none !important; + } + } -.el-upload__input { - display: none; -} + .el-upload__input { + display: none; + } -.cell { - .el-tag { - margin-right: 8px; - } -} + .cell { + .el-tag { + margin-right: 8px; + } + } -.small-padding { - .cell { - padding-left: 8px; - padding-right: 8px; - } -} + .small-padding { + .cell { + padding-left: 8px; + padding-right: 8px; + } + } -.status-col { - .cell { - padding: 0 10px; - text-align: center; - .el-tag { - margin-right: 0px; - } - } -} + .status-col { + .cell { + padding: 0 10px; + text-align: center; + .el-tag { + margin-right: 0px; + } + } + } -//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 -.el-dialog { - transform: none; - left: 0; - position: relative; - margin: 0 auto; -} + //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 + .el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; + } + //文章页textarea修改样式 + .article-textarea { + textarea { + padding-right: 40px; + resize: none; + border: none; + border-radius: 0px; + border-bottom: 1px solid #bfcbd9; + } + } -//文章页textarea修改样式 -.article-textarea { - textarea { - padding-right: 40px; - resize: none; - border: none; - border-radius: 0px; - border-bottom: 1px solid #bfcbd9; - } -} - -//element ui upload -.upload-container { - .el-upload { - width: 100%; - .el-upload-dragger { - width: 100%; - height: 200px; - } - } -} + //element ui upload + .upload-container { + .el-upload { + width: 100%; + .el-upload-dragger { + width: 100%; + height: 200px; + } + } + } diff --git a/src/styles/index.scss b/src/styles/index.scss index e566c382..9090126f 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,6 +1,7 @@ +@import './mixin.scss'; @import './btn.scss'; @import './element-ui.scss'; -@import "./mixin.scss"; +@import './sidebar.scss'; body { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; @@ -100,20 +101,25 @@ code { .app-container { padding: 20px; } + .components-container { margin: 30px 50px; position: relative; } + .pagination-container { margin-top: 30px; } - .editor-container .CodeMirror { height: 100%!important; } -.wscn-icon { +.text-center { + text-align: center +} + +.svg-icon { width: 1em; height: 1em; vertical-align: -0.15em; @@ -211,7 +217,6 @@ code { } } - //refine vue-multiselect plugin .multiselect { line-height: 16px; @@ -222,8 +227,9 @@ code { } //refine simplemde -.simplemde-container{ - .editor-toolbar.fullscreen,.CodeMirror-fullscreen{ +.simplemde-container { + .editor-toolbar.fullscreen, + .CodeMirror-fullscreen { z-index: 1003; } } diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss new file mode 100644 index 00000000..a41a033e --- /dev/null +++ b/src/styles/sidebar.scss @@ -0,0 +1,77 @@ +// 侧边栏 +.sidebar-container>.el-menu { + width: 100%!important; + min-height: 100%; +} + +.sidebar-container .svg-icon { + margin-right: 16px; +} + +.hideSidebar .el-submenu>.el-submenu__title, +.hideSidebar .submenu-title-noDropdown { + padding-left: 10px!important; +} + +.hideSidebar .submenu-title-noDropdown span, +.hideSidebar .el-submenu>.el-submenu__title>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; +} + +.hideSidebar .nest-menu .el-submenu__title { + text-align: initial!important; + padding-left: 20px!important; + span { + height: auto; + width: auto; + visibility: visible; + display: inline; + } + .el-submenu__icon-arrow { + display: block!important; + } +} + +.hideSidebar .menu-wrapper>.el-menu-item, +.hideSidebar .submenu-title-noDropdown, +.hideSidebar .menu-wrapper>.el-submenu .el-submenu__title { + text-align: center; +} + +.hideSidebar .el-menu-item .el-submenu__icon-arrow, +.hideSidebar .el-submenu .el-submenu__title .el-submenu__icon-arrow { + display: none; +} + +.hideSidebar .submenu-title-noDropdown { + position: relative; + span { + transition: opacity .3s cubic-bezier(.55, 0, .1, 1); + opacity: 0; + } + &:hover { + span { + display: block; + border-radius: 3px; + z-index: 1002; + width: 140px; + height: 56px; + visibility: visible; + position: absolute; + right: -145px; + text-align: left; + text-indent: 20px; + top: 0px; + background-color: #1f2d3d; + opacity: 1; + } + } +} + +.el-submenu .el-menu-item { + min-width: 180px!important; +} diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 00000000..08a43d6e --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/src/utils/createUniqueString.js b/src/utils/createUniqueString.js index 2e6e357e..611725c4 100644 --- a/src/utils/createUniqueString.js +++ b/src/utils/createUniqueString.js @@ -2,7 +2,7 @@ * Created by jiachenpan on 17/3/8. */ export default function createUniqueString() { - const timestamp = +new Date() + ''; - const randomNum = parseInt((1 + Math.random()) * 65536) + ''; - return (+(randomNum + timestamp)).toString(32); + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) } diff --git a/src/utils/fetch.js b/src/utils/fetch.js index 8e589a8b..4e193863 100644 --- a/src/utils/fetch.js +++ b/src/utils/fetch.js @@ -1,59 +1,66 @@ -import axios from 'axios'; -import { Message } from 'element-ui'; -import store from '../store'; -// import router from '../router'; +import axios from 'axios' +import { Message } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' // 创建axios实例 const service = axios.create({ baseURL: process.env.BASE_API, // api的base_url timeout: 5000 // 请求超时时间 -}); +}) // request拦截器 service.interceptors.request.use(config => { // Do something before request is sent if (store.getters.token) { - config.headers['X-Token'] = store.getters.token; // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改 + config.headers['X-Token'] = getToken() // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改 } - return config; + return config }, error => { // Do something with request error - console.log(error); // for debug - Promise.reject(error); + console.log(error) // for debug + Promise.reject(error) }) // respone拦截器 service.interceptors.response.use( - response => response + response => response, /** * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页 * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中 */ - // const code = response.data.code; - // // 50014:Token 过期了 50012:其他客户端登录了 50008:非法的token - // if (code === 50008 || code === 50014 || code === 50012) { - // Message({ - // message: res.message, - // type: 'error', - // duration: 5 * 1000 - // }); - // // 登出 - // store.dispatch('FedLogOut').then(() => { - // router.push({ path: '/login' }) - // }); - // } else { - // return response - // } - , +// const res = response.data; +// if (res.code !== 20000) { +// Message({ +// message: res.message, +// type: 'error', +// duration: 5 * 1000 +// }); +// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; +// if (res.code === 50008 || res.code === 50012 || res.code === 50014) { +// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { +// confirmButtonText: '重新登录', +// cancelButtonText: '取消', +// type: 'warning' +// }).then(() => { +// store.dispatch('FedLogOut').then(() => { +// location.reload();// 为了重新实例化vue-router对象 避免bug +// }); +// }) +// } +// return Promise.reject('error'); +// } else { +// return response.data; +// } error => { - console.log('err' + error);// for debug + console.log('err' + error)// for debug Message({ message: error.message, type: 'error', duration: 5 * 1000 - }); - return Promise.reject(error); + }) + return Promise.reject(error) } ) -export default service; +export default service diff --git a/src/utils/index.js b/src/utils/index.js index a011f17e..7707a3e0 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -4,15 +4,15 @@ export function parseTime(time, cFormat) { if (arguments.length === 0) { - return null; + return null } - const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'; - let date; - if (typeof time == 'object') { - date = time; + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time } else { - if (('' + time).length === 10) time = parseInt(time) * 1000; - date = new Date(time); + if (('' + time).length === 10) time = parseInt(time) * 1000 + date = new Date(time) } const formatObj = { y: date.getFullYear(), @@ -22,24 +22,24 @@ i: date.getMinutes(), s: date.getSeconds(), a: date.getDay() - }; + } const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { - let value = formatObj[key]; - if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]; + let value = formatObj[key] + if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] if (result.length > 0 && value < 10) { - value = '0' + value; + value = '0' + value } - return value || 0; - }); - return time_str; + return value || 0 + }) + return time_str } export function formatTime(time, option) { - time = +time * 1000; - const d = new Date(time); - const now = Date.now(); + time = +time * 1000 + const d = new Date(time) + const now = Date.now() - const diff = (now - d) / 1000; + const diff = (now - d) / 1000 if (diff < 30) { return '刚刚' @@ -59,64 +59,66 @@ // 格式化时间 export function getQueryObject(url) { - url = url == null ? window.location.href : url; - const search = url.substring(url.lastIndexOf('?') + 1); - const obj = {}; - const reg = /([^?&=]+)=([^?&=]*)/g; + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g search.replace(reg, (rs, $1, $2) => { - const name = decodeURIComponent($1); - let val = decodeURIComponent($2); - val = String(val); - obj[name] = val; - return rs; - }); - return obj; + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj } - /** *get getByteLen * @param {Sting} val input value * @returns {number} output value */ export function getByteLen(val) { - let len = 0; + let len = 0 for (let i = 0; i < val.length; i++) { if (val[i].match(/[^\x00-\xff]/ig) != null) { - len += 1; - } else { len += 0.5; } + len += 1 + } else { len += 0.5 } } - return Math.floor(len); + return Math.floor(len) } export function cleanArray(actual) { - const newArray = []; + const newArray = [] for (let i = 0; i < actual.length; i++) { if (actual[i]) { - newArray.push(actual[i]); + newArray.push(actual[i]) } } - return newArray; + return newArray } export function param(json) { - if (!json) return ''; + if (!json) return '' return cleanArray(Object.keys(json).map(key => { - if (json[key] === undefined) return ''; + if (json[key] === undefined) return '' return encodeURIComponent(key) + '=' + - encodeURIComponent(json[key]); - })).join('&'); + encodeURIComponent(json[key]) + })).join('&') } export function param2Obj(url) { - const search = url.split('?')[1]; + const search = url.split('?')[1] + if (!search) { + return {} + } return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}') } export function html2Text(val) { - const div = document.createElement('div'); - div.innerHTML = val; - return div.textContent || div.innerText; + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText } export function objectMerge(target, source) { @@ -124,83 +126,82 @@ giving the last one precedence */ if (typeof target !== 'object') { - target = {}; + target = {} } if (Array.isArray(source)) { - return source.slice(); + return source.slice() } for (const property in source) { if (source.hasOwnProperty(property)) { - const sourceProperty = source[property]; + const sourceProperty = source[property] if (typeof sourceProperty === 'object') { - target[property] = objectMerge(target[property], sourceProperty); - continue; + target[property] = objectMerge(target[property], sourceProperty) + continue } - target[property] = sourceProperty; + target[property] = sourceProperty } } - return target; + return target } - export function scrollTo(element, to, duration) { - if (duration <= 0) return; - const difference = to - element.scrollTop; - const perTick = difference / duration * 10; + if (duration <= 0) return + const difference = to - element.scrollTop + const perTick = difference / duration * 10 setTimeout(() => { console.log(new Date()) - element.scrollTop = element.scrollTop + perTick; - if (element.scrollTop === to) return; - scrollTo(element, to, duration - 10); - }, 10); + element.scrollTop = element.scrollTop + perTick + if (element.scrollTop === to) return + scrollTo(element, to, duration - 10) + }, 10) } export function toggleClass(element, className) { if (!element || !className) { - return; + return } - let classString = element.className; - const nameIndex = classString.indexOf(className); + let classString = element.className + const nameIndex = classString.indexOf(className) if (nameIndex === -1) { - classString += '' + className; + classString += '' + className } else { - classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length); + classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length) } - element.className = classString; + element.className = classString } export const pickerOptions = [ { text: '今天', onClick(picker) { - const end = new Date(); - const start = new Date(new Date().toDateString()); - end.setTime(start.getTime()); - picker.$emit('pick', [start, end]); + const end = new Date() + const start = new Date(new Date().toDateString()) + end.setTime(start.getTime()) + picker.$emit('pick', [start, end]) } }, { text: '最近一周', onClick(picker) { - const end = new Date(new Date().toDateString()); - const start = new Date(); - start.setTime(end.getTime() - 3600 * 1000 * 24 * 7); - picker.$emit('pick', [start, end]); + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(end.getTime() - 3600 * 1000 * 24 * 7) + picker.$emit('pick', [start, end]) } }, { text: '最近一个月', onClick(picker) { - const end = new Date(new Date().toDateString()); - const start = new Date(); - start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); - picker.$emit('pick', [start, end]); + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) + picker.$emit('pick', [start, end]) } }, { text: '最近三个月', onClick(picker) { - const end = new Date(new Date().toDateString()); - const start = new Date(); - start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); - picker.$emit('pick', [start, end]); + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90) + picker.$emit('pick', [start, end]) } }] @@ -212,3 +213,55 @@ } } + export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔last小于设定时间间隔wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } + } + + export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'shallowClone') + } + const targetObj = source.constructor === Array ? [] : {} + for (const keys in source) { + if (source.hasOwnProperty(keys)) { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = source[keys].constructor === Array ? [] : {} + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + } + } + return targetObj + } diff --git a/src/utils/openWindow.js b/src/utils/openWindow.js index a7e2b91c..2c542913 100644 --- a/src/utils/openWindow.js +++ b/src/utils/openWindow.js @@ -8,20 +8,19 @@ export default function openWindow(url, title, w, h) { // Fixes dual-screen position Most browsers Firefox - const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; - const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top - const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; - const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height - const left = ((width / 2) - (w / 2)) + dualScreenLeft; - const top = ((height / 2) - (h / 2)) + dualScreenTop; - const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left); + const left = ((width / 2) - (w / 2)) + dualScreenLeft + const top = ((height / 2) - (h / 2)) + dualScreenTop + const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) // Puts focus on the newWindow if (window.focus) { - newWindow.focus(); + newWindow.focus() } } - diff --git a/src/utils/validate.js b/src/utils/validate.js index 3dbc5fec..834a8dd9 100644 --- a/src/utils/validate.js +++ b/src/utils/validate.js @@ -2,40 +2,32 @@ * Created by jiachenpan on 16/11/18. */ -/* 是否是公司邮箱*/ -export function isWscnEmail(str) { - const reg = /^[a-z0-9](?:[-_.+]?[a-z0-9]+)*@wallstreetcn\.com$/i; - return reg.test(str.trim()); +export function isvalidUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 } /* 合法uri*/ export function validateURL(textval) { - const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; - return urlregex.test(textval); + const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return urlregex.test(textval) } /* 小写字母*/ export function validateLowerCase(str) { - const reg = /^[a-z]+$/; - return reg.test(str); + const reg = /^[a-z]+$/ + return reg.test(str) } -/* 验证key*/ -// export function validateKey(str) { -// var reg = /^[a-z_\-:]+$/; -// return reg.test(str); -// } - /* 大写字母*/ export function validateUpperCase(str) { - const reg = /^[A-Z]+$/; - return reg.test(str); + const reg = /^[A-Z]+$/ + return reg.test(str) } /* 大小写字母*/ export function validatAlphabets(str) { - const reg = /^[A-Za-z]+$/; - return reg.test(str); + const reg = /^[A-Za-z]+$/ + return reg.test(str) } - diff --git a/src/views/charts/index.vue b/src/views/charts/index.vue index cce1dc39..d824b3cc 100644 --- a/src/views/charts/index.vue +++ b/src/views/charts/index.vue @@ -1,7 +1,7 @@ diff --git a/src/views/charts/keyboard.vue b/src/views/charts/keyboard.vue index f0d066c3..bfc4db82 100644 --- a/src/views/charts/keyboard.vue +++ b/src/views/charts/keyboard.vue @@ -1,24 +1,24 @@ - diff --git a/src/views/charts/keyboard2.vue b/src/views/charts/keyboard2.vue index d46c3589..b7b53c20 100644 --- a/src/views/charts/keyboard2.vue +++ b/src/views/charts/keyboard2.vue @@ -1,24 +1,24 @@ - diff --git a/src/views/charts/line.vue b/src/views/charts/line.vue index 5992c502..63b73a39 100644 --- a/src/views/charts/line.vue +++ b/src/views/charts/line.vue @@ -1,26 +1,24 @@ - diff --git a/src/views/charts/mixChart.vue b/src/views/charts/mixChart.vue new file mode 100644 index 00000000..647c1ec6 --- /dev/null +++ b/src/views/charts/mixChart.vue @@ -0,0 +1,25 @@ + + + + + + diff --git a/src/views/charts/mixchart.vue b/src/views/charts/mixchart.vue deleted file mode 100644 index 8b2afd02..00000000 --- a/src/views/charts/mixchart.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/src/views/components/avatarUpload.vue b/src/views/components/avatarUpload.vue index 93d99a33..c21bdc15 100644 --- a/src/views/components/avatarUpload.vue +++ b/src/views/components/avatarUpload.vue @@ -1,43 +1,49 @@ + - diff --git a/src/views/components/backToTop.vue b/src/views/components/backToTop.vue new file mode 100644 index 00000000..9407ca13 --- /dev/null +++ b/src/views/components/backToTop.vue @@ -0,0 +1,156 @@ + + + diff --git a/src/views/components/countTo.vue b/src/views/components/countTo.vue index a2c920ec..c6a66bd3 100644 --- a/src/views/components/countTo.vue +++ b/src/views/components/countTo.vue @@ -1,9 +1,9 @@ - + diff --git a/src/views/components/jsoneditor.vue b/src/views/components/jsoneditor.vue deleted file mode 100644 index ca8142a5..00000000 --- a/src/views/components/jsoneditor.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - - - diff --git a/src/views/components/markdown.vue b/src/views/components/markdown.vue index 2a73dd58..c50752cb 100644 --- a/src/views/components/markdown.vue +++ b/src/views/components/markdown.vue @@ -1,22 +1,34 @@ - diff --git a/src/views/components/mixin.vue b/src/views/components/mixin.vue index f8d8d5e0..f78ab1e0 100644 --- a/src/views/components/mixin.vue +++ b/src/views/components/mixin.vue @@ -1,16 +1,16 @@ - diff --git a/src/views/components/sticky.vue b/src/views/components/sticky.vue index 6e29d19f..f6cf3daa 100644 --- a/src/views/components/sticky.vue +++ b/src/views/components/sticky.vue @@ -1,6 +1,6 @@ - + + diff --git a/src/views/dashboard/admin/barChart.vue b/src/views/dashboard/admin/barChart.vue new file mode 100644 index 00000000..a09bd8d9 --- /dev/null +++ b/src/views/dashboard/admin/barChart.vue @@ -0,0 +1,93 @@ + + + diff --git a/src/views/dashboard/admin/index.vue b/src/views/dashboard/admin/index.vue new file mode 100644 index 00000000..9517c2a4 --- /dev/null +++ b/src/views/dashboard/admin/index.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/views/dashboard/admin/lineChart.vue b/src/views/dashboard/admin/lineChart.vue new file mode 100644 index 00000000..59aa61e6 --- /dev/null +++ b/src/views/dashboard/admin/lineChart.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/views/dashboard/admin/pieChart.vue b/src/views/dashboard/admin/pieChart.vue new file mode 100644 index 00000000..6384e1de --- /dev/null +++ b/src/views/dashboard/admin/pieChart.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/views/dashboard/default/index.vue b/src/views/dashboard/default/index.vue deleted file mode 100644 index 9a16481f..00000000 --- a/src/views/dashboard/default/index.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - - - diff --git a/src/views/dashboard/editor/articlesChart.vue b/src/views/dashboard/editor/articlesChart.vue deleted file mode 100644 index a62e1962..00000000 --- a/src/views/dashboard/editor/articlesChart.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/src/views/dashboard/editor/index.vue b/src/views/dashboard/editor/index.vue index 74958c66..07f30ee6 100644 --- a/src/views/dashboard/editor/index.vue +++ b/src/views/dashboard/editor/index.vue @@ -1,295 +1,74 @@ diff --git a/src/views/dashboard/editor/monthKpi.vue b/src/views/dashboard/editor/monthKpi.vue deleted file mode 100644 index 7fdd7501..00000000 --- a/src/views/dashboard/editor/monthKpi.vue +++ /dev/null @@ -1,61 +0,0 @@ - - - - diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue index 2e4ce06f..0fd01f6f 100644 --- a/src/views/dashboard/index.vue +++ b/src/views/dashboard/index.vue @@ -1,39 +1,32 @@ diff --git a/src/views/errlog/index.vue b/src/views/errlog/index.vue index bc0be8fc..f906e9a0 100644 --- a/src/views/errlog/index.vue +++ b/src/views/errlog/index.vue @@ -1,35 +1,25 @@ - - diff --git a/src/views/error/401.vue b/src/views/error/401.vue deleted file mode 100644 index f19ed1ff..00000000 --- a/src/views/error/401.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - - diff --git a/src/views/error/404.vue b/src/views/error/404.vue deleted file mode 100644 index 14422095..00000000 --- a/src/views/error/404.vue +++ /dev/null @@ -1,217 +0,0 @@ - - - diff --git a/src/views/errorPage/401.vue b/src/views/errorPage/401.vue new file mode 100644 index 00000000..888e9df6 --- /dev/null +++ b/src/views/errorPage/401.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/src/views/errorPage/404.vue b/src/views/errorPage/404.vue new file mode 100644 index 00000000..a8ab241d --- /dev/null +++ b/src/views/errorPage/404.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/src/views/example/form1.vue b/src/views/example/form.vue similarity index 60% rename from src/views/example/form1.vue rename to src/views/example/form.vue index ff982ec9..9a09c846 100644 --- a/src/views/example/form1.vue +++ b/src/views/example/form.vue @@ -2,11 +2,14 @@
- + - +
@@ -104,7 +107,7 @@
- +
@@ -117,136 +120,146 @@ + diff --git a/src/views/example/table/dragTable.vue b/src/views/example/table/dragTable.vue index 512c8a25..864e2c7b 100644 --- a/src/views/example/table/dragTable.vue +++ b/src/views/example/table/dragTable.vue @@ -29,7 +29,7 @@ @@ -47,80 +47,79 @@ -
默认顺序   {{ olderList}}
+
默认顺序   {{ olderList}}
拖拽后顺序{{newList}}
- diff --git a/src/views/layout/AppMain.vue b/src/views/layout/AppMain.vue index e6867d8f..a6de1e6f 100644 --- a/src/views/layout/AppMain.vue +++ b/src/views/layout/AppMain.vue @@ -1,20 +1,18 @@ diff --git a/src/views/layout/Layout.vue b/src/views/layout/Layout.vue index 693456ca..f31a5b8c 100644 --- a/src/views/layout/Layout.vue +++ b/src/views/layout/Layout.vue @@ -1,75 +1,63 @@ - diff --git a/src/views/layout/Levelbar.vue b/src/views/layout/Levelbar.vue index 382d9fd1..e162a663 100644 --- a/src/views/layout/Levelbar.vue +++ b/src/views/layout/Levelbar.vue @@ -1,48 +1,49 @@ + diff --git a/src/views/layout/Navbar.vue b/src/views/layout/Navbar.vue index 9ac58aa2..8cdf1f29 100644 --- a/src/views/layout/Navbar.vue +++ b/src/views/layout/Navbar.vue @@ -1,106 +1,119 @@ + diff --git a/src/views/layout/Sidebar.vue b/src/views/layout/Sidebar.vue index d06f72f9..3c72ebee 100644 --- a/src/views/layout/Sidebar.vue +++ b/src/views/layout/Sidebar.vue @@ -1,23 +1,23 @@ + - diff --git a/src/views/layout/SidebarItem.vue b/src/views/layout/SidebarItem.vue index 2d9b85cf..a1eb781e 100644 --- a/src/views/layout/SidebarItem.vue +++ b/src/views/layout/SidebarItem.vue @@ -1,46 +1,43 @@