├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── build ├── config.js ├── index.html ├── log-plugin.js ├── server.js ├── utils.js ├── webpack.base.js ├── webpack.dev.js └── webpack.prod.js ├── client ├── app.js ├── components │ ├── App │ │ ├── index.vue │ │ └── style.scss │ ├── Dimmer │ │ └── index.vue │ ├── Header │ │ ├── index.vue │ │ └── style.scss │ └── Sidebar │ │ ├── index.vue │ │ └── style.scss ├── index.js ├── pwa.js ├── router │ └── index.js ├── store │ ├── index.js │ └── ui │ │ └── index.js ├── styles │ ├── fonts.scss │ ├── index.scss │ ├── mixins.scss │ └── vars.scss └── views │ ├── About │ └── index.vue │ ├── Dashboard │ └── index.vue │ └── Post │ └── index.vue ├── package-lock.json ├── package.json ├── static └── favicon.ico └── tests └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["lodash", 3 | [ 4 | "transform-runtime", { 5 | "helpers": false, 6 | "polyfill": false, 7 | "regenerator": true, 8 | "moduleName": "babel-runtime" 9 | }, 10 | ] 11 | ], 12 | "presets": ["es2015", "vue-app", "stage-0"] 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["vue", "standard"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | /dist 5 | theme 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vladimir Metnev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue-Element-Starter 2 | #### Demo: https://metnew.github.io/vue-element-starter/ 3 | 4 | 5 | Sponsor 6 | 7 | 8 | [![bitHound Overall Score](https://www.bithound.io/github/Metnew/vue-element-starter/badges/score.svg)](https://www.bithound.io/github/Metnew/vue-element-starter) 9 | [![bitHound Dependencies](https://www.bithound.io/github/Metnew/vue-element-starter/badges/dependencies.svg)](https://www.bithound.io/github/Metnew/vue-element-starter/master/dependencies/npm) 10 | [![Known Vulnerabilities](https://snyk.io/test/github/metnew/vue-element-starter/badge.svg)](https://snyk.io/test/github/metnew/vue-element-starter) 11 | ## Includes: 12 | 13 | - [Element](http://element.eleme.io/#/en-US) and normalize.css 14 | - [Vue -v 2.1](https://vuejs.org/) & Vue-router 15 | - [Vuex](https://github.com/vuejs/vuex) & Vuex-router-sync 16 | - [Fetch polyfill](https://www.npmjs.com/package/whatwg-fetch) 17 | - [Webpack](https://webpack.github.io/) 18 | - [TestCafe](https://testcafe.devexpress.com/) for testing 19 | - Eslint, Babel (stage-0) 20 | 21 | ## Out-of-box: 22 | ### (theme color is purple, but you can use your own color, more info below) 23 | 24 | ![](https://github.com/Metnew/vue-element-starter/blob/gh-pages/screen.gif?raw=true) 25 | 26 | ## Folder structure: 27 | 28 | ``` 29 | app 30 | ├── build // webpack config files 31 | ├── client - Your App 32 | │ └── components - your components 33 | │ ├── router - routing 34 | │ ├── store - Vuex store 35 | │ ├── styles - styles folder with scss vars, mixins, etc. 36 | │ ├── views - your pages 37 | │ ├── app.js - import dependencies and App component 38 | │ ├── index.js - main file 39 | │ ├── pwa.js - for PWA apps 40 | ├── dist - build. 41 | ├── static - static assets, etc. 42 | ├── tests - Your tests 43 | ├── theme - Element UI generated theme 44 | ``` 45 | 46 | Template built based on [Vuepack bolirplate](https://github.com/egoist/vuepack) 47 | 48 | ## To start: 49 | 50 | ```bash 51 | git clone https://github.com/Metnew/vue-element-starter.git vue-project 52 | cd vue-project && rm -rf .git 53 | npm install 54 | npm run generate_default_styles # run this command to generate default_styles for Element-theme 55 | ``` 56 | 57 | [Element-theme](https://www.npmjs.com/package/element-theme) generates default styles. Just **change primary-color** in `./client/element-variables.css` and run: 58 | 59 | ```bash 60 | npm run generate_theme 61 | ``` 62 | 63 | Now app is ready and you can run it with: 64 | 65 | ```bash 66 | npm run dev 67 | ``` 68 | 69 | Make production build: 70 | 71 | ```bash 72 | npm run build 73 | ``` 74 | 75 | ### Also 76 | PRs, issues, questions, \ are always welcome. 77 | Feel free to contact me (or add new issue). 78 | 79 | ### Author 80 | Vladimir Metnew 81 | 82 | ### LICENSE 83 | MIT 84 | -------------------------------------------------------------------------------- /build/config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | port: 4000, 5 | title: 'Vue-Element-Starter', 6 | // when you use electron please set to relative path like ./ 7 | // otherwise only set to absolute path when you're using history mode 8 | publicPath: '/', 9 | // add these dependencies to a standalone vendor bundle 10 | vendor: [ 11 | 'vue', 12 | 'vuex', 13 | 'vue-router', 14 | 'vuex-router-sync', 15 | 'whatwg-fetch', 16 | 'normalize.css', 17 | 'offline-plugin/runtime', 18 | 'element-ui', 19 | 'material-design-icons' 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /build/log-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | 4 | // this plugin if for loggin url after each time the compilation is done. 5 | module.exports = class LogPlugin { 6 | constructor (port) { 7 | this.port = port 8 | } 9 | 10 | apply (compiler) { 11 | compiler.plugin('done', () => { 12 | console.log( 13 | `> App is running at ${chalk.yellow(`http://localhost:${this.port}`)}\n` 14 | ) 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // const fs = require('fs') 3 | const path = require('path') 4 | const express = require('express') 5 | const webpack = require('webpack') 6 | const webpackConfig = require('./webpack.dev') 7 | const config = require('./config') 8 | const LogPlugin = require('./log-plugin') 9 | 10 | const app = express() 11 | 12 | const port = config.port 13 | webpackConfig.entry.client = [ 14 | `webpack-hot-middleware/client?reload=true`, 15 | webpackConfig.entry.client 16 | ] 17 | 18 | webpackConfig.plugins.push(new LogPlugin(port)) 19 | 20 | let compiler 21 | 22 | try { 23 | compiler = webpack(webpackConfig) 24 | } catch (err) { 25 | console.log(err.message) 26 | process.exit(1) 27 | } 28 | 29 | const devMiddleWare = require('webpack-dev-middleware')(compiler, { 30 | publicPath: webpackConfig.output.publicPath, 31 | quiet: false, 32 | hot: true, 33 | inline: true, 34 | headers: { 35 | 'Access-Control-Allow-Origin': '*', 36 | 'Access-Control-Allow-Methods': '*', 37 | 'Access-Control-Allow-Headers': '*' 38 | } 39 | }) 40 | app.use(devMiddleWare) 41 | app.use( 42 | require('webpack-hot-middleware')(compiler, { 43 | log: () => {} 44 | }) 45 | ) 46 | 47 | const mfs = devMiddleWare.fileSystem 48 | const file = path.join(webpackConfig.output.path, 'index.html') 49 | 50 | devMiddleWare.waitUntilValid() 51 | 52 | app.get('*', (req, res) => { 53 | devMiddleWare.waitUntilValid(() => { 54 | const html = mfs.readFileSync(file) 55 | res.end(html) 56 | }) 57 | }) 58 | 59 | app.listen(port) 60 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const _ = (module.exports = {}) 4 | 5 | _.cwd = file => { 6 | return path.join(process.cwd(), file || '') 7 | } 8 | 9 | _.outputPath = path.join(__dirname, '../dist') 10 | 11 | _.outputIndexPath = path.join(__dirname, '../dist/index.html') 12 | 13 | _.target = 'web' 14 | -------------------------------------------------------------------------------- /build/webpack.base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const webpack = require('webpack') 4 | const HtmlWebpackPlugin = require('html-webpack-plugin') 5 | const CopyWebpackPlugin = require('copy-webpack-plugin') 6 | const config = require('./config') 7 | const _ = require('./utils') 8 | 9 | module.exports = { 10 | entry: { 11 | client: './client/index.js' 12 | }, 13 | output: { 14 | path: _.outputPath, 15 | filename: '[name].js', 16 | publicPath: config.publicPath 17 | }, 18 | performance: { 19 | hints: process.env.NODE_ENV === 'production' ? 'warning' : false 20 | }, 21 | resolve: { 22 | extensions: [ 23 | '.js', 24 | '.vue', 25 | '.css', 26 | '.json', 27 | '.scss', 28 | '.eot', 29 | '.svg', 30 | '.ttf', 31 | '.woff' 32 | ], 33 | alias: { 34 | root: path.join(__dirname, '../client'), 35 | components: path.join(__dirname, '../client/components'), 36 | views: path.join(__dirname, '../client/views'), 37 | router: path.join(__dirname, '../client/router'), 38 | theme: path.join(__dirname, '../theme'), // get Element-UI icons 39 | scss_vars: path.resolve(__dirname, '../client/styles/vars.scss'), // get scss vars 40 | styles: path.join(__dirname, '../client/styles') // get scss files 41 | }, 42 | modules: [ 43 | // places where to search for required modules 44 | _.cwd('node_modules'), 45 | _.cwd('client'), 46 | _.cwd('theme') 47 | ] 48 | }, 49 | module: { 50 | rules: [ 51 | { 52 | test: /\.vue$/, 53 | use: 'vue-loader' 54 | }, 55 | { 56 | test: /\.js$/, 57 | enforce: 'pre', 58 | use: 'eslint-loader?fix=true', 59 | exclude: [/node_modules/] 60 | }, 61 | { 62 | test: /\.js$/, 63 | use: 'babel-loader', 64 | exclude: [/node_modules/] 65 | }, 66 | { 67 | test: /\.(ico|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/, 68 | use: 'file-loader?limit=100000' 69 | }, 70 | { 71 | test: /\.(jpe?g|png|gif|svg)$/i, 72 | use: [ 73 | 'file-loader?limit=100000', 74 | { 75 | loader: 'img-loader', 76 | options: { 77 | enabled: true, 78 | optipng: true 79 | } 80 | } 81 | ] 82 | } 83 | ] 84 | }, 85 | plugins: [ 86 | new HtmlWebpackPlugin({ 87 | title: config.title, 88 | template: path.resolve(__dirname, 'index.html'), 89 | filename: _.outputIndexPath 90 | }), 91 | new CopyWebpackPlugin([ 92 | { 93 | from: _.cwd('./static'), 94 | // to the root of dist path 95 | to: './' 96 | } 97 | ]) 98 | ], 99 | target: _.target 100 | } 101 | -------------------------------------------------------------------------------- /build/webpack.dev.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | process.env.NODE_ENV = 'development' 3 | 4 | const webpack = require('webpack') 5 | const base = require('./webpack.base') 6 | const FriendlyErrors = require('friendly-errors-webpack-plugin') 7 | 8 | base.devtool = 'eval-source-map' 9 | base.module.rules.push( 10 | { 11 | test: /\.css$/, 12 | loaders: ['style-loader', 'css-loader', 'resolve-url-loader'] 13 | }, 14 | { 15 | test: /\.scss$/, 16 | loaders: ['style-loader', 'css-loader', 'resolve-url-loader', 'sass-loader'] 17 | } 18 | ) 19 | 20 | // add dev plugins 21 | base.plugins.push( 22 | new webpack.HotModuleReplacementPlugin(), 23 | new webpack.DefinePlugin({ 24 | 'process.env.NODE_ENV': JSON.stringify('development') 25 | }), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | new FriendlyErrors() 28 | ) 29 | 30 | module.exports = base 31 | -------------------------------------------------------------------------------- /build/webpack.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | process.env.NODE_ENV = 'production' 3 | 4 | const exec = require('child_process').execSync 5 | const webpack = require('webpack') 6 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 7 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') 8 | const PreloadWebpackPlugin = require('preload-webpack-plugin') 9 | const ProgressPlugin = require('webpack/lib/ProgressPlugin') 10 | const OfflinePlugin = require('offline-plugin') 11 | const base = require('./webpack.base') 12 | const config = require('./config') 13 | 14 | // remove dist folder 15 | exec('rm -rf dist/') 16 | // use source-map 17 | base.devtool = 'cheap-source-map' 18 | base.module.rules.push( 19 | { 20 | test: /\.css$/, 21 | use: ExtractTextPlugin.extract({ 22 | fallback: 'style-loader', 23 | use: 'css-loader' 24 | }) 25 | }, 26 | { 27 | test: /\.scss$/, 28 | use: ExtractTextPlugin.extract({ 29 | fallback: 'style-loader', 30 | use: ['css-loader', 'sass-loader'] 31 | }) 32 | } 33 | ) 34 | // a white list to add dependencies to vendor chunk 35 | base.entry.vendor = config.vendor 36 | // use hash filename to support long-term caching 37 | base.output.filename = '[name].[chunkhash:8].js' 38 | // add webpack plugins 39 | base.plugins.push( 40 | new ProgressPlugin(), 41 | new ExtractTextPlugin('[name].[contenthash:8].css'), 42 | new OptimizeCssAssetsPlugin(), 43 | new webpack.DefinePlugin({ 44 | 'process.env.NODE_ENV': JSON.stringify('production') 45 | }), 46 | new webpack.optimize.UglifyJsPlugin({ 47 | sourceMap: true, 48 | compress: { 49 | warnings: false 50 | }, 51 | output: { 52 | comments: false 53 | } 54 | }), 55 | // extract vendor chunks 56 | new webpack.optimize.CommonsChunkPlugin({ 57 | name: 'vendor', 58 | filename: 'vendor.[chunkhash:8].js' 59 | }), 60 | new PreloadWebpackPlugin({ rel: 'preload', as: 'script', include: 'all' }), 61 | // progressive web app 62 | // it uses the publicPath in webpack config 63 | new OfflinePlugin({ 64 | relativePaths: false, 65 | AppCache: false, 66 | ServiceWorker: { 67 | events: true 68 | } 69 | }) 70 | ) 71 | 72 | // minimize webpack output 73 | base.stats = { 74 | // Add children information 75 | children: false, 76 | // Add chunk information (setting this to `false` allows for a less verbose output) 77 | chunks: false, 78 | // Add built modules information to chunk information 79 | chunkModules: false, 80 | chunkOrigins: false, 81 | modules: false 82 | } 83 | 84 | module.exports = base 85 | -------------------------------------------------------------------------------- /client/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { sync } from 'vuex-router-sync' 3 | import Element from 'element-ui' 4 | import App from 'components/App' // require components using webpack alias 5 | import { router } from './router' // Vue Router 6 | import store from './store' // Vuex store 7 | import locale from 'element-ui/lib/locale/lang/en' 8 | import 'theme/index.css' // generated Element-UI theme 9 | import 'normalize.css' // normalize 10 | import 'whatwg-fetch' // polyfill 11 | import 'material-design-icons' // material icons, because Element-UI icons set is incomplete 12 | import 'styles/index.scss' // require styles using webpack alias 13 | 14 | sync(store, router) 15 | Vue.use(Element, { locale }) 16 | 17 | const app = new Vue({ 18 | router, 19 | store, 20 | ...App 21 | }) 22 | 23 | export { app, router, store } 24 | -------------------------------------------------------------------------------- /client/components/App/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 53 | 54 | 58 | -------------------------------------------------------------------------------- /client/components/App/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | * { 6 | box-sizing: border-box; 7 | } 8 | 9 | #app { 10 | color: #333; 11 | width: 100%; 12 | height: 100%; 13 | .page-layout { 14 | position: absolute; 15 | width: 100%; 16 | height: 100%; 17 | main { 18 | z-index: 1; 19 | overflow-x: auto; 20 | height: calc(100% - 80px); 21 | padding-top: 80px; 22 | position: absolute; 23 | transform-style: preserve-3d; 24 | will-change: transform; 25 | transition-property: transform; 26 | width: 100%; 27 | transform: translate3d(0, 0, 0)!important; 28 | @include respond-to('lg') { 29 | width: calc(100% - #{$sidebar-size}); // 30 | transform: translate3d($sidebar-size, 0, 0)!important; // 31 | } 32 | .main-content { 33 | width: 100%; 34 | margin: 0; 35 | @include respond-to('sm', 'xs') { 36 | margin: 0 auto; 37 | } 38 | .container { 39 | margin-top: 0; 40 | max-width: 1140px; 41 | padding: 0 20px; 42 | > div:first-child { 43 | width: 100%; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /client/components/Dimmer/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 38 | -------------------------------------------------------------------------------- /client/components/Header/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 26 | 27 | 32 | -------------------------------------------------------------------------------- /client/components/Header/style.scss: -------------------------------------------------------------------------------- 1 | header { 2 | z-index: 6; 3 | background: #fff; 4 | border-bottom: 1px solid $grey; 5 | box-shadow: inset 0 0 0 0 $grey,0 2px 1px 0 rgba(47,61,73,.05); 6 | color: #2e3d49; 7 | position: fixed; 8 | transform-style: preserve-3d; 9 | will-change: transform; 10 | transition-duration: .2s; 11 | transition-timing-function: cubic-bezier(.4,0,.2,1); 12 | transition-property: transform; 13 | width: 100%; 14 | height: $header-height; 15 | 16 | @include respond-to('sm', 'xs') { 17 | transform: translate3d(0, 0, 0)!important; 18 | } 19 | 20 | @include respond-to('lg') { 21 | width: calc(100% - #{$sidebar-size}); 22 | transform: translate3d($sidebar-size, 0, 0)!important; 23 | } 24 | 25 | .el-menu { 26 | border-radius: 0; 27 | } 28 | 29 | &.no_sidebar { 30 | transform: translate3d(0, 0, 0)!important; 31 | width: 100%; 32 | } 33 | 34 | .header-navicon { 35 | display: flex; 36 | align-items: center; 37 | align-content: center; 38 | @include respond-to('lg') { 39 | display: none; 40 | } 41 | i { 42 | color: $white!important; 43 | font-size: 2rem!important; 44 | } 45 | } 46 | .header-menu { 47 | float: right!important; 48 | } 49 | 50 | .header-title { 51 | font-size: 24px; 52 | color: $white; 53 | margin-left: 0; 54 | line-height: 60px; 55 | @include respond-to('lg') { 56 | margin-left: 24px; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /client/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 41 | 42 | 47 | -------------------------------------------------------------------------------- /client/components/Sidebar/style.scss: -------------------------------------------------------------------------------- 1 | #sidebar { 2 | z-index: 5; 3 | width: $sidebar-size; 4 | border-right: 1px solid $grey; 5 | box-shadow: inset 0 0 0 0 $grey,0 2px 1px 0 rgba(47,61,73,.05); 6 | height: 100%; 7 | visibility: visible!important; 8 | overflow-x: hidden; 9 | will-change: transform; 10 | transition-duration: .2s; 11 | transition-timing-function: cubic-bezier(.4,0,.2,1); 12 | transition-property: transform; 13 | position: fixed; 14 | &.open { 15 | z-index: 11111; 16 | transform: translate3d(0, 0, 0)!important; 17 | } 18 | @include respond-to('sm', 'xs') { 19 | z-index: 10000; 20 | transform: translate3d(-#{$sidebar-size}, 0, 0)!important; 21 | } 22 | @include respond-to('lg') { 23 | transform: translate3d(0, 0, 0)!important; 24 | } 25 | 26 | .el-menu { 27 | height: 100%; 28 | display: flex; 29 | flex-direction: column; 30 | } 31 | 32 | .logo-container { 33 | text-align: center; 34 | width: 100%; 35 | .logo { 36 | width: 215px; 37 | height: 215px; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import { app } from './app' 2 | 3 | // enable progressive web app support (with offline-plugin) 4 | if (process.env.NODE_ENV === 'production') { 5 | require('./pwa') 6 | } 7 | 8 | app.$mount('#app') 9 | 10 | if (module.hot) { 11 | module.hot.accept() 12 | } 13 | -------------------------------------------------------------------------------- /client/pwa.js: -------------------------------------------------------------------------------- 1 | import runtime from 'offline-plugin/runtime' 2 | 3 | runtime.install({ 4 | // When an update is ready, tell ServiceWorker to take control immediately: 5 | onUpdateReady () { 6 | console.log('update ready') 7 | runtime.applyUpdate() 8 | }, 9 | 10 | // Reload to get the new version: 11 | onUpdated () { 12 | console.log('updated') 13 | window.location.reload() 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /client/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Dashboard from 'views/Dashboard' 4 | import Post from 'views/Post' 5 | import About from 'views/About' 6 | 7 | Vue.use(Router) 8 | 9 | export const routes = [ 10 | { 11 | path: '/', 12 | component: Dashboard, 13 | meta: { 14 | title: 'Dashboard' 15 | } 16 | }, { 17 | path: '/post/:id', 18 | component: Post, 19 | meta: { 20 | title: 'Post' 21 | } 22 | }, { 23 | path: '/about', 24 | component: About, 25 | meta: { 26 | title: 'About' 27 | } 28 | } 29 | ] 30 | export const router = new Router({ mode: 'history', routes }) 31 | -------------------------------------------------------------------------------- /client/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import ui from './ui' 4 | 5 | Vue.use(Vuex) 6 | 7 | const store = new Vuex.Store({ 8 | modules: { 9 | ui 10 | } 11 | }) 12 | 13 | export default store 14 | -------------------------------------------------------------------------------- /client/store/ui/index.js: -------------------------------------------------------------------------------- 1 | export const OPEN_SIDEBAR = 'OPEN_SIDEBAR' 2 | export const CLOSE_SIDEBAR = 'CLOSE_SIDEBAR' 3 | export const LOCATION_CHANGE = 'router/ROUTE_CHANGED' 4 | export const WINDOW_RESIZE = 'WINDOW_RESIZE' 5 | 6 | const state = { 7 | sidebarOpened: false, 8 | obfuscatorActive: false, 9 | isMobile: false 10 | } 11 | 12 | const mutations = { 13 | [CLOSE_SIDEBAR] (state) { 14 | state.sidebarOpened = false 15 | state.obfuscatorActive = false 16 | }, 17 | [OPEN_SIDEBAR] (state) { 18 | state.sidebarOpened = true 19 | state.obfuscatorActive = true 20 | }, 21 | [LOCATION_CHANGE] (state) { 22 | state.sidebarOpened = false 23 | state.obfuscatorActive = false 24 | }, 25 | [WINDOW_RESIZE] (state) { 26 | const { innerWidth } = window 27 | const isMobile = innerWidth > 1024 28 | state.isMobile = isMobile 29 | state.sidebarOpened = isMobile 30 | } 31 | } 32 | 33 | const actions = { 34 | openSidebar ({ commit }) { 35 | commit({ type: OPEN_SIDEBAR }) 36 | }, 37 | closeSidebar ({ commit }) { 38 | commit({ type: CLOSE_SIDEBAR }) 39 | }, 40 | handleResize ({ commit }) { 41 | commit({ type: WINDOW_RESIZE }) 42 | } 43 | } 44 | 45 | export default { 46 | state, 47 | actions, 48 | mutations 49 | } 50 | -------------------------------------------------------------------------------- /client/styles/fonts.scss: -------------------------------------------------------------------------------- 1 | // Roboto font by default 2 | @import url('https://fonts.googleapis.com/css?family=Roboto'); 3 | 4 | body { 5 | font-family: 'Roboto', sans-serif; 6 | } 7 | 8 | // Element-UI icons from /theme folder 9 | @font-face { 10 | font-family: 'element-icons'; 11 | src: url('../../theme/fonts/element-icons.woff') format('woff'), /* chrome, firefox */ 12 | url('../../theme/fonts/element-icons.ttf') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 13 | font-weight: 400; 14 | font-style: normal; 15 | } 16 | 17 | // Add Material icons styles 18 | // cause Element-UI icon set is too small 19 | @font-face { 20 | font-family: 'Material Icons'; 21 | font-style: normal; 22 | font-weight: 400; 23 | src: local('Material Icons'), local('MaterialIcons-Regular'), url(https://fonts.gstatic.com/s/materialicons/v17/2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2) format('woff2'); 24 | } 25 | 26 | .material-icons, .md-icon, md-icon { 27 | font-family: 'Material Icons'; 28 | font-weight: normal; 29 | font-style: normal; 30 | font-size: 24px; 31 | line-height: 1; 32 | letter-spacing: normal; 33 | text-transform: none; 34 | display: inline-block; 35 | white-space: nowrap; 36 | word-wrap: normal; 37 | direction: ltr; 38 | -webkit-font-feature-settings: 'liga'; 39 | -webkit-font-smoothing: antialiased; 40 | } 41 | -------------------------------------------------------------------------------- /client/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './fonts.scss'; 2 | 3 | html, body { 4 | width: 100%; 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /client/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | // MIXIN FOR RESPONSIVE CSS GRID; 2 | // Extra small screen / phone 3 | $xs: 0; // Small screen / phone 4 | $sm: 480px; // Medium screen / tablet 5 | $md: 768px; // Large screen / desktop 6 | $lg: 1024px; // // Extra large screen / wide desktop 7 | // $xl: 1200px; 8 | @mixin respond-to($media...) { 9 | @for $i from 1 through length($media){ 10 | @include privat-respond-to(nth($media, $i)) { 11 | @content; 12 | }; 13 | } 14 | } 15 | @mixin privat-respond-to($current-media) { 16 | @if $current-media == 'xs' { 17 | @media only screen and (max-width: $sm) { 18 | @content; 19 | } 20 | } 21 | 22 | @else if $current-media == 'sm' { 23 | @media only screen and (min-width: $sm + 1) { 24 | @content; 25 | } 26 | } 27 | 28 | @else if $current-media == 'md' { 29 | @media only screen and (min-width: $md + 1) { 30 | @content; 31 | } 32 | } 33 | 34 | @else if $current-media == 'lg' { 35 | @media only screen and (min-width: $lg + 1) { 36 | @content; 37 | } 38 | } 39 | 40 | @else if $current-media == 'xl' { 41 | @media only screen and (min-width: $xl) { 42 | @content; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/styles/vars.scss: -------------------------------------------------------------------------------- 1 | @import './mixins.scss'; 2 | $sidebar-size: 260px; 3 | $grey: #dbe2e8; 4 | $white: #fff; 5 | $header-height: 60px; 6 | -------------------------------------------------------------------------------- /client/views/About/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /client/views/Dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /client/views/Post/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vue-Element-Starter", 3 | "version": "1.0.0", 4 | "description": "Vue-Element-Starter", 5 | "main": "client/index.js", 6 | "scripts": { 7 | "test": "testcafe all tests/index.js --app \"npm run dev\" --app-init-delay 10000", 8 | "build": "webpack --config build/webpack.prod.js", 9 | "dev": "node build/server.js", 10 | "lint": "eslint client/* --ext .js --ext .vue", 11 | "generate_default_styles": "et -i ./client/styles/element-variables.css", 12 | "generate_theme": "et -c ./client/styles/element-variables.css" 13 | }, 14 | "author": "Vladimir Metnev ", 15 | "license": "MIT", 16 | "dependencies": { 17 | "babel-runtime": "^6.23.0", 18 | "element-ui": "^1.2.9", 19 | "material-design-icons": "^3.0.1", 20 | "normalize.css": "^6.0.0", 21 | "vue": "^2.3.0", 22 | "vue-router": "^2.5.2", 23 | "vuex": "^2.3.1", 24 | "vuex-router-sync": "^4.1.2", 25 | "whatwg-fetch": "^2.0.3" 26 | }, 27 | "devDependencies": { 28 | "autoprefixer": "^6.7.7", 29 | "babel-cli": "^6.24.1", 30 | "babel-core": "^6.24.1", 31 | "babel-eslint": "^7.2.2", 32 | "babel-loader": "^7.0.0", 33 | "babel-plugin-lodash": "^3.2.11", 34 | "babel-plugin-transform-react-jsx": "^6.24.1", 35 | "babel-plugin-transform-regenerator": "^6.24.1", 36 | "babel-plugin-transform-runtime": "^6.23.0", 37 | "babel-polyfill": "^6.23.0", 38 | "babel-preset-es2015": "^6.24.1", 39 | "babel-preset-vue-app": "^1.2.0", 40 | "babel-preset-stage-0": "^6.24.1", 41 | "chalk": "^1.1.3", 42 | "copy-webpack-plugin": "^4.0.1", 43 | "css-loader": "^0.28.0", 44 | "element-theme": "^0.7.1", 45 | "element-theme-default": "^1.3.0-beta.1", 46 | "eslint": "^3.19.0", 47 | "eslint-config-standard": "^10.2.1", 48 | "eslint-config-vue": "^2.0.2", 49 | "eslint-loader": "^1.7.1", 50 | "eslint-plugin-babel": "^4.1.1", 51 | "eslint-plugin-import": "^2.2.0", 52 | "eslint-plugin-node": "^4.2.2", 53 | "eslint-plugin-promise": "^3.5.0", 54 | "eslint-plugin-standard": "^3.0.1", 55 | "eslint-plugin-testcafe": "^0.2.1", 56 | "eslint-plugin-vue": "^2.0.1", 57 | "express": "^4.15.2", 58 | "extract-text-webpack-plugin": "^2.1.0", 59 | "file-loader": "^0.11.1", 60 | "friendly-errors-webpack-plugin": "^1.6.1", 61 | "html-webpack-plugin": "^2.28.0", 62 | "img-loader": "^2.0.0", 63 | "node-sass": "^4.5.2", 64 | "offline-plugin": "^4.7.0", 65 | "optimize-css-assets-webpack-plugin": "^1.3.1", 66 | "preload-webpack-plugin": "^1.2.2", 67 | "resolve-url-loader": "^2.0.2", 68 | "sass-loader": "^6.0.3", 69 | "style-loader": "^0.16.1", 70 | "testcafe": "^0.15.0", 71 | "vue-loader": "^12.0.2", 72 | "vue-template-compiler": "^2.3.0", 73 | "webpack": "2.4.1", 74 | "webpack-dev-middleware": "^1.10.2", 75 | "webpack-hot-middleware": "^2.18.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Metnew/vue-element-starter/aa68364ebdf9f51f4a91c50cdc2feabfa59b0883/static/favicon.ico -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | // Your tests -> :) 2 | --------------------------------------------------------------------------------