├── test └── testcafe │ ├── specs │ └── test.js │ └── index.js ├── .eslintignore ├── src ├── components │ ├── ui │ │ ├── index.js │ │ └── UiButton │ │ │ ├── index.vue │ │ │ └── style.scss │ ├── bootstrap │ │ ├── index.js │ │ └── rem.js │ └── NotFound.vue ├── assets │ ├── scss │ │ ├── _mixin.scss │ │ ├── _global.scss │ │ ├── _utilities.scss │ │ ├── mixins │ │ │ ├── _single-line.scss │ │ │ └── _clearfix.scss │ │ ├── _animation.scss │ │ ├── base.scss │ │ ├── _functions.scss │ │ ├── _layout.scss │ │ ├── _variables.scss │ │ ├── _reset.scss │ │ ├── components │ │ │ └── general.scss │ │ └── _normalize.scss │ ├── favicon.ico │ ├── imgs │ │ ├── logo.png │ │ └── logo-circle.png │ └── js │ │ └── fastclick.js └── pages │ ├── index │ ├── App.vue │ ├── app.scss │ ├── main.ejs │ ├── main.js │ └── views │ │ └── Index.vue │ └── components │ ├── main.js │ ├── views │ ├── Buttons.vue │ └── Index.vue │ ├── main.ejs │ ├── App.vue │ ├── app.scss │ └── router.js ├── docs ├── favicon.ico ├── assets │ ├── css │ │ ├── index.74e60a6c.css │ │ ├── components.177bd0cf.css │ │ └── vendor.48eb828b.css │ └── js │ │ ├── manifest.43a78824.js │ │ ├── index.310fa50f.js │ │ ├── 0.1197665f.js │ │ ├── components.2f79672d.js │ │ ├── vendor.6b4c4148.js │ │ └── libs.c1c9bc03.js ├── index.html └── components.html ├── .editorconfig ├── .babelrc ├── tools ├── config │ ├── fileExist.js │ ├── ip.js │ └── index.js ├── webpack │ ├── conf.docs.js │ ├── conf.dev.js │ ├── dev.server.js │ ├── loaders.js │ ├── conf.prod.js │ └── conf.base.js └── urls.js ├── .eslintrc.js ├── .gitignore ├── package.json └── README.md /test/testcafe/specs/test.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | tools/*.js 3 | -------------------------------------------------------------------------------- /src/components/ui/index.js: -------------------------------------------------------------------------------- 1 | export { default as UiButton } from './UiButton' 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/multi-vue/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /src/assets/scss/_mixin.scss: -------------------------------------------------------------------------------- 1 | @import "mixins/clearfix"; 2 | @import "mixins/single-line"; 3 | -------------------------------------------------------------------------------- /src/assets/scss/_global.scss: -------------------------------------------------------------------------------- 1 | @import "functions"; 2 | @import "variables"; 3 | @import "mixin"; 4 | -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/multi-vue/HEAD/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/multi-vue/HEAD/src/assets/imgs/logo.png -------------------------------------------------------------------------------- /src/assets/imgs/logo-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/multi-vue/HEAD/src/assets/imgs/logo-circle.png -------------------------------------------------------------------------------- /src/assets/scss/_utilities.scss: -------------------------------------------------------------------------------- 1 | .clearfix { 2 | @include clearfix(); 3 | } 4 | 5 | .single-line { 6 | @include single-line(); 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/_single-line.scss: -------------------------------------------------------------------------------- 1 | @mixin single-line () { 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | white-space: nowrap; 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/scss/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix () { 2 | &::after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/pages/index/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime", "transform-vue-jsx"], 7 | "comments": false, 8 | "env": { 9 | "production": { 10 | "presets": ["babili"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tools/config/fileExist.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | /** 3 | * @param {String} path to file/dir 4 | */ 5 | module.exports = function fileExist (path) { 6 | try { 7 | fs.accessSync(path, fs.constants.R_OK) 8 | return true 9 | } catch (err) { 10 | return false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/scss/_animation.scss: -------------------------------------------------------------------------------- 1 | @keyframes fadeIn { 2 | 0% { opacity: 0; } 3 | 100% { opacity: 1; } 4 | } 5 | 6 | @keyframes fadeOut { 7 | 0% { opacity: 1; } 8 | 100% { opacity: 0; } 9 | } 10 | 11 | @keyframes circle { 12 | 0% { transform: rotate3d(0, 0, 1, 0deg); } 13 | 100% { transform: rotate3d(0, 0, 1, 360deg); } 14 | } 15 | -------------------------------------------------------------------------------- /tools/config/ip.js: -------------------------------------------------------------------------------- 1 | const os = require('os') 2 | const interfaces = os.networkInterfaces() 3 | 4 | module.exports = Object.keys(interfaces) 5 | .reduce((arr, key) => arr.concat(interfaces[key]), []) 6 | .filter(item => item.family === 'IPv4' && item.internal === false) 7 | .reduce((local, item) => item && item.address || local, 'localhost') 8 | -------------------------------------------------------------------------------- /src/assets/scss/base.scss: -------------------------------------------------------------------------------- 1 | // Core variables and mixins 2 | @import "global"; 3 | 4 | // Reset and dependencies 5 | @import "normalize"; 6 | @import "reset"; 7 | 8 | // Core CSS 9 | @import "animation"; 10 | @import "layout"; 11 | 12 | // Components 13 | @import "components/general"; 14 | 15 | // Components via JavaScript 16 | 17 | // Utilities 18 | @import "utilities"; 19 | -------------------------------------------------------------------------------- /src/pages/components/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import './app.scss' 5 | import App from './App' 6 | import router from './router' 7 | 8 | Vue.use(VueRouter) 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | router, 13 | render: h => h(App) 14 | }) 15 | 16 | window.fetch('http://example.com/', { mode: 'no-cors' }) 17 | .then(_ => { console.log('GET http://example.com/') }) 18 | .catch(err => console.error(err)) 19 | -------------------------------------------------------------------------------- /tools/webpack/conf.docs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const merge = require('webpack-merge') 3 | 4 | /* Config */ 5 | const urls = require('../urls') 6 | const webpackConfProd = require('./conf.prod') 7 | 8 | const webpackConf = merge( 9 | webpackConfProd, 10 | { 11 | output: { 12 | path: urls.docs, 13 | publicPath: '' 14 | } 15 | } 16 | ) 17 | 18 | fs.writeFileSync(`${urls.temp}/config.docs.json`, JSON.stringify(webpackConf, null, 2), 'utf8') 19 | 20 | module.exports = webpackConf 21 | -------------------------------------------------------------------------------- /src/assets/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | // Add percentage of white to a color 2 | @function tint($color, $percent){ 3 | @return mix(white, $color, $percent); 4 | } 5 | 6 | // Add percentage of black to a color 7 | @function shade($color, $percent){ 8 | @return mix(black, $color, $percent); 9 | } 10 | 11 | // Add size function 12 | @function size($size) { 13 | @return 1rem * $size * 2 / 100; 14 | } 15 | 16 | // Add size function 17 | // 2x screen 18 | @function size2($size) { 19 | @return 1rem * $size / 100; 20 | } 21 | -------------------------------------------------------------------------------- /src/components/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | import FastClick from 'assets/js/fastclick' 2 | import 'assets/scss/base' 3 | import './rem' 4 | 5 | function bootstrap () { 6 | /* fastclick */ 7 | FastClick.attach(document.body) 8 | /* 9 | * ontouchstart bind , 10 | * 避免 iPhone 整屏后面的按钮 11 | * 点击无法触发 active 12 | */ 13 | document.ontouchstart = function () { } 14 | } 15 | 16 | if ('addEventListener' in document) { 17 | window.addEventListener('DOMContentLoaded', bootstrap) 18 | } else { 19 | window.onload = bootstrap 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/index/app.scss: -------------------------------------------------------------------------------- 1 | @import "~assets/scss/global"; 2 | 3 | html { 4 | height: 100%; 5 | } 6 | 7 | body { 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | height: 100%; 12 | } 13 | 14 | #app { 15 | color: #2c3e50; 16 | margin-top: -100px; 17 | max-width: size(300); 18 | font-family: Source Sans Pro, Helvetica, sans-serif; 19 | text-align: center; 20 | a { 21 | color: #42b983; 22 | text-decoration: none; 23 | } 24 | } 25 | 26 | .logo { 27 | width: 100px; 28 | height: 100px 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/components/views/Buttons.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /src/pages/index/main.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue with flow 6 | 7 | 8 | 9 | 10 | 11 | <%= htmlWebpackPlugin.files.webpackManifest %> 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/pages/components/main.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Components - Vue 6 | 7 | 8 | 9 | 10 | 11 | <%= htmlWebpackPlugin.files.webpackManifest %> 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/pages/components/views/Index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 26 | -------------------------------------------------------------------------------- /src/assets/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | body, html { 2 | height: 100%; 3 | background-color: #f5f5f9; 4 | } 5 | 6 | .container, 7 | .page { 8 | position: absolute; 9 | z-index: 1; 10 | top: 0; 11 | right: 0; 12 | bottom: 0; 13 | left: 0; 14 | } 15 | 16 | .page { 17 | overflow-x: hidden; 18 | overflow-y: auto; 19 | -webkit-overflow-scrolling: touch; 20 | } 21 | 22 | .page-bd { 23 | padding: 0 size(16) size(32); 24 | &.no-side-space { 25 | padding: 0 0 size(32); 26 | } 27 | &.no-bottom-space { 28 | padding: 0 size(16); 29 | } 30 | &.no-space { 31 | padding: 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/testcafe/index.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { Selector } from 'testcafe' 3 | 4 | const getElementById = Selector(id => document.querySelector(`#${id}`)) 5 | 6 | fixture `Getting Started` 7 | .page('https://devexpress.github.io/testcafe/example'); 8 | 9 | test('My first test', async (t) => { 10 | // Test code 11 | await t 12 | .typeText('#developer-name', 'John Smith') 13 | .click('#submit-button') 14 | 15 | const articleHeader = await getElementById('article-header') 16 | const headerText = articleHeader.innerText 17 | 18 | expect(headerText).to.equal('Thank you, John Smith!') 19 | }); 20 | -------------------------------------------------------------------------------- /src/pages/index/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import './app.scss' 5 | import App from './App' 6 | import Index from './views/Index' 7 | 8 | const { origin } = window.location 9 | const isGithub = origin.indexOf('github.io') > -1 10 | 11 | const router = new VueRouter({ 12 | mode: isGithub ? 'hash' : 'history', 13 | routes: [ 14 | { path: '/', component: Index }, 15 | { path: '*', component: () => System.import('components/NotFound') } 16 | ] 17 | }) 18 | 19 | Vue.use(VueRouter) 20 | /* eslint-disable no-new */ 21 | new Vue({ 22 | el: '#app', 23 | router, 24 | render: h => h(App) 25 | }) 26 | -------------------------------------------------------------------------------- /src/pages/components/App.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | curly: 'warn', 5 | parserOptions: { 6 | sourceType: 'module' 7 | }, 8 | env: { 9 | browser: true, 10 | es6: true, 11 | 'shared-node-browser': true 12 | }, 13 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 14 | extends: 'standard', 15 | // required to lint *.vue files 16 | plugins: [ 17 | 'html' 18 | ], 19 | // add your custom rules here 20 | 'rules': { 21 | // allow paren-less arrow functions 22 | 'arrow-parens': 0, 23 | // allow async-await 24 | 'generator-star-spacing': 0, 25 | // allow debugger during development 26 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/assets/css/index.74e60a6c.css: -------------------------------------------------------------------------------- 1 | body,html{height:100%}body{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}#app{color:#2c3e50;margin-top:-100px;max-width:6rem;font-family:Source Sans Pro,Helvetica,sans-serif;text-align:center}#app a{color:#42b983;text-decoration:none}.logo{width:100px;height:100px}html[data-v-06d015d0]{height:100%}body[data-v-06d015d0]{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center;height:100%}#app[data-v-06d015d0]{color:#2c3e50;margin-top:-100px;max-width:90%;font-size:.48rem;font-family:Source Sans Pro,Helvetica,sans-serif;text-align:center}#app a[data-v-06d015d0]{color:#42b983;text-decoration:none} -------------------------------------------------------------------------------- /docs/assets/css/components.177bd0cf.css: -------------------------------------------------------------------------------- 1 | .page{opacity:1}.slide-left-enter-active,.slide-left-leave-active{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1)}.slide-left-enter{opacity:0;-webkit-transform:translateX(61.8%);transform:translateX(61.8%)}.slide-left-leave-active{opacity:0;-webkit-transform:translateX(-61.8%);transform:translateX(-61.8%)}.slide-right-enter-active,.slide-right-leave-active{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1)}.slide-right-enter{opacity:0;-webkit-transform:translateX(-61.8%);transform:translateX(-61.8%)}.slide-right-leave-active{opacity:0;-webkit-transform:translateX(61.8%);transform:translateX(61.8%)}p{margin-bottom:.2rem}.page-hd{padding:.8rem}.pui-page-title{font-size:.48rem;font-weight:400;color:#404040;text-align:left}.pui-page-desc{margin-top:.06rem;color:#666;text-align:left;font-size:.28rem} -------------------------------------------------------------------------------- /src/pages/index/views/Index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | 20 | 47 | -------------------------------------------------------------------------------- /src/components/ui/UiButton/index.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 43 | -------------------------------------------------------------------------------- /src/assets/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | // Corlors 2 | 3 | $primary: #00a0e9; 4 | /*$primary: #108ee9;*/ 5 | $primary-click: shade($primary, 16%); 6 | $primary-hover: tint($primary, 20%); 7 | $primary-light: tint($primary, 80%); 8 | $primary-hover-bg: tint($primary, 90%); 9 | 10 | $success: #89CE6C; 11 | $success-click: shade($success, 16%); 12 | $success-hover: tint($success, 20%); 13 | $success-light: tint($success, 80%); 14 | $success-hover-bg: tint($success, 90%); 15 | 16 | $warning: #fa0; 17 | $warning-click: shade($warning, 16%); 18 | $warning-hover: tint($warning, 20%); 19 | $warning-light: tint($warning, 80%); 20 | $warning-hover-bg: tint($warning, 90%); 21 | 22 | $danger: #f50; 23 | $danger-click: shade($danger, 16%); 24 | $danger-hover: tint($danger, 20%); 25 | $danger-light: tint($danger, 80%); 26 | $danger-hover-bg: tint($danger, 90%); 27 | 28 | $gray-title: #404040; 29 | $gray: #666; 30 | $gray-help: #999; 31 | $gray-disabled: #ccc; 32 | $gray-border: #d9d9d9; 33 | $gray-hover: #ddd; 34 | $gray-split: #e9e9e9; 35 | $gray-disabled-bg: #f4f4f4; 36 | $gray-bg: #fbfbfb; 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional eslint cache 38 | .eslintcache 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | # Output of 'npm pack' 44 | *.tgz 45 | 46 | # temp & happypack 47 | .temp 48 | 49 | # OSX DS_Store 50 | .DS_Store 51 | 52 | # test 53 | test/unit/coverage 54 | test/e2e/reports 55 | 56 | # yarn 57 | yarn-error.log 58 | 59 | # custom 60 | build 61 | records.json 62 | -------------------------------------------------------------------------------- /tools/urls.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const cwd = process.cwd() 4 | 5 | // __dirname js 文件路径 6 | // process.cwd() node 运行的路径 7 | // path.resolve('node_modules') node 运行时路径 + node_modules 8 | const src = path.resolve('src') 9 | 10 | const urls = { 11 | /* base urls */ 12 | project: cwd, 13 | src: src, 14 | build: path.resolve('build'), 15 | docs: path.resolve('docs'), 16 | node_modules: path.resolve('node_modules'), 17 | webpack: path.resolve('tools/webpack'), 18 | recordsPath: path.resolve('tools/webpack/records.json'), 19 | temp: path.resolve('tools/.temp'), 20 | /* resource urls */ 21 | assets: path.resolve(src, 'assets'), 22 | components: path.resolve(src, 'components'), 23 | pages: path.resolve(src, 'pages'), 24 | favicon: path.resolve(src, 'assets/favicon.ico'), 25 | bootstrap: path.resolve(src, 'components/bootstrap'), 26 | helpers: path.resolve(src, 'handlebars/helpers'), 27 | templates: path.resolve(src, 'handlebars/templates') 28 | } 29 | 30 | fs.ensureDirSync(urls.temp) // Create .temp dir 31 | 32 | module.exports = urls 33 | -------------------------------------------------------------------------------- /src/components/NotFound.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | 20 | 49 | -------------------------------------------------------------------------------- /src/pages/components/app.scss: -------------------------------------------------------------------------------- 1 | @import "~assets/scss/global"; 2 | 3 | .page { 4 | opacity: 1; 5 | } 6 | /* 前进 */ 7 | .slide-left-enter-active, .slide-left-leave-active { 8 | transition: all .3s cubic-bezier(.645,.045,.355,1); 9 | } 10 | .slide-left-enter { 11 | opacity: 0; 12 | transform: translateX(61.8%); 13 | } 14 | .slide-left-leave-active { 15 | opacity: 0; 16 | transform: translateX(-61.8%); 17 | } 18 | 19 | /* 后退 */ 20 | .slide-right-enter-active, .slide-right-leave-active { 21 | transition: all .3s cubic-bezier(.645,.045,.355,1); 22 | } 23 | .slide-right-enter { 24 | opacity: 0; 25 | transform: translateX(-61.8%); 26 | } 27 | .slide-right-leave-active { 28 | opacity: 0; 29 | transform: translateX(61.8%); 30 | } 31 | 32 | p { 33 | margin-bottom: size(10); 34 | } 35 | 36 | .page-hd { 37 | padding: size(40); 38 | } 39 | 40 | .pui-page-title { 41 | font-size: size(24); 42 | font-weight: 400; 43 | color: $gray-title; 44 | text-align: left; 45 | } 46 | 47 | .pui-page-desc { 48 | margin-top: size(3); 49 | color: $gray; 50 | text-align: left; 51 | font-size: size(14); 52 | } 53 | -------------------------------------------------------------------------------- /tools/webpack/conf.dev.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const webpack = require('webpack') 3 | const merge = require('webpack-merge') 4 | 5 | /* Config */ 6 | const port = 8080 7 | const ip = require('../config/ip') 8 | const urls = require('../urls') 9 | const webpackConfBase = require('./conf.base') 10 | 11 | /* Webpack Plugins */ 12 | const DashboardPlugin = require('webpack-dashboard/plugin') 13 | const OpenBrowserPlugin = require('open-browser-webpack-plugin') 14 | 15 | const webpackConf = merge(webpackConfBase, { 16 | entry: { 17 | '[dev]': [ 18 | `webpack-dev-server/client?http://${ip}:${port}`, 19 | 'webpack/hot/dev-server' 20 | ] 21 | }, 22 | cache: true, 23 | devtool: '#source-map', 24 | plugins: [ 25 | new webpack.DefinePlugin({ 26 | 'process.env': { NODE_ENV: '"development"' } 27 | }), 28 | new webpack.HotModuleReplacementPlugin(), 29 | new webpack.NoEmitOnErrorsPlugin(), 30 | new webpack.NamedModulesPlugin(), 31 | new DashboardPlugin(), 32 | new OpenBrowserPlugin({ url: `http://localhost:${port}/` }) 33 | ] 34 | }) 35 | 36 | fs.writeFileSync(`${urls.temp}/config.dev.json`, JSON.stringify(webpackConf, null, 2), 'utf8') 37 | 38 | module.exports = webpackConf 39 | -------------------------------------------------------------------------------- /tools/webpack/dev.server.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const WebpackDevServer = require('webpack-dev-server') 3 | 4 | const host = '0.0.0.0' 5 | const port = 8080 6 | const ip = require('../config/ip') 7 | const webpackConfDev = require('./conf.dev') 8 | 9 | const compiler = webpack(webpackConfDev) 10 | const server = new WebpackDevServer(compiler, { 11 | hot: true, 12 | compress: true, 13 | historyApiFallback: { 14 | rewrites: [{ 15 | from: /^\/([^/]+|(?!assets))(\/.*|\/$|$)/, 16 | to (context) { 17 | return `/${context.match[1]}.html` 18 | } 19 | }] 20 | }, 21 | publicPath: '/', 22 | stats: { 23 | // Config for minimal console.log mess. 24 | assets: false, 25 | colors: true, 26 | version: true, 27 | hash: true, 28 | timings: true, 29 | chunks: true, 30 | chunkModules: false 31 | } 32 | }) 33 | 34 | server.listen(port, host, function (err) { 35 | if (err) return console.error(err) 36 | 37 | const filledIp = ip + new Array(15 - ip.length).join(' ') 38 | console.info(` 39 | ┌----------------------------------┐ 40 | ├ local IP address: ${filledIp} ┤ 41 | | | 42 | ├ Listening at ${host}:${port} ┤ 43 | └----------------------------------┘ 44 | `.trim('')) 45 | }) 46 | -------------------------------------------------------------------------------- /src/assets/scss/_reset.scss: -------------------------------------------------------------------------------- 1 | *, 2 | :after, 3 | :before { 4 | box-sizing: inherit; 5 | -webkit-tap-highlight-color: transparent; 6 | } 7 | 8 | html { 9 | font-size: 50px; 10 | line-height: 1.15; 11 | box-sizing: border-box; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-text-size-adjust: 100%; 16 | -ms-text-size-adjust: 100%; 17 | user-select: none; 18 | } 19 | 20 | body { 21 | line-height: 1.5; 22 | color: #666; 23 | font-size: .28rem; 24 | font-family: PingFang SC, Helvetica Neue, Hiragino Sans GB, Helvetica, Microsoft YaHei, Arial; 25 | // -webkit-overflow-scrolling: touch; 26 | } 27 | 28 | :focus, 29 | a { 30 | outline: none 31 | } 32 | 33 | a { 34 | background: transparent; 35 | text-decoration: none 36 | } 37 | 38 | button, 39 | input, 40 | select, 41 | textarea { 42 | appearance: none; 43 | } 44 | 45 | ul, 46 | ol { 47 | list-style-type: none; 48 | } 49 | 50 | article, 51 | aside, 52 | blockquote, 53 | body, 54 | button, 55 | code, 56 | dd, 57 | details, 58 | div, 59 | dl, 60 | dt, 61 | fieldset, 62 | figcaption, 63 | figure, 64 | footer, 65 | form, 66 | h1, 67 | h2, 68 | h3, 69 | h4, 70 | h5, 71 | h6, 72 | header, 73 | hgroup, 74 | input, 75 | legend, 76 | li, 77 | menu, 78 | nav, 79 | ol, 80 | p, 81 | pre, 82 | section, 83 | td, 84 | textarea, 85 | th, 86 | ul { 87 | margin: 0; 88 | padding: 0; 89 | } 90 | -------------------------------------------------------------------------------- /src/pages/components/router.js: -------------------------------------------------------------------------------- 1 | import VueRouter from 'vue-router' 2 | import Index from './views/Index' 3 | import Buttons from './views/Buttons' 4 | 5 | const { origin } = window.location 6 | const isGithub = origin.indexOf('github.io') > -1 7 | 8 | const router = new VueRouter({ 9 | mode: isGithub ? 'hash' : 'history', 10 | base: '/components', 11 | routes: [ 12 | { path: '/', component: Index }, 13 | { path: '/buttons', component: Buttons }, 14 | { path: '*', component: () => System.import('components/NotFound') } 15 | ], 16 | scrollBehavior (to, from, savedPosition) { 17 | if (savedPosition) { 18 | // savedPosition is only available for popstate navigations. 19 | return savedPosition 20 | } else { 21 | const position = {} 22 | // new navigation. 23 | // scroll to anchor by returning the selector 24 | if (to.hash) { 25 | position.selector = to.hash 26 | } 27 | // check if any matched route config has meta that requires scrolling to top 28 | if (to.matched.some(m => m.meta.scrollToTop)) { 29 | // cords will be used if no selector is provided, 30 | // or if the selector didn't match any element. 31 | position.x = 0 32 | position.y = 0 33 | } 34 | // if the returned position is falsy or an empty object, 35 | // will retain current scroll position. 36 | return position 37 | } 38 | } 39 | }) 40 | 41 | export default router 42 | -------------------------------------------------------------------------------- /src/components/bootstrap/rem.js: -------------------------------------------------------------------------------- 1 | (function (baseFontSize, fontscale) { 2 | var _baseFontSize = baseFontSize || 100 3 | var _fontscale = fontscale || 1 4 | var win = window 5 | var doc = win.document 6 | var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize' 7 | 8 | function reviewPort () { 9 | var ua = navigator.userAgent 10 | var matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i) 11 | var UCversion = ua.match(/U3\/((\d+|\.){5,})/i) 12 | var isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80 13 | var isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi) 14 | var dpr = win.devicePixelRatio || 1 15 | 16 | if (!isIos && !(matches && matches[1] > 534) && !isUCHd) { 17 | // 如果非iOS, 非Android4.3以上, 非UC内核, 就不执行高清, dpr设为1 18 | dpr = 1 19 | } 20 | var scale = 1 / dpr 21 | 22 | var metaEl = doc.querySelector('meta[name="viewport"]') 23 | if (!metaEl) { 24 | metaEl = doc.createElement('meta') 25 | metaEl.setAttribute('name', 'viewport') 26 | doc.head.appendChild(metaEl) 27 | } 28 | metaEl.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale) 29 | doc.documentElement.style.fontSize = _baseFontSize / 2 * dpr * _fontscale + 'px' 30 | window.viewportScale = dpr 31 | } 32 | 33 | reviewPort() 34 | win.addEventListener(resizeEvt, reviewPort, false) 35 | }()) 36 | -------------------------------------------------------------------------------- /docs/assets/js/manifest.43a78824.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,i){for(var u,a,s,f=0,l=[];f { 8 | let extraParamChar 9 | if (/\?/.test(loader)) { 10 | loader = loader.replace(/\?/, '-loader?') 11 | extraParamChar = '&' 12 | } else { 13 | loader = `${loader}-loader` 14 | extraParamChar = '?' 15 | } 16 | return loader + (options.sourceMap ? `${extraParamChar}sourceMap` : '') 17 | }).join('!') 18 | 19 | return options.extract 20 | ? ExtractTextPlugin.extract({ 21 | fallbackLoader: 'vue-style-loader', 22 | loader: sourceLoader 23 | }) 24 | : ['vue-style-loader', sourceLoader].join('!') 25 | } 26 | 27 | return { 28 | css: generateLoaders(), 29 | postcss: generateLoaders(), 30 | less: generateLoaders('less'), 31 | sass: generateLoaders('sass?indentedSyntax'), 32 | scss: generateLoaders('sass'), 33 | stylus: generateLoaders('stylus'), 34 | styl: generateLoaders('stylus') 35 | } 36 | } 37 | 38 | // Generate loaders for standalone style files (outside of .vue) 39 | exports.style = function styleLoaders (options) { 40 | const loaders = exports.css(Object.assign({ style: true }, options)) 41 | const output = [] 42 | 43 | for (let ext in loaders) { 44 | output.push({ 45 | test: new RegExp(`\\.${ext}$`), 46 | loaders: loaders[ext] 47 | }) 48 | } 49 | 50 | return output 51 | } 52 | -------------------------------------------------------------------------------- /docs/assets/js/index.310fa50f.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([3,5],{"/02Z":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default={name:"App"}},2:function(e,n,t){e.exports=t("MhDc")},Gvgx:function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default={name:"Index"}},MhDc:function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var r=t("qvlB"),o=t.n(r),i=t("lowN"),u=t.n(i),a=t("nq7v"),c=(t.n(a),t("qOuj")),s=t.n(c),l=t("vZuz"),v=t.n(l),f=window.location.origin,p=-1 2 | 3 | 4 | 5 | Vue with flow 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/components.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Components - Vue 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/assets/scss/components/general.scss: -------------------------------------------------------------------------------- 1 | a { 2 | color: $primary; 3 | text-decoration: none; 4 | &:active { 5 | color: $primary-click; 6 | } 7 | } 8 | 9 | .text-right { 10 | text-align: right; 11 | } 12 | 13 | .text-center { 14 | text-align: center; 15 | } 16 | 17 | .pull-left { 18 | float: left; 19 | } 20 | 21 | .pull-right { 22 | float: right; 23 | } 24 | 25 | .text-title { 26 | color: $gray-title; 27 | } 28 | 29 | .text-highlight { 30 | color: $primary; 31 | } 32 | 33 | .text-warning { 34 | color: $warning; 35 | } 36 | 37 | .text-danger { 38 | color: $danger; 39 | } 40 | 41 | .text-helper { 42 | color: $gray-help; 43 | } 44 | 45 | .ui-selectable { 46 | user-select: all; 47 | } 48 | 49 | /* animations */ 50 | .ui-hiding { 51 | animation: fadeOut .3s ease both; 52 | } 53 | 54 | .ui-icon-caret { 55 | display: flex; 56 | width: 0; 57 | height: 0; 58 | vertical-align: middle; 59 | border-top: 0.3em solid; 60 | border-right: 0.3em solid transparent; 61 | border-left: 0.3em solid transparent; 62 | } 63 | 64 | .ui-icon-help { 65 | display: flex; 66 | align-items: center; 67 | justify-content: space-around; 68 | width: size(20); 69 | height: size(20); 70 | vertical-align: text-top; 71 | color: #fff; 72 | background-color: $gray-border; 73 | border-radius: 100%; 74 | &::after { 75 | content: '?'; 76 | line-height: 1em; 77 | font-size: size(16); 78 | font-weight: bolder; 79 | } 80 | &:active { 81 | background-color: $gray-help; 82 | } 83 | } 84 | 85 | @font-face { 86 | font-family: 'iconfont'; /* project id:"177001" */ 87 | src: url('//at.alicdn.com/t/font_z4q9y7ysp7wopqfr.eot'); 88 | src: url('//at.alicdn.com/t/font_z4q9y7ysp7wopqfr.eot?#iefix') format('embedded-opentype'), 89 | url('//at.alicdn.com/t/font_z4q9y7ysp7wopqfr.woff') format('woff'), 90 | url('//at.alicdn.com/t/font_z4q9y7ysp7wopqfr.ttf') format('truetype'), 91 | url('//at.alicdn.com/t/font_z4q9y7ysp7wopqfr.svg#iconfont') format('svg'); 92 | } 93 | 94 | .ui-icon { 95 | font-family: "iconfont" !important; 96 | font-size: size(16); 97 | font-style: normal; 98 | color: $primary; 99 | background-image: linear-gradient(42deg, $primary, #34b6f1); 100 | text-shadow: 0 size(4) size(12) tint($primary, 42%); 101 | -webkit-background-clip: text; 102 | -webkit-text-fill-color: transparent; 103 | -webkit-font-smoothing: antialiased; 104 | -webkit-text-stroke-width: 0.2px; 105 | -moz-osx-font-smoothing: grayscale; 106 | &.no-shadow { 107 | text-shadow: none; 108 | } 109 | } 110 | 111 | .ui-iconfont { 112 | font-family: "iconfont" !important; 113 | font-style: normal; 114 | color: currentColor; 115 | -webkit-font-smoothing: antialiased; 116 | -webkit-text-stroke-width: 0.2px; 117 | -moz-osx-font-smoothing: grayscale; 118 | } 119 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-multipage", 3 | "version": "1.0.0", 4 | "description": "multi-single-application", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/blade254353074/multi-vue.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "start": "cross-env NODE_ENV=development webpack-dashboard -- node tools/webpack/dev.server.js", 12 | "dev": "cross-env NODE_ENV=development node tools/webpack/dev.server.js", 13 | "prebuild": "rimraf ./build/*", 14 | "build": "cross-env NODE_ENV=production webpack -p --progress --config ./tools/webpack/conf.prod.js", 15 | "predocs": "rimraf ./docs/*", 16 | "docs": "cross-env NODE_ENV=production webpack -p --progress --config ./tools/webpack/conf.docs.js", 17 | "test": "testcafe chrome ./test/testcafe/index.js" 18 | }, 19 | "keywords": [ 20 | "multi", 21 | "spa" 22 | ], 23 | "author": "blade254353074 ", 24 | "license": "MIT", 25 | "dependencies": { 26 | "es6-promise": "^4.0.5", 27 | "flow": "^0.2.3", 28 | "vue": "^2.1.10", 29 | "vue-router": "^2.2.0", 30 | "whatwg-fetch": "^2.0.2" 31 | }, 32 | "devDependencies": { 33 | "autoprefixer": "^6.7.2", 34 | "babel-core": "^6.22.1", 35 | "babel-eslint": "^7.1.0", 36 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 37 | "babel-loader": "^6.2.10", 38 | "babel-plugin-syntax-jsx": "^6.18.0", 39 | "babel-plugin-transform-runtime": "^6.22.0", 40 | "babel-plugin-transform-vue-jsx": "^3.1.2", 41 | "babel-preset-es2015": "^6.22.0", 42 | "babel-preset-stage-2": "^6.22.0", 43 | "babili": "^0.0.10", 44 | "chai": "^3.5.0", 45 | "compression-webpack-plugin": "^0.3.2", 46 | "cross-env": "^3.1.3", 47 | "css-loader": "^0.26.1", 48 | "ejs-loader": "^0.3.0", 49 | "eslint": "^3.14.1", 50 | "eslint-config-standard": "^6.2.1", 51 | "eslint-friendly-formatter": "^2.0.6", 52 | "eslint-loader": "^1.6.1", 53 | "eslint-plugin-html": "^2.0.0", 54 | "eslint-plugin-promise": "^3.3.1", 55 | "eslint-plugin-standard": "^2.0.1", 56 | "extract-text-webpack-plugin": "^2.0.0-rc.2", 57 | "file-loader": "^0.10.0", 58 | "fs-extra": "^2.0.0", 59 | "glob": "^7.1.1", 60 | "html-loader": "^0.4.4", 61 | "html-webpack-plugin": "^2.28.0", 62 | "imagemin-mozjpeg": "^6.0.0", 63 | "imagemin-webpack-plugin": "^1.2.1", 64 | "inline-manifest-webpack-plugin": "^3.0.1", 65 | "node-sass": "^4.5.0", 66 | "open-browser-webpack-plugin": "0.0.3", 67 | "postcss-loader": "^1.2.2", 68 | "rimraf": "^2.5.4", 69 | "sass-loader": "^4.1.0", 70 | "standard": "^8.5.0", 71 | "standard-loader": "^5.0.0", 72 | "testcafe": "^0.12.1", 73 | "url-loader": "^0.5.7", 74 | "vue-loader": "^10.2.0", 75 | "vue-style-loader": "^2.0.0", 76 | "vue-template-compiler": "^2.1.10", 77 | "webpack": "^2.2.1", 78 | "webpack-bundle-analyzer": "^2.1.1", 79 | "webpack-dashboard": "^0.3.0", 80 | "webpack-dev-server": "^1.16.3", 81 | "webpack-merge": "^2.6.1" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /docs/assets/js/components.2f79672d.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([2,5],{1:function(t,e,n){t.exports=n("joi4")},"8dD1":function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n("DDv3");e.default={name:"UiButtons",components:{UiButton:o.UiButton}}},Coa0:function(t,e,n){var o=n("VU/8")(n("e8/2"),n("jQsE"),null,null);t.exports=o.exports},HqBs:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={name:"UiIndex"}},MKfY:function(t,e){},MZVP:function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[t._m(0),t._v(" "),n("div",{staticClass:"page-bd"},[n("p",[n("UiButton",[t._v("default")])],1)])])},staticRenderFns:[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"page-hd"},[n("h1",{staticClass:"pui-page-title"},[t._v("Button")]),t._v(" "),n("p",{staticClass:"pui-page-desc"},[t._v("按钮")])])}]}},Mlw9:function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",[t._m(0),t._v(" "),n("div",{staticClass:"page-bd"},[n("p",[t._v("Hello World!")]),t._v(" "),n("ul",[n("li",[n("router-link",{attrs:{to:"buttons",append:""}},[t._v("Button 按钮")])],1)])])])},staticRenderFns:[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"page-hd"},[n("h1",{staticClass:"pui-page-title"},[t._v("Components")]),t._v(" "),n("p",{staticClass:"pui-page-desc"},[t._v("组件")])])}]}},Ps4R:function(t,e,n){n("zOdG");var o=n("VU/8")(n("HqBs"),n("Mlw9"),"data-v-1e4f2204",null);t.exports=o.exports},"e8/2":function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={name:"App",data:function(){return{transitionName:"slide-left"}},watch:{$route:function(t,e){var n=t.path.split("/").filter(function(t){return t.length}).length,o=e.path.split("/").filter(function(t){return t.length}).length;this.transitionName=n new HtmlWebpackPlugin(conf)) 126 | ] 127 | } 128 | -------------------------------------------------------------------------------- /tools/config/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const glob = require('glob') 3 | const urls = require('../urls') 4 | const fileExist = require('./fileExist') 5 | const NODE_ENV = process.env.NODE_ENV 6 | const templateExt = ['html', 'hbs', 'ejs'] 7 | 8 | function constructEntries (templateFiles) { 9 | const pagesAttr = [] 10 | templateFiles.map(template => { 11 | const dir = path.dirname(template) 12 | // key `dir/subpage1` or `poage1` 13 | const key = path.dirname(path.relative(urls.pages, template)) 14 | const templateKey = `[template]${encodeURIComponent(key)}` // 让 template 模板可追踪 15 | const jsPath = path.resolve(dir, 'main.js') 16 | const page = { 17 | key, 18 | templateKey, 19 | template 20 | } 21 | 22 | if (fileExist(jsPath)) { 23 | page.js = jsPath || './' + path.relative(urls.project, jsPath) 24 | } 25 | 26 | /* 27 | page { 28 | key: 'dir/subpage1', 29 | templateKey: '