commit 821de421145039afce84fca9b5d108041be50540 Author: Pan Date: Fri Jul 14 16:08:15 2017 +0800 init diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..8092f37 --- /dev/null +++ b/.babelrc @@ -0,0 +1,15 @@ +{ + "comments": false, + "env": { + "main": { + "presets": ["es2015", "stage-0"] + }, + "renderer": { + "presets": [ + ["es2015", { "modules": false }], + "stage-0" + ] + } + }, + "plugins": ["transform-runtime"] +} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..9c8c332 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +app/node_modules/** +app/dist/** +build/*.js +config/*.js +app/src/renderer/assets diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..1944396 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,310 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + node: true + }, + extends: 'eslint:recommended', + // required to lint *.vue files + plugins: [ + 'html' + ], + // add your custom rules here + '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', + + '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' + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e04246c --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +app/dist/index.html +app/dist/main.js +app/dist/renderer.js +app/dist/styles.css +builds/* +dist/* +node_modules/ +npm-debug.log +npm-debug.log.* +thumbs.db +!.gitkeep diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc894ab --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# electron-vue-admin + +> An electron-vue project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:9080 +npm run dev + +# build electron app for production +npm run build + +# lint all JS/Vue component files in `app/src` +npm run lint + +# run webpack in production +npm run pack +``` +More information can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/en/npm_scripts.html). + +--- + +This project was generated from [electron-vue](https://github.com/SimulatedGREG/electron-vue) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about this project can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html). diff --git a/app/dist/.gitkeep b/app/dist/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/dist/0.js b/app/dist/0.js new file mode 100644 index 0000000..29f7000 --- /dev/null +++ b/app/dist/0.js @@ -0,0 +1 @@ +webpackJsonp([0],{139:function(t,i,a){a(161);var n=a(2)(a(146),a(156),"data-v-0d269b18",null);t.exports=n.exports},146:function(t,i,a){"use strict";Object.defineProperty(i,"__esModule",{value:!0});var n=a(154),o=a.n(n),s=a(155),e=a.n(s);i.default={data:function(){return{img_404:o.a,img_404_cloud:e.a}},computed:{message:function(){return"特朗普说这个页面你不能进......"}}}},151:function(t,i,a){i=t.exports=a(3)(),i.push([t.i,".wscn-http404[data-v-0d269b18]{position:relative;width:1200px;margin:20px auto 60px;padding:0 100px;overflow:hidden}.wscn-http404 .pic-404[data-v-0d269b18]{position:relative;float:left;width:600px;padding:150px 0;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-0d269b18]{width:100%}.wscn-http404 .pic-404__child[data-v-0d269b18]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-0d269b18]{width:80px;top:17px;left:220px;opacity:0;animation-name:cloudLeft;animation-duration:2s;animation-timing-function:linear;animation-fill-mode:forwards;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-0d269b18]{width:46px;top:10px;left:420px;opacity:0;animation-name:cloudMid;animation-duration:2s;animation-timing-function:linear;animation-fill-mode:forwards;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-0d269b18]{width:62px;top:100px;left:500px;opacity:0;animation-name:cloudRight;animation-duration:2s;animation-timing-function:linear;animation-fill-mode:forwards;animation-delay:1s}@keyframes cloudLeft{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudMid{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudRight{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-0d269b18]{position:relative;float:left;width:300px;padding:150px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-0d269b18]{font-size:32px;font-weight:700;line-height:40px;color:#1482f0;opacity:0;margin-bottom:20px;animation-name:slideUp;animation-duration:.5s;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-0d269b18]{font-size:20px;line-height:24px;color:#1482f0;opacity:0;margin-bottom:10px;animation-name:slideUp;animation-duration:.5s;animation-delay:.1s;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-0d269b18]{font-size:13px;line-height:21px;color:grey;opacity:0;margin-bottom:30px;animation-name:slideUp;animation-duration:.5s;animation-delay:.2s;animation-fill-mode:forwards}.wscn-http404 .bullshit__return-home[data-v-0d269b18]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;opacity:0;font-size:14px;line-height:36px;cursor:pointer;animation-name:slideUp;animation-duration:.5s;animation-delay:.3s;animation-fill-mode:forwards}@keyframes slideUp{0%{transform:translateY(60px);opacity:0}to{transform:translateY(0);opacity:1}}",""])},154:function(t,i,a){t.exports=a.p+"imgs/404.png"},155:function(t,i){t.exports="data:image/png;base64,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"},156:function(t,i){t.exports={render:function(){var t=this,i=t.$createElement,a=t._self._c||i;return a("div",{staticStyle:{background:"#f0f2f5","margin-top":"-20px"}},[a("div",{staticClass:"wscn-http404"},[a("div",{staticClass:"pic-404"},[a("img",{staticClass:"pic-404__parent",attrs:{src:t.img_404,alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child left",attrs:{src:t.img_404_cloud,alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child mid",attrs:{src:t.img_404_cloud,alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child right",attrs:{src:t.img_404_cloud,alt:"404"}})]),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(0),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:"/"}},[t._v("返回首页")])])])])},staticRenderFns:[function(){var t=this,i=t.$createElement,a=t._self._c||i;return a("div",{staticClass:"bullshit__info"},[t._v("版权所有"),a("a",{staticClass:"link-type",attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("华尔街见闻")])])}]}},161:function(t,i,a){var n=a(151);"string"==typeof n&&(n=[[t.i,n,""]]),a(4)(n,{}),n.locals&&(t.exports=n.locals)}}); \ No newline at end of file diff --git a/app/dist/1.js b/app/dist/1.js new file mode 100644 index 0000000..8043823 --- /dev/null +++ b/app/dist/1.js @@ -0,0 +1 @@ +webpackJsonp([1],{141:function(t,o,n){n(162);var i=n(2)(n(148),n(158),null,null);t.exports=i.exports},145:function(t,o,n){"use strict";function i(t){return/^[a-z0-9](?:[-_.+]?[a-z0-9]+)*@wallstreetcn\.com$/i.test(t.trim())}o.a=i},148:function(t,o,n){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var i=n(145);o.default={name:"login",data:function(){return{loginForm:{email:"admin@wallstreetcn.com",password:"111111"},loginRules:{email:[{required:!0,trigger:"blur",validator:function(t,o,e){n.i(i.a)(o)?e():e(new Error("请输入正确的合法邮箱"))}}],password:[{required:!0,trigger:"blur",validator:function(t,o,n){o.length<6?n(new Error("密码不能小于6位")):n()}}]},loading:!1}},methods:{handleLogin:function(){var t=this;this.$refs.loginForm.validate(function(o){if(!o)return console.log("error submit!!"),!1;t.loading=!0,t.$store.dispatch("Login",t.loginForm).then(function(){t.loading=!1,t.$router.push({path:"/"})}).catch(function(){t.loading=!1})})}}}},152:function(t,o,n){o=t.exports=n(3)(),o.push([t.i,".tips{font-size:14px;color:#fff;margin-bottom:5px}.login-container{position:relative;width:100%;height:100%;height:100vh;background-color:#2d3a4b}.login-container input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #293444 inset!important;-webkit-text-fill-color:#fff!important}.login-container input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#eee;height:47px}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .svg-container{padding:6px 5px 6px 15px;color:#889aa4}.login-container .title{font-size:26px;font-weight:400;color:#eee;margin:0 auto 40px;text-align:center;font-weight:700}.login-container .login-form{position:absolute;left:0;right:0;width:400px;padding:35px 35px 15px;margin:120px auto}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container .forget-pwd{color:#fff}",""])},158:function(t,o){t.exports={render:function(){var t=this,o=t.$createElement,n=t._self._c||o;return n("div",{staticClass:"login-container"},[n("el-form",{ref:"loginForm",staticClass:"card-box login-form",attrs:{autoComplete:"on",model:t.loginForm,rules:t.loginRules,"label-position":"left","label-width":"0px"}},[n("h3",{staticClass:"title"},[t._v("系统登录")]),t._v(" "),n("el-form-item",{attrs:{prop:"email"}},[n("span",{staticClass:"svg-container"},[n("icon-svg",{attrs:{"icon-class":"jiedianyoujian"}})],1),t._v(" "),n("el-input",{attrs:{name:"email",type:"text",autoComplete:"on",placeholder:"邮箱"},model:{value:t.loginForm.email,callback:function(o){t.loginForm.email=o},expression:"loginForm.email"}})],1),t._v(" "),n("el-form-item",{attrs:{prop:"password"}},[n("span",{staticClass:"svg-container"},[n("icon-svg",{attrs:{"icon-class":"mima"}})],1),t._v(" "),n("el-input",{attrs:{name:"password",type:"password",autoComplete:"on",placeholder:"密码"},nativeOn:{keyup:function(o){if(!("button"in o)&&t._k(o.keyCode,"enter",13))return null;t.handleLogin(o)}},model:{value:t.loginForm.password,callback:function(o){t.loginForm.password=o},expression:"loginForm.password"}})],1),t._v(" "),n("el-form-item",[n("el-button",{staticStyle:{width:"100%"},attrs:{type:"primary",loading:t.loading},nativeOn:{click:function(o){o.preventDefault(),t.handleLogin(o)}}},[t._v("\n 登录\n ")])],1),t._v(" "),n("div",{staticClass:"tips"},[t._v("admin账号为:admin@wallstreetcn.com 密码随便填")]),t._v(" "),n("div",{staticClass:"tips"},[t._v("editor账号:editor@wallstreetcn.com 密码随便填")])],1)],1)},staticRenderFns:[]}},162:function(t,o,n){var i=n(152);"string"==typeof i&&(i=[[t.i,i,""]]),n(4)(i,{}),i.locals&&(t.exports=i.locals)}}); \ No newline at end of file diff --git a/app/dist/2.js b/app/dist/2.js new file mode 100644 index 0000000..ad854c4 --- /dev/null +++ b/app/dist/2.js @@ -0,0 +1 @@ +webpackJsonp([2],{140:function(e,t,n){n(163);var s=n(2)(n(147),n(160),null,null);e.exports=s.exports},147:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=n(10),a=n.n(s),r=n(17);n.n(r),t.default={name:"dashboard",computed:a()({},n.i(r.mapGetters)(["name","roles"]))}},153:function(e,t,n){t=e.exports=n(3)(),t.push([e.i,".dashboard-container{margin:30px}.dashboard-text{font-size:30px;line-height:46px}",""])},160:function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"dashboard-container"},[n("div",{staticClass:"dashboard-text"},[e._v("name:"+e._s(e.name))]),e._v(" "),n("div",{staticClass:"dashboard-text"},[e._v("role:"),e._l(e.roles,function(t){return n("span",{key:t},[e._v(e._s(t))])})],2)])},staticRenderFns:[]}},163:function(e,t,n){var s=n(153);"string"==typeof s&&(s=[[e.i,s,""]]),n(4)(s,{}),s.locals&&(e.exports=s.locals)}}); \ No newline at end of file diff --git a/app/dist/3.js b/app/dist/3.js new file mode 100644 index 0000000..4be6402 --- /dev/null +++ b/app/dist/3.js @@ -0,0 +1 @@ +webpackJsonp([3],{143:function(t,e,n){var a=n(2)(n(150),n(157),null,null);t.exports=a.exports},144:function(t,e,n){"use strict";function a(t){return n.i(i.a)({url:"/table/list",method:"get",params:t})}e.a=a;var i=n(56)},150:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=n(144);e.default={data:function(){return{list:null,listLoading:!0}},created:function(){this.fetchData()},methods:{fetchData:function(){var t=this;this.listLoading=!0,n.i(a.a)(this.listQuery).then(function(e){t.list=e.data.items,t.listLoading=!1})}}}},157:function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-table",{directives:[{name:"loading",rawName:"v-loading.body",value:t.listLoading,expression:"listLoading",modifiers:{body:!0}}],attrs:{data:t.list,"element-loading-text":"拼命加载中",border:"",fit:"","highlight-current-row":""}},[n("el-table-column",{attrs:{align:"center",label:"ID",width:"95"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.$index)+"\n ")]}}])}),t._v(" "),n("el-table-column",{attrs:{label:"Title"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.title)+"\n ")]}}])}),t._v(" "),n("el-table-column",{attrs:{label:"Author",width:"110",align:"center"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",[t._v(t._s(e.row.author))])]}}])}),t._v(" "),n("el-table-column",{attrs:{label:"Pageviews",width:"110",align:"center"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.pageviews)+"\n ")]}}])}),t._v(" "),n("el-table-column",{attrs:{align:"center",prop:"created_at",label:"Display_time",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("i",{staticClass:"el-icon-time"}),t._v(" "),n("span",[t._v(t._s(e.row.display_time))])]}}])})],1)],1)},staticRenderFns:[]}}}); \ No newline at end of file diff --git a/app/dist/4.js b/app/dist/4.js new file mode 100644 index 0000000..8b1f511 --- /dev/null +++ b/app/dist/4.js @@ -0,0 +1 @@ +webpackJsonp([4],{142:function(e,t,l){var a=l(2)(l(149),l(159),null,null);e.exports=a.exports},149:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={data:function(){return{form:{name:"",region:"",date1:"",date2:"",delivery:!1,type:[],resource:"",desc:""}}},methods:{onSubmit:function(){console.log("submit!")}}}},159:function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,l=e._self._c||t;return l("div",{staticClass:"app-container"},[l("el-form",{ref:"form",attrs:{model:e.form,"label-width":"80px"}},[l("el-form-item",{attrs:{label:"活动名称"}},[l("el-input",{model:{value:e.form.name,callback:function(t){e.form.name=t},expression:"form.name"}})],1),e._v(" "),l("el-form-item",{attrs:{label:"活动区域"}},[l("el-select",{attrs:{placeholder:"请选择活动区域"},model:{value:e.form.region,callback:function(t){e.form.region=t},expression:"form.region"}},[l("el-option",{attrs:{label:"区域一",value:"shanghai"}}),e._v(" "),l("el-option",{attrs:{label:"区域二",value:"beijing"}})],1)],1),e._v(" "),l("el-form-item",{attrs:{label:"活动时间"}},[l("el-col",{attrs:{span:11}},[l("el-date-picker",{staticStyle:{width:"100%"},attrs:{type:"date",placeholder:"选择日期"},model:{value:e.form.date1,callback:function(t){e.form.date1=t},expression:"form.date1"}})],1),e._v(" "),l("el-col",{staticClass:"line",attrs:{span:2}},[e._v("-")]),e._v(" "),l("el-col",{attrs:{span:11}},[l("el-time-picker",{staticStyle:{width:"100%"},attrs:{type:"fixed-time",placeholder:"选择时间"},model:{value:e.form.date2,callback:function(t){e.form.date2=t},expression:"form.date2"}})],1)],1),e._v(" "),l("el-form-item",{attrs:{label:"即时配送"}},[l("el-switch",{attrs:{"on-text":"","off-text":""},model:{value:e.form.delivery,callback:function(t){e.form.delivery=t},expression:"form.delivery"}})],1),e._v(" "),l("el-form-item",{attrs:{label:"活动性质"}},[l("el-checkbox-group",{model:{value:e.form.type,callback:function(t){e.form.type=t},expression:"form.type"}},[l("el-checkbox",{attrs:{label:"美食/餐厅线上活动",name:"type"}}),e._v(" "),l("el-checkbox",{attrs:{label:"地推活动",name:"type"}}),e._v(" "),l("el-checkbox",{attrs:{label:"线下主题活动",name:"type"}}),e._v(" "),l("el-checkbox",{attrs:{label:"单纯品牌曝光",name:"type"}})],1)],1),e._v(" "),l("el-form-item",{attrs:{label:"特殊资源"}},[l("el-radio-group",{model:{value:e.form.resource,callback:function(t){e.form.resource=t},expression:"form.resource"}},[l("el-radio",{attrs:{label:"线上品牌商赞助"}}),e._v(" "),l("el-radio",{attrs:{label:"线下场地免费"}})],1)],1),e._v(" "),l("el-form-item",{attrs:{label:"活动形式"}},[l("el-input",{attrs:{type:"textarea"},model:{value:e.form.desc,callback:function(t){e.form.desc=t},expression:"form.desc"}})],1),e._v(" "),l("el-form-item",[l("el-button",{attrs:{type:"primary"},on:{click:e.onSubmit}},[e._v("立即创建")]),e._v(" "),l("el-button",[e._v("取消")])],1)],1)],1)},staticRenderFns:[]}}}); \ No newline at end of file diff --git a/app/dist/fonts/element-icons.ttf b/app/dist/fonts/element-icons.ttf new file mode 100644 index 0000000..9c1b720 Binary files /dev/null and b/app/dist/fonts/element-icons.ttf differ diff --git a/app/dist/imgs/404.png b/app/dist/imgs/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/app/dist/imgs/404.png differ diff --git a/app/dist/imgs/logo.png b/app/dist/imgs/logo.png new file mode 100644 index 0000000..63736e2 Binary files /dev/null and b/app/dist/imgs/logo.png differ diff --git a/app/icons/icon.icns b/app/icons/icon.icns new file mode 100644 index 0000000..a65f91b Binary files /dev/null and b/app/icons/icon.icns differ diff --git a/app/icons/icon.ico b/app/icons/icon.ico new file mode 100644 index 0000000..8702729 Binary files /dev/null and b/app/icons/icon.ico differ diff --git a/app/index.ejs b/app/index.ejs new file mode 100644 index 0000000..b734e56 --- /dev/null +++ b/app/index.ejs @@ -0,0 +1,17 @@ + + + + + electron-vue-admin + <% if (htmlWebpackPlugin.options.appModules) { %> + + + <% } %> + + +
+ + + diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..d4dceb3 --- /dev/null +++ b/app/package.json @@ -0,0 +1,18 @@ +{ + "name": "electron-vue-admin", + "version": "0.0.0", + "description": "An electron-vue project", + "main": "./dist/main.js", + "dependencies": { + "axios": "^0.16.2", + "babel-runtime": "6.23.0", + "element-ui": "1.3.4", + "normalize.css": "3.0.2", + "vue": "2.3.3", + "vue-electron": "1.0.6", + "vue-router": "2.5.3", + "vuex": "2.3.1" + }, + "devDependencies": {}, + "author": "Pan " +} diff --git a/app/src/main/index.dev.js b/app/src/main/index.dev.js new file mode 100644 index 0000000..5947d90 --- /dev/null +++ b/app/src/main/index.dev.js @@ -0,0 +1,32 @@ +/** + * This file is used specifically and only for development. It enables the use of ES6+ + * features for the main process and installs `electron-debug` & `vue-devtools`. There + * shouldn't be any need to modify this file, but it can be used to extend your + * development environment. + */ + +/* eslint-disable no-console */ + +// Set babel `env` and install `babel-register` +process.env.NODE_ENV = 'development' +process.env.BABEL_ENV = 'main' + +require('babel-register')({ + ignore: /node_modules/ +}) + +// Install `electron-debug` with `devtron` +require('electron-debug')({ showDevTools: true }) + +// Install `vue-devtools` +require('electron').app.on('ready', () => { + let installExtension = require('electron-devtools-installer') + installExtension.default(installExtension.VUEJS_DEVTOOLS) + .then(() => {}) + .catch(err => { + console.log('Unable to install `vue-devtools`: \n', err) + }) +}) + +// Require `main` process to boot app +require('./index') diff --git a/app/src/main/index.js b/app/src/main/index.js new file mode 100644 index 0000000..37ad903 --- /dev/null +++ b/app/src/main/index.js @@ -0,0 +1,41 @@ +'use strict' + +import { app, BrowserWindow } from 'electron' + +let mainWindow +const winURL = process.env.NODE_ENV === 'development' + ? `http://localhost:${require('../../../config').port}` + : `file://${__dirname}/index.html` + +function createWindow () { + /** + * Initial window options + */ + mainWindow = new BrowserWindow({ + height: 600, + width: 800 + }) + + mainWindow.loadURL(winURL) + + mainWindow.on('closed', () => { + mainWindow = null + }) + + // eslint-disable-next-line no-console + console.log('mainWindow opened') +} + +app.on('ready', createWindow) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (mainWindow === null) { + createWindow() + } +}) diff --git a/app/src/renderer/App.vue b/app/src/renderer/App.vue new file mode 100644 index 0000000..ea03301 --- /dev/null +++ b/app/src/renderer/App.vue @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/renderer/api/login.js b/app/src/renderer/api/login.js new file mode 100644 index 0000000..1b14881 --- /dev/null +++ b/app/src/renderer/api/login.js @@ -0,0 +1,30 @@ +import fetch from '@/utils/fetch'; + +export function login(email, password) { + return fetch({ + url: '/user/login', + method: 'post', + data: { + email, + password + } + }); +} + +export function getInfo(token) { + return fetch({ + url: '/user/info', + method: 'get', + params: { token } + }); +} + +export function logout() { + return fetch({ + url: '/user/logout', + method: 'post' + }); +} + + + diff --git a/app/src/renderer/api/table.js b/app/src/renderer/api/table.js new file mode 100644 index 0000000..b475c0a --- /dev/null +++ b/app/src/renderer/api/table.js @@ -0,0 +1,11 @@ +import fetch from '@/utils/fetch'; + +export function getList(params) { + return fetch({ + url: '/table/list', + method: 'get', + params + }); +} + + diff --git a/app/src/renderer/assets/404_images/404.png b/app/src/renderer/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/app/src/renderer/assets/404_images/404.png differ diff --git a/app/src/renderer/assets/404_images/404_cloud.png b/app/src/renderer/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/app/src/renderer/assets/404_images/404_cloud.png differ diff --git a/app/src/renderer/assets/iconfont/iconfont.js b/app/src/renderer/assets/iconfont/iconfont.js new file mode 100644 index 0000000..11a01bd --- /dev/null +++ b/app/src/renderer/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +(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/app/src/renderer/components/Hamburger/index.vue b/app/src/renderer/components/Hamburger/index.vue new file mode 100644 index 0000000..b0d1597 --- /dev/null +++ b/app/src/renderer/components/Hamburger/index.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/app/src/renderer/components/Icon-svg/index.vue b/app/src/renderer/components/Icon-svg/index.vue new file mode 100644 index 0000000..44881b1 --- /dev/null +++ b/app/src/renderer/components/Icon-svg/index.vue @@ -0,0 +1,22 @@ + + + diff --git a/app/src/renderer/main.js b/app/src/renderer/main.js new file mode 100644 index 0000000..5a636bd --- /dev/null +++ b/app/src/renderer/main.js @@ -0,0 +1,58 @@ +// 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 Electron from 'vue-electron' +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 NProgress from 'nprogress' +import 'normalize.css/normalize.css' +import '@/assets/iconfont/iconfont' +import IconSvg from '@/components/Icon-svg/index.vue' + +Vue.config.productionTip = false + +Vue.use(ElementUI) +Vue.use(Electron) +Vue.component('icon-svg', IconSvg) + +const whiteList = ['/login']; +router.beforeEach((to, from, next) => { + // NProgress.start(); + if (store.getters.token) { + if (to.path === '/login') { + next({ path: '/' }); + } else { + if (store.getters.roles.length === 0) { + store.dispatch('GetInfo').then(res => { + const roles = res.data.role; + store.dispatch('GenerateRoutes', { roles }).then(() => { + router.addRoutes(store.getters.addRouters); + next({ ...to }); + }) + }) + } else { + next(); + } + } + } else { + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + next('/login'); + // NProgress.done(); + } + } +}); + +router.afterEach(() => { + // NProgress.done(); +}); + +new Vue({ + router, + store, + render: h => h(App) +}).$mount('#app'); diff --git a/app/src/renderer/router/_import_development.js b/app/src/renderer/router/_import_development.js new file mode 100644 index 0000000..dfa4bb9 --- /dev/null +++ b/app/src/renderer/router/_import_development.js @@ -0,0 +1 @@ +module.exports = file => require('@/views/' + file + '.vue') diff --git a/app/src/renderer/router/_import_production.js b/app/src/renderer/router/_import_production.js new file mode 100644 index 0000000..331acba --- /dev/null +++ b/app/src/renderer/router/_import_production.js @@ -0,0 +1 @@ +module.exports = file => () => import('@/views/' + file + '.vue') diff --git a/app/src/renderer/router/index.js b/app/src/renderer/router/index.js new file mode 100644 index 0000000..ab3edd7 --- /dev/null +++ b/app/src/renderer/router/index.js @@ -0,0 +1,73 @@ +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 large page will cause webpack hot update too slow.so only in production use Lazy Loading + +/* layout */ +import Layout from '../views/layout/Layout'; + +/* login */ +const Login = _import('login/index'); + +/* dashboard */ +const dashboard = _import('dashboard/index'); + +/* error page */ +const Err404 = _import('404'); + +/* demo page */ +const Form = _import('page/form'); +const Table = _import('table/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 in the sidebar + * meta : `{ role: ['admin'] }` will control the page role + **/ +export const constantRouterMap = [ + { path: '/login', component: Login, hidden: true }, + { path: '/404', component: Err404, hidden: true }, + { + path: '/', + component: Layout, + redirect: '/dashboard', + name: 'Home', + hidden: true, + children: [{ path: 'dashboard', component: dashboard }] + } +] + +export default new Router({ + // mode: 'history', //后端支持可开 + scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap +}); + +export const asyncRouterMap = [ + { + path: '/example', + component: Layout, + redirect: 'noredirect', + name: 'Example', + icon: 'zujian', + children: [ + { path: 'index', component: Form, name: 'Form', icon: 'zonghe' } + ] + }, + + { + path: '/table', + component: Layout, + redirect: '/table/index', + name: 'Table', + icon: 'tubiaoleixingzhengchang', + noDropdown: true, + children: [{ path: 'index', component: Table, name: 'Table', meta: { role: ['admin'] } }] + }, + + { path: '*', redirect: '/404', hidden: true } +]; diff --git a/app/src/renderer/store/getters.js b/app/src/renderer/store/getters.js new file mode 100644 index 0000000..8553cd8 --- /dev/null +++ b/app/src/renderer/store/getters.js @@ -0,0 +1,10 @@ +const getters = { + sidebar: state => state.app.sidebar, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + roles: state => state.user.roles, + permission_routers: state => state.permission.routers, + addRouters: state => state.permission.addRouters +}; +export default getters diff --git a/app/src/renderer/store/index.js b/app/src/renderer/store/index.js new file mode 100644 index 0000000..ee7d313 --- /dev/null +++ b/app/src/renderer/store/index.js @@ -0,0 +1,19 @@ +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); + +const store = new Vuex.Store({ + modules: { + app, + user, + permission + }, + getters +}); + +export default store diff --git a/app/src/renderer/store/modules/app.js b/app/src/renderer/store/modules/app.js new file mode 100644 index 0000000..8874125 --- /dev/null +++ b/app/src/renderer/store/modules/app.js @@ -0,0 +1,26 @@ +// import Cookies from 'js-cookie'; + +const app = { + state: { + sidebar: { + opened: false + } + }, + mutations: { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.opened) { + // Cookies.set('sidebarStatus', 1); + } else { + // Cookies.set('sidebarStatus', 0); + } + state.sidebar.opened = !state.sidebar.opened; + } + }, + actions: { + ToggleSideBar: ({ commit }) => { + commit('TOGGLE_SIDEBAR') + } + } +}; + +export default app; diff --git a/app/src/renderer/store/modules/permission.js b/app/src/renderer/store/modules/permission.js new file mode 100644 index 0000000..ef2b347 --- /dev/null +++ b/app/src/renderer/store/modules/permission.js @@ -0,0 +1,62 @@ +import { asyncRouterMap, constantRouterMap } from '@/router/index'; + +/** + * 通过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) + } else { + return true + } +} + +/** + * 递归过滤异步路由表,返回符合用户角色权限的路由表 + * @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); + } + }, + actions: { + GenerateRoutes({ commit }, data) { + return new Promise(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; diff --git a/app/src/renderer/store/modules/user.js b/app/src/renderer/store/modules/user.js new file mode 100644 index 0000000..73abe65 --- /dev/null +++ b/app/src/renderer/store/modules/user.js @@ -0,0 +1,83 @@ +import { login, logout, getInfo } from '@/api/login'; + +const user = { + state: { + token: '', + name: '', + avatar: '', + roles: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token; + }, + SET_NAME: (state, name) => { + state.name = name; + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar; + }, + SET_ROLES: (state, roles) => { + state.roles = roles; + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const email = userInfo.email.trim(); + return new Promise((resolve, reject) => { + login(email, userInfo.password).then(response => { + const data = response.data; + window.localStorage.setItem('Admin-Token', data.token); + commit('SET_TOKEN', data.token); + resolve(); + }).catch(error => { + reject(error); + }); + }); + }, + + + // 获取用户信息 + GetInfo({ 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); + resolve(response); + }).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(); + }).catch(error => { + reject(error); + }); + }); + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', ''); + Cookies.remove('Admin-Token'); + resolve(); + }); + } + } +}; + +export default user; diff --git a/app/src/renderer/styles/element-ui.scss b/app/src/renderer/styles/element-ui.scss new file mode 100644 index 0000000..c618f7d --- /dev/null +++ b/app/src/renderer/styles/element-ui.scss @@ -0,0 +1,29 @@ + //覆盖一些element-ui样式 覆盖css样式可在这里添加 +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +//暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +//element ui upload +.upload-container { + .el-upload { + width: 100%; + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} diff --git a/app/src/renderer/styles/index.scss b/app/src/renderer/styles/index.scss new file mode 100644 index 0000000..5857740 --- /dev/null +++ b/app/src/renderer/styles/index.scss @@ -0,0 +1,71 @@ +@import './element-ui.scss'; +@import './mixin.scss'; +body { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +html { + box-sizing: border-box; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +//vue router transition css +.fade-enter-active, +.fade-leave-active { + transition: all .2s ease +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +//main-container全局样式 +.app-main{ + min-height: 100% +} + +.app-container { + padding: 20px; +} + +.svg-icon { + width: 1em; + height: 1em; + vertical-align: -0.15em; + fill: currentColor; + overflow: hidden; +} + diff --git a/app/src/renderer/styles/mixin.scss b/app/src/renderer/styles/mixin.scss new file mode 100644 index 0000000..601d7a0 --- /dev/null +++ b/app/src/renderer/styles/mixin.scss @@ -0,0 +1,27 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + &::-webkit-scrollbar { + width: 6px; + } + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + diff --git a/app/src/renderer/utils/fetch.js b/app/src/renderer/utils/fetch.js new file mode 100644 index 0000000..bfc57f0 --- /dev/null +++ b/app/src/renderer/utils/fetch.js @@ -0,0 +1,67 @@ +import axios from 'axios'; +import { Message } from 'element-ui'; +import store from '../store'; + +console.log(process.env) + +// 创建axios实例 +const service = axios.create({ + baseURL: process.env.BASE_API, // api的base_url + timeout: 5000 // 请求超时时间 +}); + +// request拦截器 +service.interceptors.request.use(config => { + if (store.getters.token) { + config.headers['X-Token'] = store.getters.token; // 让每个请求携带自定义token 请根据实际情况自行修改 + } + return config; +}, error => { + // Do something with request error + console.log(error); // for debug + Promise.reject(error); +}) + +// respone拦截器 +service.interceptors.response.use( + response => { + /** + * code为非20000是抛错 可结合自己业务进行修改 + */ + const res = response.data; + if (res.code !== 20000) { + Message({ + message: res.data, + 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 + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }); + return Promise.reject(error); + } +) + +export default service; diff --git a/app/src/renderer/utils/index.js b/app/src/renderer/utils/index.js new file mode 100644 index 0000000..f13c7f3 --- /dev/null +++ b/app/src/renderer/utils/index.js @@ -0,0 +1,58 @@ +/** + * Created by jiachenpan on 16/11/18. + */ + + export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null; + } + 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); + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + 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]; + if (result.length > 0 && value < 10) { + value = '0' + value; + } + return value || 0; + }); + return time_str; + } + + export function formatTime(time, option) { + time = +time * 1000; + const d = new Date(time); + const now = Date.now(); + + const diff = (now - d) / 1000; + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' + } + } diff --git a/app/src/renderer/utils/validate.js b/app/src/renderer/utils/validate.js new file mode 100644 index 0000000..6051c04 --- /dev/null +++ b/app/src/renderer/utils/validate.js @@ -0,0 +1,35 @@ +/** + * 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()); +} + +/* 合法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); +} + +/* 小写字母*/ +export function validateLowerCase(str) { + const reg = /^[a-z]+$/; + return reg.test(str); +} + +/* 大写字母*/ +export function validateUpperCase(str) { + const reg = /^[A-Z]+$/; + return reg.test(str); +} + +/* 大小写字母*/ +export function validatAlphabets(str) { + const reg = /^[A-Za-z]+$/; + return reg.test(str); +} + + diff --git a/app/src/renderer/views/404.vue b/app/src/renderer/views/404.vue new file mode 100644 index 0000000..a3e02b9 --- /dev/null +++ b/app/src/renderer/views/404.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/app/src/renderer/views/dashboard/index.vue b/app/src/renderer/views/dashboard/index.vue new file mode 100644 index 0000000..52cc61c --- /dev/null +++ b/app/src/renderer/views/dashboard/index.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/app/src/renderer/views/layout/AppMain.vue b/app/src/renderer/views/layout/AppMain.vue new file mode 100644 index 0000000..2dce938 --- /dev/null +++ b/app/src/renderer/views/layout/AppMain.vue @@ -0,0 +1,18 @@ + + + diff --git a/app/src/renderer/views/layout/Layout.vue b/app/src/renderer/views/layout/Layout.vue new file mode 100644 index 0000000..8d7a235 --- /dev/null +++ b/app/src/renderer/views/layout/Layout.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/app/src/renderer/views/layout/Levelbar.vue b/app/src/renderer/views/layout/Levelbar.vue new file mode 100644 index 0000000..c0c6145 --- /dev/null +++ b/app/src/renderer/views/layout/Levelbar.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/app/src/renderer/views/layout/Navbar.vue b/app/src/renderer/views/layout/Navbar.vue new file mode 100644 index 0000000..2fd82db --- /dev/null +++ b/app/src/renderer/views/layout/Navbar.vue @@ -0,0 +1,99 @@ + + + + + + + + diff --git a/app/src/renderer/views/layout/Sidebar.vue b/app/src/renderer/views/layout/Sidebar.vue new file mode 100644 index 0000000..57ee036 --- /dev/null +++ b/app/src/renderer/views/layout/Sidebar.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/renderer/views/layout/SidebarItem.vue b/app/src/renderer/views/layout/SidebarItem.vue new file mode 100644 index 0000000..e635f22 --- /dev/null +++ b/app/src/renderer/views/layout/SidebarItem.vue @@ -0,0 +1,46 @@ + + + + + + diff --git a/app/src/renderer/views/layout/index.js b/app/src/renderer/views/layout/index.js new file mode 100644 index 0000000..8eea53c --- /dev/null +++ b/app/src/renderer/views/layout/index.js @@ -0,0 +1,7 @@ +export { default as Navbar } from './Navbar'; + +export { default as Sidebar } from './Sidebar'; + +export { default as Levelbar } from './Levelbar'; + +export { default as AppMain } from './AppMain'; diff --git a/app/src/renderer/views/login/index.vue b/app/src/renderer/views/login/index.vue new file mode 100644 index 0000000..564d1e9 --- /dev/null +++ b/app/src/renderer/views/login/index.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/app/src/renderer/views/page/form.vue b/app/src/renderer/views/page/form.vue new file mode 100644 index 0000000..95dd746 --- /dev/null +++ b/app/src/renderer/views/page/form.vue @@ -0,0 +1,72 @@ + + + diff --git a/app/src/renderer/views/table/index.vue b/app/src/renderer/views/table/index.vue new file mode 100644 index 0000000..a53ade6 --- /dev/null +++ b/app/src/renderer/views/table/index.vue @@ -0,0 +1,58 @@ + + + diff --git a/config.js b/config.js new file mode 100644 index 0000000..e70fb73 --- /dev/null +++ b/config.js @@ -0,0 +1,12 @@ +'use strict' + +let config = { + // Use ESLint (extends `none`) + // Further changes can be made in `.eslintrc.js` + eslint: true, + + // webpack-dev-server port + port: 9080 +} + +module.exports = config diff --git a/config/dev.env.js b/config/dev.env.js new file mode 100644 index 0000000..bb359c4 --- /dev/null +++ b/config/dev.env.js @@ -0,0 +1,4 @@ +module.exports = { + NODE_ENV: '"development"', + BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"' +} diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..c10cd85 --- /dev/null +++ b/config/index.js @@ -0,0 +1,8 @@ +module.exports = { + build: { + env: require('./prod.env') + }, + dev: { + env: require('./dev.env') + } +} diff --git a/config/prod.env.js b/config/prod.env.js new file mode 100644 index 0000000..29c242d --- /dev/null +++ b/config/prod.env.js @@ -0,0 +1,4 @@ +module.exports = { + NODE_ENV: '"production"', + BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"' +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6bfa0b7 --- /dev/null +++ b/package.json @@ -0,0 +1,94 @@ +{ + "name": "electron-vue-admin", + "version": "0.0.0", + "description": "An electron-vue project", + "scripts": { + "build": "npm run pack && build", + "build:dir": "npm run pack && build --dir", + "dev": "node tasks/runner.js", + "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter app", + "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix app", + "pack": "npm run pack:main && npm run pack:renderer", + "pack:main": "cross-env NODE_ENV=production webpack -p --progress --colors --config webpack.main.config.js", + "pack:renderer": "cross-env NODE_ENV=production webpack -p --progress --colors --config webpack.renderer.config.js", + "postinstall": "npm run lint:fix && cd app && npm install" + }, + "build": { + "productName": "ElectronVue", + "appId": "org.simulatedgreg.electron-vue", + "category": "public.app-category.tools", + "dmg": { + "contents": [ + { + "x": 410, + "y": 150, + "type": "link", + "path": "/Applications" + }, + { + "x": 130, + "y": 150, + "type": "file" + } + ] + }, + "files": [ + "dist/", + "node_modules/", + "package.json" + ], + "mac": { + "icon": "app/icons/icon.icns" + }, + "win": { + "icon": "app/icons/icon.ico" + }, + "linux": { + "target": [ + "AppImage" + ] + } + }, + "author": "Greg Holguin ", + "license": "MIT", + "devDependencies": { + "babel-core": "^6.8.0", + "babel-loader": "^6.2.4", + "babel-plugin-transform-runtime": "^6.8.0", + "babel-preset-es2015": "^6.6.0", + "babel-preset-stage-0": "^6.5.0", + "babel-register": "^6.18.0", + "babel-runtime": "^6.6.1", + "cross-env": "^3.1.4", + "css-loader": "^0.26.1", + "del": "^2.2.1", + "devtron": "^1.1.0", + "electron": "^1.3.1", + "electron-debug": "^1.1.0", + "electron-devtools-installer": "^2.0.1", + "electron-builder": "^11.4.4", + "electron-rebuild": "^1.1.3", + "babel-eslint": "^7.0.0", + "eslint": "^3.13.1", + "eslint-friendly-formatter": "^2.0.5", + "eslint-loader": "^1.3.0", + "eslint-plugin-html": "^2.0.0", + "extract-text-webpack-plugin": "^2.0.0-beta.4", + "file-loader": "^0.9.0", + "html-webpack-plugin": "^2.16.1", + "json-loader": "^0.5.4", + "style-loader": "^0.13.1", + "tree-kill": "^1.1.0", + "url-loader": "^0.5.7", + "vue-hot-reload-api": "^2.0.7", + "vue-html-loader": "^1.2.2", + "vue-loader": "^10.0.2", + "vue-style-loader": "^1.0.0", + "vue-template-compiler": "^2.3.3", + "webpack": "^2.2.1", + "webpack-dev-server": "^2.3.0", + "node-sass": "4.5.2", + "sass-loader": "6.0.5" + }, + "dependencies": {} +} diff --git a/tasks/runner.js b/tasks/runner.js new file mode 100644 index 0000000..b16078a --- /dev/null +++ b/tasks/runner.js @@ -0,0 +1,56 @@ +'use strict' + +const config = require('../config') +const exec = require('child_process').exec +const treeKill = require('tree-kill') + +const YELLOW = '\x1b[33m' +const BLUE = '\x1b[34m' +const END = '\x1b[0m' + +let isElectronOpen = false + +function repeat(str, times) { + return (new Array(times + 1)).join(str) +} + +function format(command, data, color) { + return color + command + END + + ' ' + // Two space offset + data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2)) + + '\n' +} + +const children = [] + +function run(command, color, name) { + const child = exec(command) + + child.stdout.on('data', data => { + console.log(format(name, data, color)) + + /** + * Start electron after successful compilation + * (prevents electron from opening a blank window that requires refreshing) + */ + if (/Compiled/g.test(data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2))) && !isElectronOpen) { + console.log(`${BLUE}Starting electron...\n${END}`) + run('cross-env NODE_ENV=development electron app/src/main/index.dev.js', BLUE, 'electron') + isElectronOpen = true + } + }) + + child.stderr.on('data', data => console.error(format(name, data, color))) + child.on('exit', code => exit(code)) + + children.push(child) +} + +function exit(code) { + children.forEach(child => { + treeKill(child.pid) + }) +} + +console.log(`${YELLOW}Starting webpack-dev-server...\n${END}`) +run(`webpack-dev-server --hot --colors --config webpack.renderer.config.js --port ${config.port} --content-base app/dist`, YELLOW, 'webpack') diff --git a/webpack.main.config.js b/webpack.main.config.js new file mode 100644 index 0000000..210dc76 --- /dev/null +++ b/webpack.main.config.js @@ -0,0 +1,60 @@ +'use strict' + +process.env.BABEL_ENV = 'main' + +const path = require('path') +const pkg = require('./app/package.json') +const webpack = require('webpack') + +let mainConfig = { + entry: { + main: path.join(__dirname, 'app/src/main/index.js') + }, + externals: Object.keys(pkg.dependencies || {}), + module: { + rules: [ + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.json$/, + loader: 'json-loader' + }, + { + test: /\.node$/, + loader: 'node-loader' + } + ] + }, + node: { + __dirname: false, + __filename: false + }, + output: { + filename: '[name].js', + libraryTarget: 'commonjs2', + path: path.join(__dirname, 'app/dist') + }, + plugins: [ + new webpack.NoEmitOnErrorsPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false + } + }) + ], + resolve: { + extensions: ['.js', '.json', '.node'], + modules: [ + path.join(__dirname, 'app/node_modules') + ] + }, + target: 'electron-main' +} + +module.exports = mainConfig diff --git a/webpack.renderer.config.js b/webpack.renderer.config.js new file mode 100644 index 0000000..153ed48 --- /dev/null +++ b/webpack.renderer.config.js @@ -0,0 +1,159 @@ +'use strict' + +process.env.BABEL_ENV = 'renderer' + +const path = require('path') +const pkg = require('./app/package.json') +const settings = require('./config.js') +const webpack = require('webpack') +const config = require('./config/index.js') + +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') + +const rendererConfig = { + devtool: '#eval-source-map', + devServer: { overlay: true }, + entry: { + renderer: path.join(__dirname, 'app/src/renderer/main.js') + }, + externals: Object.keys(pkg.dependencies || {}), + module: { + rules: [ + { + test: /\.css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader' + }) + }, + { + test: /\.html$/, + use: 'vue-html-loader' + }, + { + test: /\.js$/, + use: 'babel-loader', + include: [path.resolve(__dirname, 'app/src/renderer')], + exclude: /node_modules/ + }, + { + test: /\.json$/, + use: 'json-loader' + }, + { + test: /\.node$/, + use: 'node-loader' + }, + { + test: /\.vue$/, + use: { + loader: 'vue-loader', + options: { + loaders: { + sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', + scss: 'vue-style-loader!css-loader!sass-loader' + } + } + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'imgs/[name].[ext]' + } + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'fonts/[name].[ext]' + } + } + } + ] + }, + plugins: [ + new ExtractTextPlugin('styles.css'), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: './app/index.ejs', + appModules: process.env.NODE_ENV !== 'production' + ? path.resolve(__dirname, 'app/node_modules') + : false + }), + new webpack.NoEmitOnErrorsPlugin() + ], + output: { + filename: '[name].js', + libraryTarget: 'commonjs2', + path: path.join(__dirname, 'app/dist') + }, + resolve: { + alias: { + components: path.join(__dirname, 'app/src/renderer/components'), + views: path.join(__dirname, 'app/src/renderer/views'), + renderer: path.join(__dirname, 'app/src/renderer'), + '@': path.join(__dirname, 'app/src/renderer') + }, + extensions: ['.js', '.vue', '.json', '.css', '.node'], + modules: [ + path.join(__dirname, 'app/node_modules'), + path.join(__dirname, 'node_modules') + ] + }, + target: 'electron-renderer' +} + +if (process.env.NODE_ENV !== 'production') { + if (settings.eslint) { + rendererConfig.module.rules.push( + { + test: /\.(js|vue)$/, + enforce: 'pre', + exclude: /node_modules/, + use: { + loader: 'eslint-loader', + options: { + formatter: require('eslint-friendly-formatter') + } + } + } + ) + } + console.log(config.dev) + rendererConfig.plugins.push( + new webpack.DefinePlugin({ + 'process.env': config.dev.env + }) + ) +} + +/** + * Adjust rendererConfig for production settings + */ +if (process.env.NODE_ENV === 'production') { + rendererConfig.devtool = '' + + rendererConfig.plugins.push( + new webpack.DefinePlugin({ + 'process.env': config.build.env + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }), + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false + } + }) + ) +} + +module.exports = rendererConfig